convolution¶
Computes the convolution, and optionally, the correlation of two real vectors.
Synopsis¶
convolution (x, y)
Required Arguments¶
- float
x[]
(Input) - Real vector of length
nx
. - float
y[]
(Input) - Real vector of length
ny
.
Return Value¶
An array of length nz
containing the convolution of x
and y
. If
no zeros are computed, then None
is returned.
Optional Arguments¶
periodic
- The input is periodic.
correlation
- Return the correlation of
x
andy
. firstCall
- If the function is called multiple times with the same
nx
andny
, select this option on the first call. continueCall
- If the function is called multiple times with the same
nx
andny
, select this option on intermediate calls. lastCall
- If the function is called multiple times with the same
nx
andny
, select this option on the final call.
Description¶
The function convolution
, by default, computes the discrete convolution
of two sequences x and y. More precisely, let \(n_x\) be the length
of x, and \(n_y\) denote the length of y. If a circular convolution
is desired, the optional argument periodic
must be selected. We set
and we pad out the shorter vector with zeros. Then, we compute
where the index on x is interpreted as a positive number between 1 and \(n_z\), modulo \(n_z\).
The technique used to compute the \(z_i\)’s is based on the fact that
the (complex discrete) Fourier transform maps convolution into
multiplication. Thus, the Fourier transform of z
is given by
where the following equation is true.
The technique used here to compute the convolution is to take the discrete
Fourier transform of x and y, multiply the results together
component-wise, and then take the inverse transform of this product. It is
very important to make sure that \(n_z\) is the product of small primes
if option periodic
is selected. If \(n_z\) is a product of small
primes, then the computational effort will be proportional to \(n_z
\log(n_z)\). If option periodic
is not selected, then a good value is
chosen for \(n_z\) so that the Fourier transforms are efficient and
\(n_z \geq n_x + n_y − 1\). This will mean that both vectors will be
padded with zeros.
We point out that no complex transforms of x or y are taken since both sequences are real, and real transforms can simulate the complex transform above. Such a strategy is six times faster and requires less space than when using the complex transform.
Optionally, the function convolution
computes the discrete correlation
of two sequences x and y. More precisely, let n be the length of x
and y. If a circular correlation is desired, then option periodic
must
be selected. We set (on output)
\(n_z = n\) | if periodic is
chosen |
\(n_z = 2^\alpha 3^\beta 5^\gamma\) geq 2n - 1 | if periodic is
not chosen |
where α, β, and γ are nonnegative integers yielding the smallest number of the type \(2^\alpha 3^\beta 5^\gamma\) satisfying the inequality. Once \(n_z\) is determined, we pad out the vectors with zeros. Then, we compute
where the index on x is interpreted as a positive number between one and \(n_z\), modulo \(n_z\). Note that this means that
contains the correlation of \(x(k − 1)\) with y as \(k = 0, 1, ..., n_z/2\). Thus, if \(x(k − 1) = y(k)\) for all k, then we would expect
to be the largest component of z. The technique used to compute the \(z_i\)’s is based on the fact that the (complex discrete) Fourier transform maps correlation into multiplication. Thus, the Fourier transform of z is given by
where the following equation is true.
Thus, the technique used here to compute the correlation is to take the
discrete Fourier transform of x and the conjugate of the discrete Fourier
transform of y, multiply the results together component-wise, and then take
the inverse transform of this product. It is very important to make sure that
\(n_z\) is the product of small primes if periodic
is selected. If
\(n_z\) is the product of small primes, then the computational effort
will be proportional to \(n_z \log(n_z)\). If periodic
is not chosen,
then a good value is chosen for \(n_z\) so that the Fourier transforms
are efficient and \(n_z \geq 2n − 1\). This will mean that both vectors
will be padded with zeros.
We point out that no complex transforms of x or y are taken since both sequences are real, and real transforms can simulate the complex transform above. Such a strategy is six times faster and requires less space than when using the complex transform.
Examples¶
Example 1¶
This example computes a nonperiodic convolution. The idea here is that you can compute a moving average of the type found in digital filtering using this function. The averaging operator in this case is especially simple and is given by averaging five consecutive points in the sequence. We try to recover the values of an exponential function contaminated by noise. The large error for the last value has to do with the fact that the convolution is averaging the zeros in the “pad” rather than the function values. Notice that the signal size is 100, but only reports the errors at 10 points.
from __future__ import print_function
from numpy import *
from pyimsl.math.convolution import convolution
from pyimsl.math.constant import constant
from pyimsl.math.randomSeedSet import randomSeedSet
from pyimsl.math.randomUniform import randomUniform
ny = 100
nfltr = 5
pi = constant("pi")
twopi = 2.0 * pi
fltr = empty(nfltr)
y = empty(ny)
# Set up the filter
for i in range(0, nfltr):
fltr[i] = 0.2
# Set up y-vector for the nonperiodic stuff
randomSeedSet(1234579)
noise = randomUniform(ny)
for i in range(0, ny):
x = float(i) / (ny - 1)
y[i] = exp(x) + 0.5 * noise[i] - 0.25
# Call the convolution routine for the nonperiodic case.
z = convolution(fltr, y)
# Call test routines to check z & zhat here. Print results
print("Nonperiodic Case")
print(" x F1(x) Original Error Filtered Error")
total1 = 0.0
total2 = 0.0
for i in range(0, ny):
if (i > ny - 2):
k = i - ny + 2
else:
k = i + 2
x = float(i) / float(ny - 1)
origer = abs(y[i] - exp(x))
fltrer = abs(z[i + 2] - exp(x))
if ((i % 11) == 0):
print("%10.4f%13.4f%18.4f%18.4f"
% (x, exp(x), origer, fltrer))
total1 += origer
total2 += fltrer
print("Average absolute error before filter: %10.5f" % (total1 / ny))
print("Average absolute error after filter: %11.5f" % (total2 / ny))
Output¶
Nonperiodic Case
x F1(x) Original Error Filtered Error
0.0000 1.0000 0.0811 0.3523
0.1111 1.1175 0.0226 0.0754
0.2222 1.2488 0.1526 0.0488
0.3333 1.3956 0.0959 0.0161
0.4444 1.5596 0.1747 0.0276
0.5556 1.7429 0.1035 0.0250
0.6667 1.9477 0.0402 0.0562
0.7778 2.1766 0.0673 0.0835
0.8889 2.4324 0.1044 0.0050
1.0000 2.7183 0.0154 1.1255
Average absolute error before filter: 0.12481
Average absolute error after filter: 0.06785
Example 2¶
This example computes both a periodic correlation between two distinct signals x and y. There are 100 equally spaced points on the interval \([0, 2\pi]\) and \(f_1(x) = \sin (x)\). Define x and y as follows:
Note that the maximum value of z (the correlation of x with) occurs at \(i = 25\), which corresponds to the offset.
from __future__ import print_function
from numpy import *
from pyimsl.math.convolution import convolution
from pyimsl.math.constant import constant
from pyimsl.math.vectorNorm import vectorNorm
n = 100
pi = constant("pi")
x = empty(n)
y = empty(n)
# Set up y-vector for the nonperiodic case.
for i in range(0, n):
x[i] = sin(2.0 * pi * i / (n - 1))
y[i] = sin(2.0 * pi * i / (n - 1) + pi / 2.0)
# Call the correlation function for the nonperiodic case
z = convolution(x, y, correlation=True, periodic=True)
xnorm = vectorNorm(x)
ynorm = vectorNorm(y)
for i in range(0, n):
z[i] /= xnorm * ynorm
max = z[0]
k = 0
for i in range(1, n):
if (max < z[i]):
max = z[i]
k = i
print("The element of Z with the largest normalized")
print("value is Z(%2d)." % (k))
print("The normalized value of Z(%2d) is %6.3f" % (k, z[k]))
Output¶
The element of Z with the largest normalized
value is Z(25).
The normalized value of Z(25) is 1.000