Skip to content

Commit 61e350f

Browse files
Merge 74ab8f3 into 04bfac7
2 parents 04bfac7 + 74ab8f3 commit 61e350f

File tree

7 files changed

+342
-65
lines changed

7 files changed

+342
-65
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

1617
### Changed
1718

dpnp/dpnp_iface_mathematical.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@
9090
"clip",
9191
"conj",
9292
"conjugate",
93-
"convolve",
9493
"copysign",
9594
"cross",
9695
"cumprod",
@@ -791,24 +790,6 @@ def clip(a, /, min=None, max=None, *, out=None, order="K", **kwargs):
791790

792791
conj = conjugate
793792

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

dpnp/dpnp_iface_statistics.py

Lines changed: 146 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,150 @@ 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 1-D array.
396+
v : {dpnp.ndarray, usm_ndarray}
397+
Second 1-D array. The length of `v` must be less than or equal to
398+
the length of `a`.
399+
mode : {'full', 'valid', 'same'}, optional
400+
- 'full': This returns the convolution
401+
at each point of overlap, with an output shape of (N+M-1,). At
402+
the end-points of the convolution, the signals do not overlap
403+
completely, and boundary effects may be seen.
404+
- 'same': Mode 'same' returns output of length ``max(M, N)``. Boundary
405+
effects are still visible.
406+
- 'valid': Mode 'valid' returns output of length
407+
``max(M, N) - min(M, N) + 1``. The convolution product is only given
408+
for points where the signals overlap completely. Values outside
409+
the signal boundary have no effect.
410+
411+
Default: ``'full'``.
412+
method : {'auto', 'direct', 'fft'}, optional
413+
- 'direct': The convolution is determined directly from sums.
414+
- 'fft': The Fourier Transform is used to perform the calculations.
415+
This method is faster for long sequences but can have accuracy issues.
416+
- 'auto': Automatically chooses direct or Fourier method based on
417+
an estimate of which is faster.
418+
419+
Note: Use of the FFT convolution on input containing NAN or INF
420+
will lead to the entire output being NAN or INF.
421+
Use method='direct' when your input contains NAN or INF values.
422+
423+
Default: ``'auto'``.
424+
425+
Returns
426+
-------
427+
out : ndarray
428+
Discrete, linear convolution of `a` and `v`.
429+
430+
See Also
431+
--------
432+
:obj:`dpnp.correlate` : Cross-correlation of two 1-dimensional sequences.
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+
dpnp.check_supported_arrays_type(a, v)
477+
478+
if a.size == 0 or v.size == 0:
479+
raise ValueError(
480+
f"Array arguments cannot be empty. "
481+
f"Received sizes: a.size={a.size}, v.size={v.size}"
482+
)
483+
if a.ndim > 1 or v.ndim > 1:
484+
raise ValueError(
485+
f"Only 1-dimensional arrays are supported. "
486+
f"Received shapes: a.shape={a.shape}, v.shape={v.shape}"
487+
)
488+
489+
if a.ndim == 0:
490+
a = dpnp.reshape(a, (1,))
491+
if v.ndim == 0:
492+
v = dpnp.reshape(v, (1,))
493+
494+
device = a.sycl_device
495+
rdtype = result_type_for_device([a.dtype, v.dtype], device)
496+
497+
if v.size > a.size:
498+
a, v = v, a
499+
500+
r = _convolve_impl(a, v, mode, method, rdtype)
501+
502+
return dpnp.asarray(r, dtype=rdtype, order="C")
503+
504+
360505
def corrcoef(x, y=None, rowvar=True, *, dtype=None):
361506
"""
362507
Return Pearson product-moment correlation coefficients.
@@ -714,17 +859,7 @@ def correlate(a, v, mode="valid", method="auto"):
714859
revert = True
715860
a, v = v, a
716861

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}")
862+
r = _convolve_impl(a, v[::-1], mode, method, rdtype)
728863

729864
if revert:
730865
r = r[::-1]

dpnp/tests/test_mathematical.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -108,35 +108,6 @@ def test_conj_out(self, dtype):
108108
assert_dtype_allclose(result, expected)
109109

110110

111-
@pytest.mark.usefixtures("allow_fall_back_on_numpy")
112-
class TestConvolve:
113-
def test_object(self):
114-
d = [1.0] * 100
115-
k = [1.0] * 3
116-
assert_array_equal(dpnp.convolve(d, k)[2:-2], dpnp.full(98, 3.0))
117-
118-
def test_no_overwrite(self):
119-
d = dpnp.ones(100)
120-
k = dpnp.ones(3)
121-
dpnp.convolve(d, k)
122-
assert_array_equal(d, dpnp.ones(100))
123-
assert_array_equal(k, dpnp.ones(3))
124-
125-
def test_mode(self):
126-
d = dpnp.ones(100)
127-
k = dpnp.ones(3)
128-
default_mode = dpnp.convolve(d, k)
129-
full_mode = dpnp.convolve(d, k, mode="full")
130-
assert_array_equal(full_mode, default_mode)
131-
# integer mode
132-
with assert_raises(ValueError):
133-
dpnp.convolve(d, k, mode=-1)
134-
assert_array_equal(dpnp.convolve(d, k, mode=2), full_mode)
135-
# illegal arguments
136-
with assert_raises(TypeError):
137-
dpnp.convolve(d, k, mode=None)
138-
139-
140111
class TestClip:
141112
@pytest.mark.parametrize(
142113
"dtype", get_all_dtypes(no_bool=True, no_none=True, no_complex=True)

0 commit comments

Comments
 (0)