passage-0.1: Language/Passage/SimulatorConf.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Language.Passage.SimulatorConf where
import MonadLib
import qualified Data.IntMap as IM
import qualified Data.IntSet as IS
import Language.Passage.Term
import Language.Passage.AST
import Language.Passage.Utils
import Language.Passage.Graph
-- The "simulator" infrastructure
data SimState = SimState
{ cfgSampleNum :: Int
, cfgItsPerSample :: Int
, cfgWarmup :: Int
, cfgMersenne :: Bool
, cfgProfile :: Bool
, cfgMonitor :: [(String, Term NodeIdx)]
, cfgObserve :: IM.IntMap Double -- NodeIdx |-> observed values
, cfgInitialize :: IM.IntMap Double
, cfgRandomSeed :: [Int] -- reversed list of seeds for threads
, cfgNetwork :: Maybe BayesianGraph
, cfgThreadNum :: Int
, cfgSpecialSlicers :: Bool
, cfgSplitFiles :: Bool
}
initSimState :: SimState
initSimState = SimState { cfgSampleNum = 100000
, cfgItsPerSample = 10
, cfgWarmup = 1000
, cfgMersenne = False
, cfgProfile = False
, cfgMonitor = []
, cfgObserve = IM.empty
, cfgInitialize = IM.empty
, cfgRandomSeed = []
, cfgNetwork = Nothing
, cfgThreadNum = 2
, cfgSpecialSlicers = False
, cfgSplitFiles = False
}
newtype BayesianSimulator a = B (StateT SimState Id a)
deriving (Functor, Monad)
upd :: (SimState -> SimState) -> BayesianSimulator ()
upd f = B $ sets_ f
getField :: (SimState -> a) -> BayesianSimulator a
getField f = B (f `fmap` get)
runSim :: BayesianSimulator a -> SimState
runSim (B m) = snd $ runId $ runStateT initSimState m
setWarmupCount :: Int -> BayesianSimulator ()
setWarmupCount i = upd $ \s -> s { cfgWarmup = i }
setSampleCount :: Int -> BayesianSimulator ()
setSampleCount i = upd $ \s -> s { cfgSampleNum = i }
setIterationsPerSample :: Int -> BayesianSimulator ()
setIterationsPerSample i = upd $ \s -> s { cfgItsPerSample = i }
-- | Set the random seed for a thread. This function may be calledd
-- multiple times to set the seeds for multiple threads.
-- The seeds are used in order: first call is for thread 0, next for thread 1,
-- etc.
setRandomSeed :: Int -> BayesianSimulator ()
setRandomSeed d = upd $ \s -> s { cfgRandomSeed = d : cfgRandomSeed s }
setThreadNum :: Int -> BayesianSimulator ()
setThreadNum n = upd $ \s -> s { cfgThreadNum = n }
useMersenneTwister :: Bool -> BayesianSimulator ()
useMersenneTwister b = upd (\s -> s { cfgMersenne = b })
-- | When using a specialized slizer, we generate a custom slicer
-- for each stochastic variable. The benefit of this is that, in principle,
-- this may result in more efficient code, at the cost of longer compilation
-- time, and larger binary. The alternative is to use a generic slicing
-- function which is parameterized by the log-likelihood function for
-- a variable.
useSpecialSlicers :: Bool -> BayesianSimulator ()
useSpecialSlicers b = upd (\s -> s { cfgSpecialSlicers = b })
-- | Generate a separate file for each stochastic variable.
-- The benefit of this flag is that it makes it possible to compile
-- multiple files in parallel. The drawback is that some optimizations
-- may be lost because the files are compiled separately. Also, there
-- is some overhead for processing multiple files.
splitFiles :: Bool -> BayesianSimulator ()
splitFiles b = upd (\s -> s { cfgSplitFiles = b })
enableProfiling :: Bool -> BayesianSimulator ()
enableProfiling b = upd $ \s -> s { cfgProfile = b }
model :: BayesianNetwork a -> BayesianSimulator a
model t = do upd $ \s -> s { cfgNetwork = Just nw }
return a
where (a, nw) = buildBayesianGraph t
observe :: Term NodeIdx -> Double -> BayesianSimulator ()
observe t d =
case splitArray t of
-- (TVar idx, its) | Just is <- mapM isConst its
(TVar idx, []) ->
do obs <- getField cfgObserve
case IM.insertLookupWithKey unused idx d obs of
(Nothing, m1) -> upd (\s -> s { cfgObserve = m1 })
(Just _, _) ->
error $ "observe: Model error. Node was observed before: "
++ show t
_ -> error $ "observe: Model error. Only nodes can be observed, received: "
++ show t
where unused = error "BUG: observe--not used"
initialize :: Term NodeIdx -> Double -> BayesianSimulator ()
initialize t d =
case splitArray t of
-- (TVar idx, its) | Just is <- mapM isConst its
(TVar idx, []) ->
do init <- getField cfgInitialize
case IM.insertLookupWithKey unused idx d init of
(Nothing, m1) -> upd (\s -> s { cfgInitialize = m1 })
(Just _, _) ->
error $ "initialize: Model error. Node was initialized before: "
++ show t
_ -> error $ "initialize: Model error. Only nodes can be initialized, received: "
++ show t
where unused = error "BUG: initialize--not used"
monitor :: String -> Expr -> BayesianSimulator ()
monitor nm e = upd (\s -> s { cfgMonitor = (nm, e) : cfgMonitor s })