Python 中概率密度函数的更快卷积
Faster convolution of probability density functions in Python
假设需要计算一般数量的离散概率密度函数的卷积。对于下面的示例,有四种分布,它们具有指定概率的值 0、1、2:
import numpy as np
pdfs = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1],[0.3,0.7,0.0],[1.0,0.0,0.0]])
卷积可以这样找到:
pdf = pdfs[0]
for i in range(1,pdfs.shape[0]):
pdf = np.convolve(pdfs[i], pdf)
看到 0,1,...,8 的概率由
给出
array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007, 0. , 0. , 0. ])
这部分是我代码中的瓶颈,似乎必须有一些可用的东西来矢量化这个操作。有没有人有让它更快的建议?
或者,您可以使用的解决方案
pdf1 = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1]])
pdf2 = np.array([[0.3,0.7,0.0],[1.0,0.0,0.0]])
convolve(pd1,pd2)
并得到成对卷积
array([[ 0.18, 0.51, 0.24, 0.07, 0. ],
[ 0.5, 0.4, 0.1, 0. , 0. ]])
也会有很大帮助。
您可以使用快速傅里叶变换 (FFT) 有效地计算所有 PDF 的卷积:关键事实是 FFT of the convolution 是各个概率密度函数的 FFT 的乘积。所以对每个PDF进行变换,将变换后的PDF相乘,然后进行逆变换。您需要用零填充每个输入 PDF 以达到适当的长度,以避免回绕的影响。
这应该相当有效:如果您有 m
个 PDF,每个包含 n
个条目,那么使用此方法计算卷积的时间应该增加为 (m^2)n log(mn)
。时间主要由 FFT 控制,我们正在有效地计算 m + 1
个独立的 FFT(m
个正向变换和一个反向变换),每个 FFT 的长度不大于 mn
个数组。但一如既往,如果你想要真正的时间,你应该分析。
这是一些代码:
import numpy.fft
def convolve_many(arrays):
"""
Convolve a list of 1d float arrays together, using FFTs.
The arrays need not have the same length, but each array should
have length at least 1.
"""
result_length = 1 + sum((len(array) - 1) for array in arrays)
# Copy each array into a 2d array of the appropriate shape.
rows = numpy.zeros((len(arrays), result_length))
for i, array in enumerate(arrays):
rows[i, :len(array)] = array
# Transform, take the product, and do the inverse transform
# to get the convolution.
fft_of_rows = numpy.fft.fft(rows)
fft_of_convolution = fft_of_rows.prod(axis=0)
convolution = numpy.fft.ifft(fft_of_convolution)
# Assuming real inputs, the imaginary part of the output can
# be ignored.
return convolution.real
将此应用于您的示例,这是我得到的结果:
>>> convolve_many([[0.6, 0.3, 0.1], [0.5, 0.4, 0.1], [0.3, 0.7], [1.0]])
array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007])
这是基本思想。如果你想调整它,你也可以看看 numpy.fft.rfft
(and its inverse, numpy.fft.irfft
),它利用输入是真实的这一事实来产生更紧凑的转换数组。您还可以通过用零填充 rows
数组来提高速度,以便列的总数最适合执行 FFT。这里 "optimal" 的定义将取决于 FFT 实现,但例如,2 的幂将是很好的目标。最后,如果所有输入数组的长度都相同,则在创建 rows
时可以进行一些明显的简化。但我会将这些潜在的增强功能留给您。
假设需要计算一般数量的离散概率密度函数的卷积。对于下面的示例,有四种分布,它们具有指定概率的值 0、1、2:
import numpy as np
pdfs = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1],[0.3,0.7,0.0],[1.0,0.0,0.0]])
卷积可以这样找到:
pdf = pdfs[0]
for i in range(1,pdfs.shape[0]):
pdf = np.convolve(pdfs[i], pdf)
看到 0,1,...,8 的概率由
给出array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007, 0. , 0. , 0. ])
这部分是我代码中的瓶颈,似乎必须有一些可用的东西来矢量化这个操作。有没有人有让它更快的建议?
或者,您可以使用的解决方案
pdf1 = np.array([[0.6,0.3,0.1],[0.5,0.4,0.1]])
pdf2 = np.array([[0.3,0.7,0.0],[1.0,0.0,0.0]])
convolve(pd1,pd2)
并得到成对卷积
array([[ 0.18, 0.51, 0.24, 0.07, 0. ],
[ 0.5, 0.4, 0.1, 0. , 0. ]])
也会有很大帮助。
您可以使用快速傅里叶变换 (FFT) 有效地计算所有 PDF 的卷积:关键事实是 FFT of the convolution 是各个概率密度函数的 FFT 的乘积。所以对每个PDF进行变换,将变换后的PDF相乘,然后进行逆变换。您需要用零填充每个输入 PDF 以达到适当的长度,以避免回绕的影响。
这应该相当有效:如果您有 m
个 PDF,每个包含 n
个条目,那么使用此方法计算卷积的时间应该增加为 (m^2)n log(mn)
。时间主要由 FFT 控制,我们正在有效地计算 m + 1
个独立的 FFT(m
个正向变换和一个反向变换),每个 FFT 的长度不大于 mn
个数组。但一如既往,如果你想要真正的时间,你应该分析。
这是一些代码:
import numpy.fft
def convolve_many(arrays):
"""
Convolve a list of 1d float arrays together, using FFTs.
The arrays need not have the same length, but each array should
have length at least 1.
"""
result_length = 1 + sum((len(array) - 1) for array in arrays)
# Copy each array into a 2d array of the appropriate shape.
rows = numpy.zeros((len(arrays), result_length))
for i, array in enumerate(arrays):
rows[i, :len(array)] = array
# Transform, take the product, and do the inverse transform
# to get the convolution.
fft_of_rows = numpy.fft.fft(rows)
fft_of_convolution = fft_of_rows.prod(axis=0)
convolution = numpy.fft.ifft(fft_of_convolution)
# Assuming real inputs, the imaginary part of the output can
# be ignored.
return convolution.real
将此应用于您的示例,这是我得到的结果:
>>> convolve_many([[0.6, 0.3, 0.1], [0.5, 0.4, 0.1], [0.3, 0.7], [1.0]])
array([ 0.09 , 0.327, 0.342, 0.182, 0.052, 0.007])
这是基本思想。如果你想调整它,你也可以看看 numpy.fft.rfft
(and its inverse, numpy.fft.irfft
),它利用输入是真实的这一事实来产生更紧凑的转换数组。您还可以通过用零填充 rows
数组来提高速度,以便列的总数最适合执行 FFT。这里 "optimal" 的定义将取决于 FFT 实现,但例如,2 的幂将是很好的目标。最后,如果所有输入数组的长度都相同,则在创建 rows
时可以进行一些明显的简化。但我会将这些潜在的增强功能留给您。