通用编程:Log FFT或高精度卷积(python)

时间:2012-03-09 22:47:54

标签: python fft numerical-methods convolution

我有一个稍微不寻常的问题,但我试图避免重新编码FFT。

一般来说,我想知道这个:如果我有一个为float类型实现的算法,但它可以在定义某组操作的任何地方工作(例如复数,也为其定义{ {1}},+,...),在支持这些操作的另一种类型上使用该算法的最佳方法是什么?在实践中,这很棘手,因为通常数字算法是为了速度而非通用性而编写的。

具体地:
我正在使用具有非常高动态范围的值,因此我想将它们存储在日志空间中(主要是为了避免下溢)。

我想要的是某些系列的FFT的日志:

*

即使这样也会导致严重的下溢。我想要的是存储日志值并使用x = [1,2,3,4,5] fft_x = [ log( x_val ) for x_val in fft(x) ] 代替+*代替logaddexp等。

我对如何做到这一点的想法是实现一个简单的LogFloat类来定义这些基本操作(但在日志空间中运行)。然后我可以通过让它使用我记录的值来运行FFT代码。

+

然后,想法是构建一个class LogFloat: def __init__(self, sign, log_val): assert(float(sign) in (-1, 1)) self.sign = int(sign) self.log_val = log_val @staticmethod def from_float(fval): return LogFloat(sign(fval), log(abs(fval))) def __imul__(self, lf): self.sign *= lf.sign self.log_val += lf.log_val return self def __idiv__(self, lf): self.sign *= lf.sign self.log_val -= lf.log_val return self def __iadd__(self, lf): if self.sign == lf.sign: self.log_val = logaddexp(self.log_val, lf.log_val) else: # subtract the smaller magnitude from the larger if self.log_val > lf.log_val: self.log_val = log_sub(self.log_val, lf.log_val) else: self.log_val = log_sub(lf.log_val, self.log_val) self.sign *= -1 return self def __isub__(self, lf): self.__iadd__(LogFloat(-1 * lf.sign, lf.log_val)) return self def __pow__(self, lf): # note: there may be a way to do this without exponentiating # if the exponent is 0, always return 1 # print self, '**', lf if lf.log_val == -float('inf'): return LogFloat.from_float(1.0) lf_value = lf.sign * math.exp(lf.log_val) if self.sign == -1: # note: in this case, lf_value must be an integer return LogFloat(self.sign**int(lf_value), self.log_val * lf_value) return LogFloat(self.sign, self.log_val * lf_value) def __mul__(self, lf): temp = LogFloat(self.sign, self.log_val) temp *= lf return temp def __div__(self, lf): temp = LogFloat(self.sign, self.log_val) temp /= lf return temp def __add__(self, lf): temp = LogFloat(self.sign, self.log_val) temp += lf return temp def __sub__(self, lf): temp = LogFloat(self.sign, self.log_val) temp -= lf return temp def __str__(self): result = str(self.sign * math.exp(self.log_val)) + '(' if self.sign == -1: result += '-' result += 'e^' + str(self.log_val) + ')' return result def __neg__(self): return LogFloat(-self.sign, self.log_val) def __radd__(self, val): # for sum if val == 0: return self return self + val 的列表,然后在FFT中使用它:

LogFloat

如果我重新实现FFT(并且在我之前使用x_log_float = [ LogFloat.from_float(x_val) for x_val in x ] fft_x_log_float = fft(x_log_float) 的任何地方只使用LogFloat),这肯定可以完成,但我想我会征求意见。这是一个相当反复出现的问题:我有一个库存算法,我想在日志空间中操作(它只使用一些操作,如'+',' - ','','/'等)。< / p>

这让我想起用模板编写泛型函数,以便返回参数,参数等是从相同的类型构造的。例如,如果你可以对浮点数进行FFT,你应该能够轻松地对复数值进行迭代(通过简单地使用为复杂值提供必要操作的类)。

目前看来,看起来所有的FFT实现都是针对最前沿的速度编写的,因此不是很通用。到目前为止,看起来我不得不为通用类型重新实现FFT ......

我这样做的原因是因为我想要非常高精度的卷积(并且 N ^ 2 运行时非常慢)。 任何建议将不胜感激。

*注意,我可能需要为float实现三角函数,这没关系。

修改 这确实有效,因为LogFloat是一个可交换的环(并且它不需要为LogFloat实现三角函数)。最简单的方法是重新实现FFT,但@JFSebastian也指出了一种使用LogFloat通用卷积的方法,这避免了编码FFT(再次,使用DSP教科书或the Wikipedia pseudocode)。

2 个答案:

答案 0 :(得分:1)

我承认我在你的问题中没有完全跟上数学。然而,听起来你真正想知道的是如何处理极小和大(绝对值)数字而不会出现下溢和溢出。除非我误解你,否则我认为这类似于我处理货币单位的问题,而不是由于四舍五入而导致数十亿美元交易的便士。如果是这种情况,我的解决方案是Python的内置十进制数学模块。该文档适用于Python 2Python 3。简短版本是十进制数学是一种任意精度的浮点和定点类型。 Python模块符合IBM / IEEE十进制数学标准。在Python 3.3(目前处于alpha形式,但我一直使用它没有任何问题),该模块已在C中重写,速度提高了100倍(在我的快速测试中)。

答案 1 :(得分:0)

您可以将时域样本缩放大量s以避免下溢,然后,如果

  
    

F (f(t))= X (j * w)

  

然后

  
    

F (s f(s * t))&lt; - &gt; <强> X (W / S)

  

现在使用卷积定理,您可以找出如何缩放最终结果以消除缩放因子的影响。