module Numeric.FFT.Vector.Base(
Transform(..),
planOfType,
PlanType(..),
plan,
run,
Plan(),
planInputSize,
planOutputSize,
execute,
executeM,
withPlanner,
CFlags,
CPlan,
Scalable(..),
modifyInput,
modifyOutput,
constMultOutput,
multC,
unsafeModify,
) where
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Storable.Mutable as MS
import Data.Vector.Generic as V hiding (forM_)
import Data.Vector.Generic.Mutable as M hiding (unsafeModify)
import Data.List as L
import Control.Concurrent.MVar
import Control.Monad.Primitive (RealWorld,PrimMonad(..), PrimBase,
unsafePrimToPrim, unsafePrimToIO)
import Control.Monad(forM_)
import Foreign (Storable(..), Ptr, FunPtr,
ForeignPtr, withForeignPtr, newForeignPtr)
import Foreign.C (CInt(..), CUInt)
import Data.Bits ( (.|.) )
import Data.Complex(Complex(..))
import Foreign.Storable.Complex()
import System.IO.Unsafe (unsafePerformIO)
data PlanType = Estimate | Measure | Patient | Exhaustive
data Preservation = PreserveInput | DestroyInput
type CFlags = CUInt
planInitFlags :: PlanType -> Preservation -> CFlags
planInitFlags pt pr = planTypeInt .|. preservationInt
where
planTypeInt = case pt of
Estimate -> 64
Measure -> 0
Patient -> 32
Exhaustive -> 8
preservationInt = case pr of
PreserveInput -> 16
DestroyInput -> 1
newtype CPlan = CPlan {unCPlan :: ForeignPtr CPlan}
withPlan :: CPlan -> (Ptr CPlan -> IO a) -> IO a
withPlan = withForeignPtr . unCPlan
foreign import ccall unsafe fftw_execute :: Ptr CPlan -> IO ()
foreign import ccall "&" fftw_destroy_plan :: FunPtr (Ptr CPlan -> IO ())
newPlan :: Ptr CPlan -> IO CPlan
newPlan = fmap CPlan . newForeignPtr fftw_destroy_plan
data Plan a b = Plan {
planInput :: !(VS.MVector RealWorld a),
planOutput :: !(VS.MVector RealWorld b),
planExecute :: IO ()
}
planInputSize :: Storable a => Plan a b -> Int
planInputSize = MS.length . planInput
planOutputSize :: Storable b => Plan a b -> Int
planOutputSize = MS.length . planOutput
execute :: (Vector v a, Vector v b, Storable a, Storable b)
=> Plan a b -> v a -> v b
execute Plan{..} = \v ->
if n /= V.length v
then error $ "execute: size mismatch; expected " L.++ show n
L.++ ", got " L.++ show (V.length v)
else unsafePerformIO $ do
forM_ [0..n1] $ \k -> M.unsafeWrite planInput k
$ V.unsafeIndex v k
planExecute
v' <- unsafeNew m
forM_ [0..m1] $ \k -> M.unsafeRead planOutput k
>>= M.unsafeWrite v' k
V.unsafeFreeze v'
where
n = MS.length planInput
m = MS.length planOutput
executeM :: forall m v a b .
(PrimBase m, MVector v a, MVector v b, Storable a, Storable b)
=> Plan a b
-> v (PrimState m) a
-> v (PrimState m) b
-> m ()
executeM Plan{..} = \vIn vOut ->
if n /= M.length vIn || m /= M.length vOut
then error $ "executeM: size mismatch; expected " L.++ show (n,m)
L.++ ", got " L.++ show (M.length vIn, M.length vOut)
else unsafePrimToPrim $ act vIn vOut
where
n = MS.length planInput
m = MS.length planOutput
act :: v (PrimState m) a -> v (PrimState m) b -> IO ()
act vIn vOut = do
forM_ [0..n1] $ \k -> unsafePrimToIO (M.unsafeRead vIn k :: m a)
>>= M.unsafeWrite planInput k
unsafePrimToPrim planExecute
forM_ [0..n1] $ \k -> M.unsafeRead planOutput k
>>= unsafePrimToIO . (M.unsafeWrite vOut k
:: b -> m ())
foreign import ccall unsafe fftw_malloc :: CInt -> IO (Ptr a)
foreign import ccall "&" fftw_free :: FunPtr (Ptr a -> IO ())
newFFTVector :: forall a . Storable a => Int -> IO (MS.MVector RealWorld a)
newFFTVector n = do
p <- fftw_malloc $ toEnum $ n * sizeOf (undefined :: a)
fp <- newForeignPtr fftw_free p
return $ MS.MVector n fp
data Transform a b = Transform {
inputSize :: Int -> Int,
outputSize :: Int -> Int,
creationSizeFromInput :: Int -> Int,
makePlan :: CInt -> Ptr a -> Ptr b -> CFlags -> IO (Ptr CPlan),
normalization :: Int -> Plan a b -> Plan a b
}
planOfType :: (Storable a, Storable b) => PlanType
-> Transform a b -> Int -> Plan a b
planOfType ptype Transform{..} n
| m_in <= 0 || m_out <= 0 = error "Can't (yet) plan for empty arrays!"
| otherwise = unsafePerformIO $ do
planInput <- newFFTVector m_in
planOutput <- newFFTVector m_out
MS.unsafeWith planInput $ \inP -> MS.unsafeWith planOutput $ \outP -> do
pPlan <- makePlan (toEnum n) inP outP $ planInitFlags ptype DestroyInput
cPlan <- newPlan pPlan
let planExecute = MS.unsafeWith planInput $ \_ ->
MS.unsafeWith planOutput $ \_ ->
withPlan cPlan fftw_execute
return $ normalization n $ Plan {..}
where
m_in = inputSize n
m_out = outputSize n
plan :: (Storable a, Storable b) => Transform a b -> Int -> Plan a b
plan = planOfType Estimate
run :: (Vector v a, Vector v b, Storable a, Storable b)
=> Transform a b -> v a -> v b
run p = \v -> execute
(planOfType Estimate p $ creationSizeFromInput p $ V.length v)
v
class Scalable a where
scaleByD :: Double -> a -> a
instance Scalable Double where
scaleByD = (*)
instance Scalable (Complex Double) where
scaleByD s (x:+y) = s*x :+ s*y
modifyInput :: (MS.MVector RealWorld a -> IO ()) -> Plan a b -> Plan a b
modifyInput f p@Plan{..} = p {planExecute = f planInput >> planExecute}
modifyOutput :: (MS.MVector RealWorld b -> IO ()) -> Plan a b -> Plan a b
modifyOutput f p@Plan{..} = p {planExecute = planExecute >> f planOutput}
constMultOutput :: (Storable b, Scalable b) => Double -> Plan a b -> Plan a b
constMultOutput !s = modifyOutput (multC s)
multC :: (Storable a, Scalable a) => Double -> MS.MVector RealWorld a -> IO ()
multC !s v = forM_ [0..n1] $ \k -> unsafeModify v k (scaleByD s)
where !n = MS.length v
unsafeModify :: (Storable a)
=> MS.MVector RealWorld a -> Int -> (a -> a) -> IO ()
unsafeModify v k f = MS.unsafeRead v k >>= MS.unsafeWrite v k . f
plannerLock :: MVar ()
plannerLock = unsafePerformIO $ newMVar ()
withPlanner :: IO a -> IO a
withPlanner action = do
takeMVar plannerLock
res <- action
putMVar plannerLock ()
return res