Skip to content

Commit b09fd05

Browse files
committed
Merge branch 'master' into remove-workaraound-dpctl-2030
2 parents 2dfa892 + 434156d commit b09fd05

17 files changed

+459
-222
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
* Added implementation of `dpnp.hanning` [#2358](https://github.com/IntelPython/dpnp/pull/2358)
1313
* Added implementation of `dpnp.blackman` [#2363](https://github.com/IntelPython/dpnp/pull/2363)
1414
* Added implementation of `dpnp.bartlett` [#2366](https://github.com/IntelPython/dpnp/pull/2366)
15+
* Added implementation of `dpnp.convolve` [#2205](https://github.com/IntelPython/dpnp/pull/2205)
1516
* Added implementation of `dpnp.kaiser` [#2387](https://github.com/IntelPython/dpnp/pull/2387)
1617

1718
### Changed

dpnp/dpnp_iface_manipulation.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,9 @@ def atleast_1d(*arys):
958958
dpnp.check_supported_arrays_type(*arys)
959959
for ary in arys:
960960
if ary.ndim == 0:
961+
# 0-d arrays cannot be empty
962+
# 0-d arrays always have a size of 1, so
963+
# reshape(1) is guaranteed to succeed
961964
result = ary.reshape(1)
962965
else:
963966
result = ary

dpnp/dpnp_iface_mathematical.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@
9191
"clip",
9292
"conj",
9393
"conjugate",
94-
"convolve",
9594
"copysign",
9695
"cross",
9796
"cumprod",
@@ -792,24 +791,6 @@ def clip(a, /, min=None, max=None, *, out=None, order="K", **kwargs):
792791

793792
conj = conjugate
794793

795-
796-
def convolve(a, v, mode="full"):
797-
"""
798-
Returns the discrete, linear convolution of two one-dimensional sequences.
799-
800-
For full documentation refer to :obj:`numpy.convolve`.
801-
802-
Examples
803-
--------
804-
>>> ca = dpnp.convolve([1, 2, 3], [0, 1, 0.5])
805-
>>> print(ca)
806-
[0. , 1. , 2.5, 4. , 1.5]
807-
808-
"""
809-
810-
return call_origin(numpy.convolve, a=a, v=v, mode=mode)
811-
812-
813794
_COPYSIGN_DOCSTRING = """
814795
Composes a floating-point value with the magnitude of `x1_i` and the sign of
815796
`x2_i` for each element of input arrays `x1` and `x2`.

dpnp/dpnp_iface_statistics.py

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"amax",
6363
"amin",
6464
"average",
65+
"convolve",
6566
"corrcoef",
6667
"correlate",
6768
"cov",
@@ -357,6 +358,146 @@ def average(a, axis=None, weights=None, returned=False, *, keepdims=False):
357358
return avg
358359

359360

361+
def _convolve_impl(a, v, mode, method, rdtype):
362+
l_pad, r_pad = _get_padding(a.size, v.size, mode)
363+
364+
if method == "auto":
365+
method = _choose_conv_method(a, v, rdtype)
366+
367+
if method == "direct":
368+
r = _run_native_sliding_dot_product1d(a, v[::-1], l_pad, r_pad, rdtype)
369+
elif method == "fft":
370+
r = _convolve_fft(a, v, l_pad, r_pad, rdtype)
371+
else:
372+
raise ValueError(
373+
f"Unknown method: {method}. Supported methods: auto, direct, fft"
374+
)
375+
376+
return r
377+
378+
379+
def convolve(a, v, mode="full", method="auto"):
380+
r"""
381+
Returns the discrete, linear convolution of two one-dimensional sequences.
382+
The convolution operator is often seen in signal processing, where it
383+
models the effect of a linear time-invariant system on a signal [1]_. In
384+
probability theory, the sum of two independent random variables is
385+
distributed according to the convolution of their individual
386+
distributions.
387+
388+
If `v` is longer than `a`, the arrays are swapped before computation.
389+
390+
For full documentation refer to :obj:`numpy.convolve`.
391+
392+
Parameters
393+
----------
394+
a : {dpnp.ndarray, usm_ndarray}
395+
First input array.
396+
v : {dpnp.ndarray, usm_ndarray}
397+
Second input array.
398+
mode : {'full', 'valid', 'same'}, optional
399+
- 'full': This returns the convolution
400+
at each point of overlap, with an output shape of (N+M-1,). At
401+
the end-points of the convolution, the signals do not overlap
402+
completely, and boundary effects may be seen.
403+
- 'same': Mode 'same' returns output of length ``max(M, N)``. Boundary
404+
effects are still visible.
405+
- 'valid': Mode 'valid' returns output of length
406+
``max(M, N) - min(M, N) + 1``. The convolution product is only given
407+
for points where the signals overlap completely. Values outside
408+
the signal boundary have no effect.
409+
410+
Default: ``'full'``.
411+
method : {'auto', 'direct', 'fft'}, optional
412+
- 'direct': The convolution is determined directly from sums.
413+
- 'fft': The Fourier Transform is used to perform the calculations.
414+
This method is faster for long sequences but can have accuracy issues.
415+
- 'auto': Automatically chooses direct or Fourier method based on
416+
an estimate of which is faster.
417+
418+
Note: Use of the FFT convolution on input containing NAN or INF
419+
will lead to the entire output being NAN or INF.
420+
Use ``method='direct'`` when your input contains NAN or INF values.
421+
422+
Default: ``'auto'``.
423+
424+
Returns
425+
-------
426+
out : dpnp.ndarray
427+
Discrete, linear convolution of `a` and `v`.
428+
429+
See Also
430+
--------
431+
:obj:`dpnp.correlate` : Cross-correlation of two 1-dimensional sequences.
432+
433+
Notes
434+
-----
435+
The discrete convolution operation is defined as
436+
437+
.. math:: (a * v)_n = \sum_{m = -\infty}^{\infty} a_m v_{n - m}
438+
439+
It can be shown that a convolution :math:`x(t) * y(t)` in time/space
440+
is equivalent to the multiplication :math:`X(f) Y(f)` in the Fourier
441+
domain, after appropriate padding (padding is necessary to prevent
442+
circular convolution). Since multiplication is more efficient (faster)
443+
than convolution, the function implements two approaches - direct and fft
444+
which are regulated by the keyword `method`.
445+
446+
References
447+
----------
448+
.. [1] Uncyclopedia, "Convolution",
449+
https://en.wikipedia.org/wiki/Convolution
450+
451+
Examples
452+
--------
453+
Note how the convolution operator flips the second array
454+
before "sliding" the two across one another:
455+
456+
>>> import dpnp as np
457+
>>> a = np.array([1, 2, 3], dtype=np.float32)
458+
>>> v = np.array([0, 1, 0.5], dtype=np.float32)
459+
>>> np.convolve(a, v)
460+
array([0. , 1. , 2.5, 4. , 1.5], dtype=float32)
461+
462+
Only return the middle values of the convolution.
463+
Contains boundary effects, where zeros are taken
464+
into account:
465+
466+
>>> np.convolve(a, v, 'same')
467+
array([1. , 2.5, 4. ], dtype=float32)
468+
469+
The two arrays are of the same length, so there
470+
is only one position where they completely overlap:
471+
472+
>>> np.convolve(a, v, 'valid')
473+
array([2.5], dtype=float32)
474+
475+
"""
476+
477+
a, v = dpnp.atleast_1d(a, v)
478+
479+
if a.size == 0 or v.size == 0:
480+
raise ValueError(
481+
f"Array arguments cannot be empty. "
482+
f"Received sizes: a.size={a.size}, v.size={v.size}"
483+
)
484+
if a.ndim > 1 or v.ndim > 1:
485+
raise ValueError(
486+
f"Only 1-dimensional arrays are supported. "
487+
f"Received shapes: a.shape={a.shape}, v.shape={v.shape}"
488+
)
489+
490+
device = a.sycl_device
491+
rdtype = result_type_for_device([a.dtype, v.dtype], device)
492+
493+
if v.size > a.size:
494+
a, v = v, a
495+
496+
r = _convolve_impl(a, v, mode, method, rdtype)
497+
498+
return dpnp.asarray(r, dtype=rdtype, order="C")
499+
500+
360501
def corrcoef(x, y=None, rowvar=True, *, dtype=None):
361502
"""
362503
Return Pearson product-moment correlation coefficients.
@@ -714,17 +855,7 @@ def correlate(a, v, mode="valid", method="auto"):
714855
revert = True
715856
a, v = v, a
716857

717-
l_pad, r_pad = _get_padding(a.size, v.size, mode)
718-
719-
if method == "auto":
720-
method = _choose_conv_method(a, v, rdtype)
721-
722-
if method == "direct":
723-
r = _run_native_sliding_dot_product1d(a, v, l_pad, r_pad, rdtype)
724-
elif method == "fft":
725-
r = _convolve_fft(a, v[::-1], l_pad, r_pad, rdtype)
726-
else: # pragma: no cover
727-
raise ValueError(f"Unknown method: {method}")
858+
r = _convolve_impl(a, v[::-1], mode, method, rdtype)
728859

729860
if revert:
730861
r = r[::-1]

dpnp/tests/helper.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,19 @@ def generate_random_numpy_array(
185185
return a
186186

187187

188+
def factor_to_tol(dtype, factor):
189+
"""
190+
Calculate the tolerance for comparing floating point and complex arrays.
191+
The tolerance is based on the maximum resolution of the input dtype multiplied by the factor.
192+
"""
193+
194+
tol = 0
195+
if numpy.issubdtype(dtype, numpy.inexact):
196+
tol = numpy.finfo(dtype).resolution
197+
198+
return factor * tol
199+
200+
188201
def get_abs_array(data, dtype=None):
189202
if numpy.issubdtype(dtype, numpy.unsignedinteger):
190203
data = numpy.abs(data)
@@ -312,6 +325,36 @@ def get_integer_dtypes(all_int_types=False, no_unsigned=False):
312325
return dtypes
313326

314327

328+
def get_integer_float_dtypes(
329+
all_int_types=False,
330+
no_unsigned=False,
331+
no_float16=True,
332+
device=None,
333+
xfail_dtypes=None,
334+
exclude=None,
335+
):
336+
"""
337+
Build a list of integer and float types supported by DPNP.
338+
"""
339+
dtypes = get_integer_dtypes(
340+
all_int_types=all_int_types, no_unsigned=no_unsigned
341+
)
342+
dtypes += get_float_dtypes(no_float16=no_float16, device=device)
343+
344+
def mark_xfail(dtype):
345+
if xfail_dtypes is not None and dtype in xfail_dtypes:
346+
return pytest.param(dtype, marks=pytest.mark.xfail)
347+
return dtype
348+
349+
def not_excluded(dtype):
350+
if exclude is None:
351+
return True
352+
return dtype not in exclude
353+
354+
dtypes = [mark_xfail(dtype) for dtype in dtypes if not_excluded(dtype)]
355+
return dtypes
356+
357+
315358
def has_support_aspect16(device=None):
316359
"""
317360
Return True if the device supports 16-bit precision floating point operations,

dpnp/tests/test_binary_ufuncs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
get_float_complex_dtypes,
2121
get_float_dtypes,
2222
get_integer_dtypes,
23+
get_integer_float_dtypes,
2324
has_support_aspect16,
2425
numpy_version,
2526
)
@@ -141,7 +142,7 @@ def test_invalid_out(self, xp, out):
141142
@pytest.mark.parametrize("func", ["fmax", "fmin", "maximum", "minimum"])
142143
class TestBoundFuncs:
143144
@pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True))
144-
def test_out(self, func, dtype):
145+
def test_basic(self, func, dtype):
145146
a = generate_random_numpy_array(10, dtype)
146147
b = generate_random_numpy_array(10, dtype)
147148
expected = getattr(numpy, func)(a, b)
@@ -278,7 +279,7 @@ def test_invalid_out(self, xp, out):
278279

279280
@pytest.mark.parametrize("func", ["floor_divide", "remainder"])
280281
class TestFloorDivideRemainder:
281-
ALL_DTYPES = get_all_dtypes(no_none=True, no_bool=True, no_complex=True)
282+
ALL_DTYPES = get_integer_float_dtypes()
282283

283284
def do_inplace_op(self, base, other, func):
284285
if func == "floor_divide":

dpnp/tests/test_histogram.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
get_float_complex_dtypes,
2020
get_float_dtypes,
2121
get_integer_dtypes,
22+
get_integer_float_dtypes,
2223
has_support_aspect64,
2324
numpy_version,
2425
)
2526

2627

2728
class TestDigitize:
28-
@pytest.mark.parametrize(
29-
"dtype", get_all_dtypes(no_bool=True, no_complex=True)
30-
)
29+
@pytest.mark.parametrize("dtype", get_integer_float_dtypes())
3130
@pytest.mark.parametrize("right", [True, False])
3231
@pytest.mark.parametrize(
3332
"x, bins",
@@ -73,12 +72,8 @@ def test_digitize_inf(self, dtype, right):
7372
expected = numpy.digitize(x, bins, right=right)
7473
assert_dtype_allclose(result, expected)
7574

76-
@pytest.mark.parametrize(
77-
"dtype_x", get_all_dtypes(no_bool=True, no_complex=True)
78-
)
79-
@pytest.mark.parametrize(
80-
"dtype_bins", get_all_dtypes(no_bool=True, no_complex=True)
81-
)
75+
@pytest.mark.parametrize("dtype_x", get_integer_float_dtypes())
76+
@pytest.mark.parametrize("dtype_bins", get_integer_float_dtypes())
8277
@pytest.mark.parametrize("right", [True, False])
8378
def test_digitize_diff_types(self, dtype_x, dtype_bins, right):
8479
x = numpy.array([1, 2, 3, 4, 5], dtype=dtype_x)
@@ -90,9 +85,7 @@ def test_digitize_diff_types(self, dtype_x, dtype_bins, right):
9085
expected = numpy.digitize(x, bins, right=right)
9186
assert_dtype_allclose(result, expected)
9287

93-
@pytest.mark.parametrize(
94-
"dtype", get_all_dtypes(no_bool=True, no_complex=True)
95-
)
88+
@pytest.mark.parametrize("dtype", get_integer_float_dtypes())
9689
@pytest.mark.parametrize(
9790
"x, bins",
9891
[

dpnp/tests/test_linalg.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
get_all_dtypes,
2323
get_complex_dtypes,
2424
get_float_complex_dtypes,
25+
get_integer_float_dtypes,
2526
has_support_aspect64,
2627
is_cpu_device,
2728
is_cuda_device,
@@ -1409,9 +1410,7 @@ def test_einsum_tensor(self):
14091410
result = dpnp.einsum("ijij->", tensor_dp)
14101411
assert_dtype_allclose(result, expected)
14111412

1412-
@pytest.mark.parametrize(
1413-
"dtype", get_all_dtypes(no_bool=True, no_complex=True, no_none=True)
1414-
)
1413+
@pytest.mark.parametrize("dtype", get_integer_float_dtypes())
14151414
def test_different_paths(self, dtype):
14161415
# Simple test, designed to exercise most specialized code paths,
14171416
# note the +0.5 for floats. This makes sure we use a float value

0 commit comments

Comments
 (0)