python - scipy.signal.convolve 中来自黎曼和的人工制品

标签 python scipy convolution

简短摘要:如何快速计算两个数组的有限卷积?

问题描述

我正在尝试获得由定义的两个函数 f(x), g(x) 的有限卷积

finite convolution

为了实现这一点,我对函数进行了离散采样,并将它们转换为长度为 steps 的数组:

xarray = [x * i / steps for i in range(steps)]
farray = [f(x) for x in xarray]
garray = [g(x) for x in xarray]

然后我尝试使用 scipy.signal.convolve 函数计算卷积。此函数给出与 conv 建议的算法相同的结果 here .然而,结果与分析解决方案有很大不同。修改算法 conv 以使用梯形法则可得到所需的结果。

为了说明这一点,我让

f(x) = exp(-x)
g(x) = 2 * exp(-2 * x)

结果是:

enter image description here

这里Riemann代表一个简单的Riemann和,trapezoidal是Riemann算法的修改版本,使用梯形法则,scipy.signal.convolve 是 scipy 函数,analytical 是解析卷积。

现在让 g(x) = x^2 * exp(-x) 结果变成:

enter image description here

这里的“比率”是从 scipy 获得的值与分析值的比率。以上证明了问题不能通过重归一化积分来解决。

问题

是否可以使用 scipy 的速度但保留梯形法则的更好结果,还是我必须编写 C 扩展才能获得所需的结果?

一个例子

只需复制并粘贴下面的代码即可查看我遇到的问题。通过增加 steps 变量,可以使这两个结果更加一致。我认为问题是由于右手黎曼和的伪影造成的,因为当积分增加时积分被高估,而当积分减少时再次接近解析解。

编辑:我现在包含了原始算法 2作为比较,它给出与 scipy.signal.convolve 函数相同的结果。

import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
import math

def convolveoriginal(x, y):
    '''
    The original algorithm from http://www.physics.rutgers.edu/~masud/computing/WPark_recipes_in_python.html.
    '''
    P, Q, N = len(x), len(y), len(x) + len(y) - 1
    z = []
    for k in range(N):
        t, lower, upper = 0, max(0, k - (Q - 1)), min(P - 1, k)
        for i in range(lower, upper + 1):
            t = t + x[i] * y[k - i]
        z.append(t)
    return np.array(z) #Modified to include conversion to numpy array

def convolve(y1, y2, dx = None):
    '''
    Compute the finite convolution of two signals of equal length.
    @param y1: First signal.
    @param y2: Second signal.
    @param dx: [optional] Integration step width.
    @note: Based on the algorithm at http://www.physics.rutgers.edu/~masud/computing/WPark_recipes_in_python.html.
    '''
    P = len(y1) #Determine the length of the signal
    z = [] #Create a list of convolution values
    for k in range(P):
        t = 0
        lower = max(0, k - (P - 1))
        upper = min(P - 1, k)
        for i in range(lower, upper):
            t += (y1[i] * y2[k - i] + y1[i + 1] * y2[k - (i + 1)]) / 2
        z.append(t)
    z = np.array(z) #Convert to a numpy array
    if dx != None: #Is a step width specified?
        z *= dx
    return z

steps = 50 #Number of integration steps
maxtime = 5 #Maximum time
dt = float(maxtime) / steps #Obtain the width of a time step
time = [dt * i for i in range (steps)] #Create an array of times
exp1 = [math.exp(-t) for t in time] #Create an array of function values
exp2 = [2 * math.exp(-2 * t) for t in time]
#Calculate the analytical expression
analytical = [2 * math.exp(-2 * t) * (-1 + math.exp(t)) for t in time]
#Calculate the trapezoidal convolution
trapezoidal = convolve(exp1, exp2, dt)
#Calculate the scipy convolution
sci = signal.convolve(exp1, exp2, mode = 'full')
#Slice the first half to obtain the causal convolution and multiply by dt
#to account for the step width
sci = sci[0:steps] * dt
#Calculate the convolution using the original Riemann sum algorithm
riemann = convolveoriginal(exp1, exp2)
riemann = riemann[0:steps] * dt

#Plot
plt.plot(time, analytical, label = 'analytical')
plt.plot(time, trapezoidal, 'o', label = 'trapezoidal')
plt.plot(time, riemann, 'o', label = 'Riemann')
plt.plot(time, sci, '.', label = 'scipy.signal.convolve')
plt.legend()
plt.show()

感谢您的宝贵时间!

最佳答案

或者,对于那些喜欢 numpy 而不是 C 的人。它会比 C 实现慢,但它只是几行。

>>> t = np.linspace(0, maxtime-dt, 50)
>>> fx = np.exp(-np.array(t))
>>> gx = 2*np.exp(-2*np.array(t))
>>> analytical = 2 * np.exp(-2 * t) * (-1 + np.exp(t))

在这种情况下这看起来像梯形(但我没有检查数学)

>>> s2a = signal.convolve(fx[1:], gx, 'full')*dt
>>> s2b = signal.convolve(fx, gx[1:], 'full')*dt
>>> s = (s2a+s2b)/2
>>> s[:10]
array([ 0.17235682,  0.29706872,  0.38433313,  0.44235042,  0.47770012,
        0.49564748,  0.50039326,  0.49527721,  0.48294359,  0.46547582])
>>> analytical[:10]
array([ 0.        ,  0.17221333,  0.29682141,  0.38401317,  0.44198216,
        0.47730244,  0.49523485,  0.49997668,  0.49486489,  0.48254154])

最大绝对误差:

>>> np.max(np.abs(s[:len(analytical)-1] - analytical[1:]))
0.00041657780840698155
>>> np.argmax(np.abs(s[:len(analytical)-1] - analytical[1:]))
6

关于python - scipy.signal.convolve 中来自黎曼和的人工制品,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8874170/

相关文章:

python - 如何将对象存储在动态生成的变量中?

Python While 循环未返回所需值

python - scipy 在 linux 上安装 : can't find a lapack object sgges_

python - 分别为每个 channel 对 3d 数组与 2d 内核进行卷积

python - 如何在 Django 中显示 Iftop

Python连载,能写不能读

python - 了解 scipy.interpolate 的 interpn 函数

python-3.x - 如何在Python的scipy信号卷积中添加比率参数?

keras - 在keras的自动编码器的最后一层裁剪

pytorch - 在 pytorch 中使用可分离的 2D 卷积实现 3D 高斯模糊