module Math.FFT.Base (
module Math.FFT.Base,
module Math.FFT.FFI,
) where
import Math.FFT.FFI
import qualified Foreign.C.Types as C
import Foreign.C.String (withCString, peekCString)
import Foreign.Marshal.Array (copyArray, withArray, withArrayLen)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (Storable)
import Foreign.Storable.Complex ()
import System.IO.Unsafe (unsafePerformIO)
import Control.Arrow (second)
import Control.Monad (when)
import Control.Applicative ((<$>))
import Control.Exception (assert, finally)
import Control.Concurrent.MVar (MVar, newMVar, withMVar)
import Data.Array.CArray
(CArray, withCArray, unsafeForeignPtrToCArray,
Ix, Shapable, shape, rank, size, rangeSize)
import Data.Array.CArray.Base (mallocForeignPtrArrayAligned, mapCArrayInPlace)
import Data.Ix.Shapable (shapeToStride, sBounds)
import Data.Complex (Complex)
import Data.Bits (Bits, complement, (.&.), (.|.))
import Data.List (nub, (\\))
import Data.Typeable ()
class (Storable a, RealFloat a) => FFTWReal a where
plan_guru_dft :: C.CInt -> Ptr IODim -> C.CInt -> Ptr IODim -> Ptr (Complex a)
-> Ptr (Complex a) -> FFTWSign -> FFTWFlag -> IO Plan
plan_guru_dft_r2c :: C.CInt -> Ptr IODim -> C.CInt -> Ptr IODim -> Ptr a
-> Ptr (Complex a) -> FFTWFlag -> IO Plan
plan_guru_dft_c2r :: C.CInt -> Ptr IODim -> C.CInt -> Ptr IODim -> Ptr (Complex a)
-> Ptr a -> FFTWFlag -> IO Plan
plan_guru_r2r :: C.CInt -> Ptr IODim -> C.CInt -> Ptr IODim -> Ptr a
-> Ptr a -> Ptr FFTWKind -> FFTWFlag -> IO Plan
instance FFTWReal Float where
plan_guru_dft = cf_plan_guru_dft
plan_guru_dft_r2c = cf_plan_guru_dft_r2c
plan_guru_dft_c2r = cf_plan_guru_dft_c2r
plan_guru_r2r = cf_plan_guru_r2r
instance FFTWReal Double where
plan_guru_dft = c_plan_guru_dft
plan_guru_dft_r2c = c_plan_guru_dft_r2c
plan_guru_dft_c2r = c_plan_guru_dft_c2r
plan_guru_r2r = c_plan_guru_r2r
lock :: MVar ()
lock = unsafePerformIO $ newMVar ()
withLock :: IO a -> IO a
withLock = withMVar lock . const
newtype Flag = Flag { unFlag :: FFTWFlag }
deriving (Eq, Show, Num, Bits)
nullFlag :: Flag
nullFlag = Flag 0
destroyInput :: Flag
destroyInput = Flag c_destroy_input
preserveInput :: Flag
preserveInput = Flag c_preserve_input
unaligned :: Flag
unaligned = Flag c_unaligned
conserveMemory :: Flag
conserveMemory = Flag c_conserve_memory
estimate :: Flag
estimate = Flag c_estimate
measure :: Flag
measure = Flag c_measure
patient :: Flag
patient = Flag c_patient
exhaustive :: Flag
exhaustive = Flag c_exhaustive
data Sign = DFTForward | DFTBackward
deriving (Eq,Show)
unSign :: Sign -> FFTWSign
unSign DFTForward = c_forward
unSign DFTBackward = c_backward
data Kind = R2HC | HC2R
| DHT
| REDFT00 | REDFT10 | REDFT01 | REDFT11
| RODFT00 | RODFT01 | RODFT10 | RODFT11
deriving (Eq,Show)
unKind :: Kind -> FFTWKind
unKind k = case k of
R2HC -> c_r2hc
HC2R -> c_hc2r
DHT -> c_dht
REDFT00 -> c_redft00
REDFT10 -> c_redft10
REDFT01 -> c_redft01
REDFT11 -> c_redft11
RODFT00 -> c_rodft00
RODFT01 -> c_rodft01
RODFT10 -> c_rodft10
RODFT11 -> c_rodft11
type TSpec = ([IODim],[IODim])
data DFT = CC | RC | CR | CRO | RR
deriving (Eq, Show)
check :: Plan -> IO ()
check p = when (p == nullPtr) . ioError $ userError "invalid plan"
execute :: Plan -> IO ()
execute p = check p >> c_execute p
unsafeNormalize :: (Ix i, Shapable i, Fractional e, Storable e)
=> [Int] -> CArray i e -> CArray i e
unsafeNormalize tdims a = mapCArrayInPlace (* s) a
where s = 1 / fromIntegral (product $ map (shape a !!) tdims)
dftG :: (FFTWReal r, Ix i, Shapable i) => Sign -> Flag -> [Int] -> CArray i (Complex r) -> CArray i (Complex r)
dftG s f tdims ain = case s of
DFTForward -> dftGU s f tdims ain
DFTBackward -> unsafeNormalize tdims (dftGU s f tdims ain)
dftCRG :: (FFTWReal r, Ix i, Shapable i) => Flag -> [Int] -> CArray i (Complex r) -> CArray i r
dftCRG f tdims ain = unsafeNormalize tdims (dftCRGU f tdims ain)
dftCROG :: (FFTWReal r, Ix i, Shapable i) => Flag -> [Int] -> CArray i (Complex r) -> CArray i r
dftCROG f tdims ain = unsafeNormalize tdims (dftCROGU f tdims ain)
dftN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i (Complex r) -> CArray i (Complex r)
dftN = dftG DFTForward estimate
idftN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i (Complex r) -> CArray i (Complex r)
idftN = dftG DFTBackward estimate
dftRCN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i (Complex r)
dftRCN = dftRCG estimate
dftCRN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i (Complex r) -> CArray i r
dftCRN = dftCRG estimate
dftCRON :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i (Complex r) -> CArray i r
dftCRON = dftCROG estimate
fzr :: b -> [a] -> [(a,b)]
fzr = flip zip . repeat
drr :: (FFTWReal r, Ix i, Shapable i) => Kind -> [Int] -> CArray i r -> CArray i r
drr = (dftRRN .) . fzr
dftRRN :: (FFTWReal r, Ix i, Shapable i) => [(Int,Kind)] -> CArray i r -> CArray i r
dftRRN = dftRRG estimate
dftRHN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dftRHN = drr R2HC
dftHRN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dftHRN = drr HC2R
dhtN :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dhtN = drr DHT
dct1N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dct1N = drr REDFT00
dct2N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dct2N = drr REDFT10
dct3N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dct3N = drr REDFT01
dct4N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dct4N = drr REDFT11
dst1N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dst1N = drr RODFT00
dst2N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dst2N = drr RODFT10
dst3N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dst3N = drr RODFT01
dst4N :: (FFTWReal r, Ix i, Shapable i) => [Int] -> CArray i r -> CArray i r
dst4N = drr RODFT11
dft :: (FFTWReal r, Ix i, Shapable i) => CArray i (Complex r) -> CArray i (Complex r)
dft = dftN [0]
idft :: (FFTWReal r, Ix i, Shapable i) => CArray i (Complex r) -> CArray i (Complex r)
idft = idftN [0]
dftRC :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i (Complex r)
dftRC = dftRCN [0]
dftCR :: (FFTWReal r, Ix i, Shapable i) => CArray i (Complex r) -> CArray i r
dftCR = dftCRN [0]
dftCRO :: (FFTWReal r, Ix i, Shapable i) => CArray i (Complex r) -> CArray i r
dftCRO = dftCRON [0]
dftRH :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dftRH = dftRHN [0]
dftHR :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dftHR = dftHRN [0]
dht :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dht = dhtN [0]
dct1 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dct1 = dct1N [0]
dct2 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dct2 = dct2N [0]
dct3 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dct3 = dct3N [0]
dct4 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dct4 = dct4N [0]
dst1 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dst1 = dst1N [0]
dst2 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dst2 = dst2N [0]
dst3 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dst3 = dst3N [0]
dst4 :: (FFTWReal r, Ix i, Shapable i) => CArray i r -> CArray i r
dst4 = dst4N [0]
infix 7 `has`
has :: Flag -> Flag -> Bool
a `has` b = a .&. b == b
transformCArray :: (Ix i, Storable a, Storable b)
=> Flag -> CArray i a -> (i,i) -> (FFTWFlag -> Ptr a -> Ptr b -> IO Plan) -> CArray i b
transformCArray f a lu planner = if f `has` estimate
&& not (any (f `has`) [patient, exhaustive])
then go else transformCArray' f a lu planner
where go = unsafePerformIO $ do
ofp <- mallocForeignPtrArrayAligned (rangeSize lu)
withCArray a $ \ip ->
withForeignPtr ofp $ \op -> do
p <- withLock $ planner (unFlag f) ip op
execute p
unsafeForeignPtrToCArray ofp lu
transformCArray' :: (Ix i, Storable a, Storable b)
=> Flag -> CArray i a -> (i,i) -> (FFTWFlag -> Ptr a -> Ptr b -> IO Plan) -> CArray i b
transformCArray' f a lu planner = unsafePerformIO $ do
ofp <- mallocForeignPtrArrayAligned (rangeSize lu)
wfp <- mallocForeignPtrArrayAligned sz
withCArray a $ \ip ->
withForeignPtr ofp $ \op ->
withForeignPtr wfp $ \wp -> do
p <- withLock $ planner (unFlag f') wp op
copyArray wp ip sz
execute p
unsafeForeignPtrToCArray ofp lu
where sz = size a
f' = f .&. complement preserveInput .|. destroyInput
dftShape :: (Ix i, Shapable i, Storable e)
=> DFT -> [Int] -> CArray i e -> ((i,i),TSpec)
dftShape t tdims a = assert valid (oBounds,tspec)
where shp = shape a
rnk = rank a
strides = shapeToStride shp
valid = not (null tdims) && 0 <= minimum tdims
&& maximum tdims < rnk && nub tdims == tdims
tspec = (map (d !!) tdims, map (d !!) ([0 .. rnk 1] \\ tdims))
where d = zipWith3 IODim lShape strides oStrides
oShape = adjust f ldim shp
where f = case t of
RC -> (\n -> n `div` 2 + 1)
CR -> (\n -> (n 1) * 2)
CRO -> (\n -> (n 1) * 2 + 1)
_ -> id
lShape = adjust f ldim shp
where f = case t of
CR -> (\n -> (n 1) * 2)
CRO -> (\n -> (n 1) * 2 + 1)
_ -> id
oBounds = sBounds oShape
oStrides = shapeToStride oShape
ldim = last tdims
withTSpec :: TSpec -> (C.CInt -> Ptr IODim -> C.CInt -> Ptr IODim -> IO a) -> IO a
withTSpec (dims,dims') f = withArrayLen dims $ \r ds ->
withArrayLen dims' $ \hr hds ->
f (fromIntegral r) ds (fromIntegral hr) hds
adjust :: (a -> a) -> Int -> [a] -> [a]
adjust f i = uncurry (++) . second (\(x:xs) -> f x : xs) . splitAt i
dftGU :: (FFTWReal r, Ix i, Shapable i) => Sign -> Flag -> [Int] -> CArray i (Complex r) -> CArray i (Complex r)
dftGU s f tdims ain = transformCArray f ain bds go
where go f' ip op = withTSpec tspec $ \r ds hr hds ->
plan_guru_dft r ds hr hds ip op (unSign s) f'
(bds,tspec) = dftShape CC tdims ain
dftRCG :: (FFTWReal r, Ix i, Shapable i) => Flag -> [Int] -> CArray i r -> CArray i (Complex r)
dftRCG f tdims ain = transformCArray f ain bds go
where go f' ip op = withTSpec tspec $ \r ds hr hds ->
plan_guru_dft_r2c r ds hr hds ip op f'
(bds,tspec) = dftShape RC tdims ain
dftCRG_ :: (FFTWReal r, Ix i, Shapable i) => Bool -> Flag -> [Int] -> CArray i (Complex r) -> CArray i r
dftCRG_ isOdd f tdims ain = tCArr f ain bds go
where go f' ip op = withTSpec tspec $ \r ds hr hds ->
plan_guru_dft_c2r r ds hr hds ip op f'
(bds,tspec) = dftShape (if isOdd then CRO else CR) tdims ain
tCArr = if length tdims == 1 && f `has` preserveInput
then transformCArray
else transformCArray'
dftCRGU :: (FFTWReal r, Ix i, Shapable i) => Flag -> [Int] -> CArray i (Complex r) -> CArray i r
dftCRGU = dftCRG_ False
dftCROGU :: (FFTWReal r, Ix i, Shapable i) => Flag -> [Int] -> CArray i (Complex r) -> CArray i r
dftCROGU = dftCRG_ True
dftRRG :: (FFTWReal r, Ix i, Shapable i) => Flag -> [(Int,Kind)] -> CArray i r -> CArray i r
dftRRG f tk ain = tCArr f ain bds go
where go f' ip op = withTSpec tspec $ \r ds hr hds ->
withArray (map unKind ks) $ \pk ->
plan_guru_r2r r ds hr hds ip op pk f'
(bds,tspec) = dftShape RR tdims ain
(tdims,ks) = unzip tk
tCArr = if any (== HC2R) ks && not (f `has` preserveInput)
then transformCArray'
else transformCArray
exportWisdomString :: IO String
exportWisdomString = do
pc <- c_export_wisdom_string
peekCString pc `finally` c_free pc
importWisdomString :: String -> IO Bool
importWisdomString str =
(==1) <$> withCString str c_import_wisdom_string
importWisdomSystem :: IO Bool
importWisdomSystem = (==1) <$> c_import_wisdom_system