Parece que a convolução baseada em FFT sofre de resolução limitada de ponto flutuante devido à avaliação de tudo em torno das raízes da unidade, como você pode ver no erro de fator neste código Python:
from scipy.signal import convolve, fftconvolve
a = [1.0, 1E-15]
b = [1.0, 1E-15]
convolve(a, b) # [ 1.00000000e+00, 2.00000000e-15, 1.00000000e-30]
fftconvolve(a, b) # [ 1.00000000e+00, 2.11022302e-15, 1.10223025e-16]
Existem algoritmos de convolução rápida que não sofrem com esse problema?
Ou a convolução direta (tempo quadrático) é a única maneira de obter uma solução precisa?
(Se esses números pequenos são significativos o suficiente para não serem cortados é o meu ponto).
fft
convolution
algorithms
fourier
fast-convolution
user541686
fonte
fonte
convolve()
apenas chamafftconvolve()
agora, se o tamanho da entrada for grande. Especifiquemethod='direct'
se você deseja direto.Respostas:
Isenção de responsabilidade: Eu sei que esse tópico é mais antigo, mas se alguém estiver procurando por "convolução rápida precisa, alta faixa dinâmica" ou semelhante, este é um dos primeiros de apenas alguns resultados decentes. Quero compartilhar minhas idéias que obtive sobre esse tópico, para que possa ajudar alguém no futuro. Peço desculpas se posso usar os termos errados na minha resposta, mas tudo o que encontrei neste tópico é bastante vago e leva a confusão, mesmo neste tópico. Espero que o leitor entenda de qualquer maneira.
A convolução direta é mais precisa para usinar a precisão de cada ponto, ou seja, o erro relativo é geralmente aproximado ou próximo de 1.e-16 para precisão dupla para cada ponto do resultado. Cada ponto tem 16 dígitos corretos. Os erros de arredondamento podem ser significativos para convoluções atipicamente grandes e, estritamente falando, deve-se ter cuidado com o cancelamento e usar algo como a soma de Kahan e tipos de dados de precisão alta o suficiente, mas na prática o erro é quase sempre ideal.
O erro de uma convolução da FFT além dos erros de arredondamento é um erro "relativo global", o que significa que o erro em cada ponto depende da precisão da máquina e do valor de pico do resultado. Por exemplo, se o valor de pico do resultado for2⋅109⋅10−16=2⋅10−7 . Portanto, se um valor no resultado for muito pequeno, digamos10- 9 , o erro relativo nesse ponto pode ser enorme. A convolução da FFT é basicamente inútil se você precisar de pequenos erros relativos no final do seu resultado, por exemplo, se houver uma deterioração exponencial dos seus dados e se precisar de valores precisos no final. Curiosamente, se a convolução da FFT não se limita a esse erro, ela apresenta erros de arredondamento muito menores em comparação à convolução direta, pois você obviamente faz menos adições / multiplicações. Na verdade, é por isso que as pessoas costumam afirmar que a convolução da FFT é mais precisa, e elas estão quase certas em algum sentido, para que possam ser bastante inflexíveis.
2.e9
, o erro absoluto em cada ponto seráInfelizmente, não há uma correção universal fácil para obter convoluções rápidas e precisas, mas, dependendo do seu problema, pode haver uma ... Encontrei duas:
Se você possui núcleos lisos que podem ser bem aproximados por um polinômio na cauda, o método Multipolo rápido da caixa preta com interpolação Chebyshev pode ser interessante para você. Se o seu kernel é "bom", isso funciona perfeitamente: você obtém complexidade computacional linear (!) E precisão da precisão da máquina. Se isso se encaixa no seu problema, você deve usá-lo. No entanto, não é fácil de implementar.
Para alguns kernels específicos (acho que funções convexas, geralmente a partir de densidades de probabilidade), você pode usar um "deslocamento exponencial" para obter um erro ideal em alguma parte da cauda do resultado. Existe uma tese de doutorado e um github com uma implementação em python usando isso sistematicamente, e o autor o chama de convolução FFT precisa . Na maioria dos casos, isso não é super útil, pois ele regride de volta à convolução direta ou você pode usar a convolução da FFT de qualquer maneira. Embora o código faça isso automaticamente, o que é legal, é claro.
--------------------EDITAR:--------------------
Eu olhei um pouco para o algoritmo Karatsuba (eu realmente fiz uma pequena implementação) e, para mim, parece que ele geralmente tem um comportamento de erro semelhante ao da convolução da FFT, ou seja, você recebe um erro em relação ao valor de pico do resultado. Devido à natureza de divisão e conquista do algoritmo, alguns valores na cauda do resultado realmente apresentam um erro melhor, mas não vejo uma maneira sistemática fácil de dizer quais ou, de qualquer forma, como usar essa observação. Que pena, no começo eu pensei que o Karatsuba poderia ser algo útil entre a convolução direta e a FFT. Mas não vejo casos de uso comuns em que o Karatsuba deve ser preferido em relação aos dois algoritmos comuns de convolução.
E para acrescentar à mudança exponencial que mencionei acima: há muitos casos em que você pode usá-la para melhorar o resultado de uma convolução, mas, novamente, não é uma correção universal. Na verdade, eu uso isso junto com a convolução da FFT para obter bons resultados (no caso geral de todas as entradas: no pior mesmo erro que a convolução normal da FFT, no melhor erro relativo em cada ponto da precisão da máquina). Mas, novamente, isso realmente funciona muito bem para kernels e dados específicos, mas para mim é o kernel e os dados ou um pouco exponencial em decadência.
fonte
Um candidato é o algoritmo Karatsuba , executado emO (Nregistro23) ≈ O (N1.5849625) Tempo. Não é baseado em transformação. Há também algum código com uma explicação no arquivo de código-fonte do Music-DSP, que parece uma descoberta independente de um algoritmo semelhante.
Testar uma implementação Python do algoritmo Karatsuba (instalado por
sudo pip install karatsuba
) usando os números em sua pergunta mostra que, mesmo com números de ponto flutuante de 64 bits, o erro relativo é grande para um dos valores de saída:que imprime:
fonte
Em vez de descartar o algoritmo de convolução rápida, por que não usar uma FFT com maior faixa dinâmica?
Uma resposta a esta pergunta mostra como usar a biblioteca Eigen FFT com multiprecisão de impulso.
fonte
Acredito que a precisão do algoritmo Cordic possa ser estendida até onde você desejar, se assim for, use um DFT inteiro e um comprimento de palavra apropriado ao seu problema.
O mesmo seria verdade com a convolução direta, use números inteiros muito longos.
fonte
A convolução quadrática no tempo para obter um resultado de DFT geralmente é menos precisa (pode incorrer em ruído numérico de quantização mais finita, devido a uma camada mais profunda de etapas aritméticas) do que o algoritmo FFT típico ao usar os mesmos tipos aritméticos e unidades de operação.
Convém tentar tipos de dados com maior precisão (precisão quádrupla ou aritmética bignum).
fonte