2012-03-09 60 views
5

我有一個稍微不尋常的問題,但我試圖避免重新編碼FFT。通用編程:日誌FFT或高精度卷積(python)

在一般情況下,我想知道這一點:如果我有一個用於float類型實現的算法,但只要有一定的操作集定義(如複雜的數字,這也定義了它的工作+*, ...),在支持這些操作的另一種類型上使用該算法的最佳方式是什麼?在實踐中,這是棘手的,因爲通常數值算法是爲了速度而不是通用性編寫的。

具體做法是:
我的工作與價值觀具有非常高的動態範圍,所以我想將它們存儲在日誌空間(主要是爲了避免下溢)。

我想是一些系列的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 

然後,想法是構建的LogFloat秒的列表,然後在FFT使用它:

x_log_float = [ LogFloat.from_float(x_val) for x_val in x ] 
fft_x_log_float = fft(x_log_float) 

這絕對可以做,如果我再執行FFT(和簡單。使用LogFloat的地方我會用float過,但我想我會請教這是一個相當反覆出現的問題:我有一隻股票的算法,我想在日誌空間操作(和它僅使用像「操作了一把+ ',' - ','','/'等)。

這讓我想起使用模板編寫泛型函數,以便返回參數,參數等由相同類型構造而成。例如,如果你可以做一個浮點數的FFT,你應該能夠很容易地對複數值做一個(通過簡單地使用一個爲複數值提供必要操作的類)。

當前標準的,它看起來像所有的FFT實現是帶血的速度寫的,所以不會很一般。因此,截至目前,它看起來像我不得不重新實現FFT泛型類型...

的原因,我這樣做是因爲我要非常高精度的卷積(和N^2運行非常緩慢)。 任何意見將不勝感激。

*注意,我可能需要實現三角函數的LogFloat,這將是罰款。

編輯: 這並不工作,因爲LogFloat是一個交換環(和它不要求執行的三角函數的LogFloat)。最簡單的方法是重新實現FFT,但@ J.F.Sebastian還指出了一種使用通用卷積的方法,該方法避免了對FFT進行編碼(使用DSP教科書或使用DSP教科書或the Wikipedia pseudocode也很容易)。

+0

我不知道你的問題是什麼。如果fft是用python編寫的,那麼上面應該(模仿雄心勃勃的瘋狂)工作。如果它調用了一個c實現,那麼它將無法工作,因爲c代碼就是c代碼,它不會執行python的操作。 那麼問題是什麼? – 2012-03-10 01:25:15

+0

有[通用卷積](http://www.math.ucla.edu/~jimc/mathnet_d/sage/reference/sage/rings/polynomial/convolution.html)。 'LogFloat'可能不是處理下溢的最佳方法。 – jfs 2012-03-10 05:22:41

+0

DSP教科書和教程網站中的大量FFT提供了非常簡單的FFT源代碼示例,通常大約有1或2頁代碼。 (在我的DSP網站上有幾個FFT只有30到40行BASIC) – hotpaw2 2012-03-10 05:50:38

回答

1

我承認我並沒有完全跟上你的問題的數學。然而,這聽起來像你真正想知道的是如何處理極小和大(絕對值)的數字,而不會造成下溢和溢出。除非我誤解了你,否則我認爲這與我使用貨幣單位的問題類似,而不是因四捨五入而損失數十億美元交易的便士。如果是這樣,我的解決方案是Python的內置十進制數學模塊。該文檔適用於Python 2Python 3。簡短版本是十進制數學是一種任意精度的浮點和定點類型。 Python模塊符合IBM/IEEE的十進制數學標準。在Python 3.3中(目前使用的是alpha版本,但我一直在使用它,完全沒有問題),該模塊已被C語言重寫,速度提高了100倍(在我的快速測試中)。

+0

你是對的,我試圖避免下溢(我正在研究密度來計算概率密度函數)。我肯定會嘗試任意精確的python庫 - 直到現在,我一直對開銷有些不確定,但如果它能做到這一點,它將會非常酷。那麼我只需要一個FFT編碼來使用這些值(而不是浮點或numpy的64位浮點數)。 – user 2012-05-31 02:21:49

+0

'Decimal'對象比機器級別的float(這是Python的'float')要慢。如果你對速度敏感,並且你停留在Python 2上,我會說找到一個不同的解決方案。如果你使用的是Python 3或者可以升級到Python 3,那麼請升級到最新的Python 3.3版本,其中'Decimal'是用C語言編寫的(我的應用程序對速度很敏感,我對3.3的性能表示滿意)。關於重做FFT - 不應該是必需的。 「小數」對象是「浮動」的替代品。 Yay鴨打字! – wkschwartz 2012-05-31 02:36:27

+0

我知道你的意思是鴨子打字。出於某種原因,我認爲大多數fft(例如numpy fft,我認爲)將參數轉換爲指定類型的數組以執行更快的計算。你知道你是否可以使用numpy fft進行鴨型嗎?如果是這樣,那就省了很多麻煩! – user 2012-05-31 02:45:11

0

您可以通過大量的規模的時域樣本,以避免下溢,然後,如果

˚F(F(T))= X(j * W)

然後

˚F(SF(S * T))< - >X(W/S)

現在使用卷積定理可以解決如何擴展您的最終結果刪除比例因子的影響。