packages feed

goal-simulation-0.1: Goal/Simulation/Mealy.hs

{-| A general purpose library for simulating differential processes, of a deterministic or
    stochastic nature. -}

module Goal.Simulation.Mealy
    ( -- * Exports
      module Data.Machine
    -- * Accumulation
    , accumulateFunction
    , accumulateFunction'
    , accumulateMealy
    , accumulateMealy'
    , accumulateRandomFunction
    , accumulateRandomFunction'
    , accumulateRandomFunction0
      -- * Execution
    , stream
    , streamM
    , streamM_
    ) where

--- Imports ---


-- Goal --

import Goal.Probability

-- Reexporting --

import Data.Machine

import qualified Control.Monad.ST as ST


--- Mealys ---


accumulateFunction :: (a -> acc -> (b,acc)) -> acc -> Mealy a b
-- | accumulateFunction takes a function from a value and an accumulator (e.g. just a sum
-- value or an evolving set of parameters for some model) to a value and an accumulator.
-- The accumulator is then looped back into the function, returning a Mealy from a to
-- b, which updates the accumulator every time step.
accumulateFunction f acc = Mealy $ \a ->
    let (b,acc') = f a acc
     in (b,accumulateFunction f acc')

accumulateFunction' :: (a -> acc -> (b,acc)) -> acc -> Mealy a (b,acc)
-- | accumulateFunction' acts like accumulateFunction but the Mealy automata will
-- continue to return the accumulator as it generates it.
accumulateFunction' f =
    accumulateFunction f'
    where f' a acc =
              let (b,acc') = f a acc
               in ((b,acc'),acc')

accumulateRandomFunction :: (a -> acc -> forall s . RandST s (b,acc)) -> acc -> RandST s' (Mealy a b)
-- | accumulateRandomFunction is analogous to accumulateFunction, but takes as an
-- argument a function which returns a random variable.
accumulateRandomFunction rf acc0 = do
    rf' <-  accumulateRandomFunction0 (uncurry rf)
    return $ accumulateMealy acc0 rf'

accumulateRandomFunction' :: (a -> acc -> forall s . RandST s (b,acc)) -> acc -> RandST s' (Mealy a (b,acc))
-- | accumulateRandomFunction' is analogous to accumulateFunction', but takes as an
-- argument a function which returns a random variable.
accumulateRandomFunction' rf acc0 = do
    rf' <- accumulateRandomFunction0 (uncurry rf)
    return $ accumulateMealy' acc0 rf'

accumulateRandomFunction0 :: (a -> forall s . RandST s b) -> RandST s' (Mealy a b)
-- | accumulateRandomFunction' Mealifies stateless random functions.
accumulateRandomFunction0 rf = do
    sd <- seed
    return $ accumulateFunction f sd
    where f a sd = ST.runST $ do
              gn <- restore sd
              b <- runRand (rf a) gn
              sd' <- save gn
              return (b,sd')

accumulateMealy :: acc -> Mealy (a,acc) (b,acc) -> Mealy a b
-- | accumulateMealy takes a Mealy with an accumulating parameter and loops it.
accumulateMealy acc0 mly0 =
    accumulateFunction f (acc0,mly0)
    where f a (acc,Mealy cf) =
              let ((b,acc'),mly') = cf (a,acc)
               in (b,(acc',mly'))

accumulateMealy' :: acc -> Mealy (a,acc) (b,acc) -> Mealy a (b,acc)
-- | accumulateMealy except with a returned accumulator.
accumulateMealy' acc0 mly0 =
    accumulateFunction f (acc0,mly0)
    where f a (acc,Mealy cf) =
              let ((b,acc'),mly') = cf (a,acc)
               in ((b,acc'),(acc',mly'))

--- Execution ---

{-
parallelizeMealys :: [Mealy a b] -> Mealy [a] [b]
{-| Turns a list of circuits into a circuit over lists, bound by the power of parMap rseq. -}
parallelizeMealys crcs = Mealy $ \as ->
    let (bs,crcs') = unzip $ parZip crcs as
    in  (bs, parallelizeMealys crcs')
    where parZip [] [] = []
          parZip (crc:crcs) (a:as) =
              let (b,crc') = runMealy crc a
              in  b `par` (b,crc') : parZip crcs as
          parZip _ _ = error "Parallel circuit does not match size of input"
          -}

stream :: Mealy a b -> [a] -> [b]
stream mly as = run . supply as . auto $ mly

streamM :: Monad m => Mealy a b -> (b -> m c) -> [a] -> m [c]
streamM mly fM as = runT . supply as $ auto mly ~> autoM fM

streamM_ :: Monad m => Mealy a b -> (b -> m c) -> [a] -> m ()
streamM_ mly fM as = runT_ . supply as $ auto mly ~> autoM fM