HaskellでFFT.Array編

Data.Arrayを使って、HaskellFFT.
64KのサンプルでList版から101.1sec->0.73secに短縮。
大量のサンプル数を突っ込むとギブアップする。

module Numerical.FFT
(
 fft1d
) where

import qualified Data.Vector as V 
import Data.Array
import Data.Array.IArray (amap)
import Data.Maybe
import Data.Complex
import Data.Bits

{- 時間間引きのstockham FFT -}

butterflyCalc :: (RealFloat a, Show a) => V.Vector (Complex a) -> Array (Int,Int) (Complex a) -> Array (Int,Int) (Complex a)
butterflyCalc twiddlesTable srcArray = 
  let (n',m') = snd $ bounds srcArray
      n = div (n'+1) 2
      m = (m'+1) * 2
      l = m'+1
  in array ((0,0),(n-1,m-1)) [((i,j), if (j < l)
                                      then
                                        let w = ((V.!) twiddlesTable (n*j))
                                        in srcArray!(i,j)+(srcArray!(i+n,j))*w
                                      else 
                                        let w = ((V.!) twiddlesTable (n*(j-l)))
                                        in srcArray!(i,j-l)-(srcArray!(i+n,j-l))*w) |i<-[0..n-1],j<-[0..m-1]]
 
butterflyCalcIter :: (RealFloat a,Num a,Show a) => V.Vector (Complex a) -> Array (Int,Int) (Complex a) -> Array (Int,Int) (Complex a)
butterflyCalcIter twiddlesTable currentArray 
  | ((fst $ snd $ bounds currentArray) == 0) = currentArray
  | otherwise = butterflyCalcIter twiddlesTable $ butterflyCalc twiddlesTable currentArray
    
fft1dCore :: (Show a, RealFloat a) => Array (Int,Int) (Complex a) -> Bool -> Array (Int,Int) (Complex a)
fft1dCore inDatas bInverse = 
  let numOfSample = rangeSize $ bounds inDatas
      signOfRootOfUnity = if bInverse then 1 else -1 
      twiddlesTable = V.generate numOfSample (\i -> cis (signOfRootOfUnity*2*pi*(fromIntegral i)/(fromIntegral numOfSample))) --回転子のωNのテーブルを作ります。
      outDatas = butterflyCalcIter twiddlesTable inDatas
  in if bInverse then amap (\z -> z/(fromIntegral numOfSample)) outDatas else outDatas 

fft1d :: (Show a, RealFloat a) => Array (Int,Int) (Complex a) -> Bool -> Maybe (Array (Int,Int) (Complex a))
fft1d inDatas bInverse = 
  let n = rangeSize $ bounds inDatas
  in if (n .&. (n-1) == 0)
     then Just (fft1dCore inDatas bInverse)
     else Nothing -- Dataの長さが2のべき乗でありません