Более быстрая свертка функций плотности вероятности в 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. ]])

также очень помогло бы.

1 ответов


вы можете вычислить свертку всех ваших PDF-файлов эффективно, используя быстрые преобразования Фурье( FFS): ключевым фактом является то, что БПФ свертки является произведением FFTs индивидуальных функций плотности вероятности. Поэтому преобразуйте каждый PDF-файл, умножьте преобразованные PDF-файлы вместе, а затем выполните обратное преобразование. Вам нужно будет заполнить каждый входной PDF-файл нулями соответствующей длины, чтобы избежать эффектов от wraparound.

это должно быть разумно эффективный: если у вас есть m PDF-файлы, каждый из которых содержит n записи, то время для вычисления свертки с помощью этого метода должно расти как (m^2)n log(mn). Время доминирует FFS, и мы эффективно вычисляем m + 1 независимая БПФ (m прямые преобразования и одно обратное преобразование), каждый из массива длиной не более 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])