monad-bayes 0.1.0.0 → 0.1.1.0
raw patch · 37 files changed
+1381/−1229 lines, 37 filesdep ~basesetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Control.Monad.Bayes.Class: discrete :: (DiscreteDistr d, MonadSample m) => d -> m Int
Files
- CHANGELOG.md +8/−0
- LICENSE +0/−22
- LICENSE.md +22/−0
- Setup.hs +1/−0
- benchmark/SSM.hs +6/−12
- benchmark/Single.hs +44/−43
- benchmark/Speed.hs +117/−113
- models/Dice.hs +6/−8
- models/HMM.hs +46/−29
- models/LDA.hs +24/−26
- models/LogReg.hs +4/−4
- models/NonlinearSSM.hs +22/−18
- models/Sprinkler.hs +5/−5
- monad-bayes.cabal +172/−130
- src/Control/Monad/Bayes/Class.hs +147/−122
- src/Control/Monad/Bayes/Enumerator.hs +47/−45
- src/Control/Monad/Bayes/Free.hs +41/−45
- src/Control/Monad/Bayes/Helpers.hs +50/−40
- src/Control/Monad/Bayes/Inference/PMMH.hs +33/−30
- src/Control/Monad/Bayes/Inference/RMSMC.hs +62/−48
- src/Control/Monad/Bayes/Inference/SMC.hs +67/−47
- src/Control/Monad/Bayes/Inference/SMC2.hs +38/−32
- src/Control/Monad/Bayes/Population.hs +88/−70
- src/Control/Monad/Bayes/Sampler.hs +23/−28
- src/Control/Monad/Bayes/Sequential.hs +31/−27
- src/Control/Monad/Bayes/Traced.hs +12/−14
- src/Control/Monad/Bayes/Traced/Basic.hs +42/−46
- src/Control/Monad/Bayes/Traced/Common.hs +46/−43
- src/Control/Monad/Bayes/Traced/Dynamic.hs +32/−34
- src/Control/Monad/Bayes/Traced/Static.hs +45/−44
- src/Control/Monad/Bayes/Weighted.hs +29/−30
- test/Spec.hs +12/−13
- test/TestEnumerator.hs +8/−9
- test/TestInference.hs +9/−11
- test/TestPopulation.hs +15/−12
- test/TestSequential.hs +13/−12
- test/TestWeighted.hs +14/−17
CHANGELOG.md view
@@ -1,3 +1,11 @@+# Unreleased++- No unreleased changes so far.++# 0.1.1.0 (2020-04-08)++- New exported function: `Control.Monad.Bayes.Class` now exports `discrete`.+ # 0.1.0.0 (2020-02-17) Initial release.
− LICENSE
@@ -1,22 +0,0 @@-The MIT License (MIT)--Copyright (c) 2015-2020 Adam Scibior--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in all-copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-SOFTWARE.-
+ LICENSE.md view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2015-2020 Adam Scibior++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
benchmark/SSM.hs view
@@ -1,15 +1,13 @@ module Main where -import Control.Monad.IO.Class--import Control.Monad.Bayes.Sampler-import Control.Monad.Bayes.Weighted-import Control.Monad.Bayes.Population-import Control.Monad.Bayes.Inference.SMC-import Control.Monad.Bayes.Inference.RMSMC import Control.Monad.Bayes.Inference.PMMH as PMMH+import Control.Monad.Bayes.Inference.RMSMC+import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Inference.SMC2 as SMC2-+import Control.Monad.Bayes.Population+import Control.Monad.Bayes.Sampler+import Control.Monad.Bayes.Weighted+import Control.Monad.IO.Class import NonlinearSSM main :: IO ()@@ -17,19 +15,15 @@ let t = 5 dat <- generateData t let ys = map snd dat- liftIO $ print "SMC" smcRes <- runPopulation $ smcMultinomial t 10 (param >>= model ys) liftIO $ print $ show smcRes- liftIO $ print "RM-SMC" smcrmRes <- runPopulation $ rmsmcLocal t 10 10 (param >>= model ys) liftIO $ print $ show smcrmRes- liftIO $ print "PMMH" pmmhRes <- prior $ pmmh 2 t 3 param (model ys) liftIO $ print $ show pmmhRes- liftIO $ print "SMC2" smc2Res <- runPopulation $ smc2 t 3 2 1 param (model ys) liftIO $ print $ show smc2Res
benchmark/Single.hs view
@@ -1,44 +1,42 @@-import System.Random.MWC (createSystemRandom, GenIO)-import Data.Time-import Options.Applicative-import Data.Semigroup ((<>))- import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Sampler-import Control.Monad.Bayes.Weighted-import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Inference.RMSMC+import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Population-import Control.Monad.Bayes.Sequential+import Control.Monad.Bayes.Sampler import Control.Monad.Bayes.Traced-+import Control.Monad.Bayes.Weighted+import Data.Time import qualified HMM-import qualified LogReg import qualified LDA+import qualified LogReg+import Options.Applicative+import System.Random.MWC (createSystemRandom) data Model = LR Int | HMM Int | LDA (Int, Int)- deriving(Show, Read)+ deriving (Show, Read)+ parseModel :: String -> Maybe Model parseModel s = case s of- 'L':'R':n -> Just $ LR (read n)- 'H':'M':'M':n -> Just $ HMM (read n)- 'L':'D':'A':n -> Just $ LDA (5, read n)+ 'L' : 'R' : n -> Just $ LR (read n)+ 'H' : 'M' : 'M' : n -> Just $ HMM (read n)+ 'L' : 'D' : 'A' : n -> Just $ LDA (5, read n) _ -> Nothing -getModel :: MonadInfer m => Model -> (Int, m String)-getModel model = (size model, program model) where- size (LR n) = n- size (HMM n) = n- size (LDA (d,w)) = d*w- synthesize :: SamplerST a -> (a -> b) -> b- synthesize dataGen prog = prog (sampleSTfixed dataGen)- program (LR n) = show <$> synthesize (LogReg.syntheticData n) LogReg.logisticRegression- program (HMM n) = show <$> synthesize (HMM.syntheticData n) HMM.hmm- program (LDA (d,w)) = show <$> synthesize (LDA.syntheticData d w) LDA.lda+getModel :: MonadInfer m => Model -> (Int, m String)+getModel model = (size model, program model)+ where+ size (LR n) = n+ size (HMM n) = n+ size (LDA (d, w)) = d * w+ synthesize :: SamplerST a -> (a -> b) -> b+ synthesize dataGen prog = prog (sampleSTfixed dataGen)+ program (LR n) = show <$> synthesize (LogReg.syntheticData n) LogReg.logisticRegression+ program (HMM n) = show <$> synthesize (HMM.syntheticData n) HMM.hmm+ program (LDA (d, w)) = show <$> synthesize (LDA.syntheticData d w) LDA.lda data Alg = SMC | MH | RMSMC- deriving(Read,Show)+ deriving (Read, Show) runAlg :: Model -> Alg -> SamplerIO String runAlg model alg =@@ -46,17 +44,16 @@ SMC -> let n = 100 (k, m) = getModel model- in show <$> (runPopulation $ smcSystematic k n m)- MH ->+ in show <$> runPopulation (smcSystematic k n m)+ MH -> let t = 100 (_, m) = getModel model- in show <$> (prior $ mh t m)+ in show <$> prior (mh t m) RMSMC -> let n = 10 t = 1 (k, m) = getModel model- in show <$> (runPopulation $ rmsmcBasic k n t m)-+ in show <$> runPopulation (rmsmcBasic k n t m) infer :: Model -> Alg -> IO () infer model alg = do@@ -65,23 +62,27 @@ print x opts :: ParserInfo (Model, Alg)-opts = flip info fullDesc $ liftA2 (,) model alg where- model = option (maybeReader parseModel)- ( long "model"- <> short 'm'- <> help "Model")- alg = option auto- ( long "alg"- <> short 'a'- <> help "Inference algorithm")+opts = flip info fullDesc $ liftA2 (,) model alg+ where+ model =+ option+ (maybeReader parseModel)+ ( long "model"+ <> short 'm'+ <> help "Model"+ )+ alg =+ option+ auto+ ( long "alg"+ <> short 'a'+ <> help "Inference algorithm"+ ) main :: IO () main = do (model, alg) <- execParser opts- startTime <- getCurrentTime- infer model alg- endTime <- getCurrentTime print (diffUTCTime endTime startTime)
benchmark/Speed.hs view
@@ -1,26 +1,21 @@-import Criterion.Main-import Criterion.Types-import System.Random.MWC (createSystemRandom, GenIO)-import Control.Monad (replicateM)--import Debug.Trace--import GHC.IO.Handle-import System.Exit-import System.Process hiding (env)-+import Control.Monad (unless) import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Sampler-import Control.Monad.Bayes.Weighted-import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Inference.RMSMC+import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Population+import Control.Monad.Bayes.Sampler import Control.Monad.Bayes.Traced-+import Control.Monad.Bayes.Weighted+import Criterion.Main+import Criterion.Types+import GHC.IO.Handle -- import NonlinearSSM import qualified HMM-import qualified LogReg import qualified LDA+import qualified LogReg+import System.Exit+import System.Process hiding (env)+import System.Random.MWC (GenIO, createSystemRandom) -- | Path to the Anglican project with benchmarks. anglicanPath :: String@@ -55,56 +50,53 @@ -- | Insert data into an Anglican model. anglicanData :: LeinProc -> Model -> IO ()-anglicanData lein model = do- anglican lein ["(use 'nstools.ns)\n"]- anglican lein ["(ns+ " ++ anglicanModelName model ++ ")\n"]+anglicanData leinProc model = do+ anglican leinProc ["(use 'nstools.ns)\n"]+ anglican leinProc ["(ns+ " ++ anglicanModelName model ++ ")\n"] case model of LR dataset -> do let (xs, labels) = unzip dataset- anglican lein ["(ns-unmap *ns* 'xs)\n"]- anglican lein ["(def xs " ++ clojureShowVector xs ++ ")\n", "nil\n"]- anglican lein ["(ns-unmap *ns* 'labels)\n"]- anglican lein ["(def labels " ++ clojureBoolVector labels ++ ")\n", "nil\n"]+ anglican leinProc ["(ns-unmap *ns* 'xs)\n"]+ anglican leinProc ["(def xs " ++ clojureShowVector xs ++ ")\n", "nil\n"]+ anglican leinProc ["(ns-unmap *ns* 'labels)\n"]+ anglican leinProc ["(def labels " ++ clojureBoolVector labels ++ ")\n", "nil\n"] HMM observations -> do- anglican lein ["(ns-unmap *ns* 'observations)\n"]- anglican lein ["(def observations " ++ clojureShowVector observations ++ ")\n", "nil\n"]+ anglican leinProc ["(ns-unmap *ns* 'observations)\n"]+ anglican leinProc ["(def observations " ++ clojureShowVector observations ++ ")\n", "nil\n"] LDA docs -> do- anglican lein ["(ns-unmap *ns* 'docs)\n"]- anglican lein ["(def docs " ++ clojureVector (map clojureShowVector docs) ++ ")\n", "nil\n"]- anglican lein ["(use 'nstools.ns)\n"]- anglican lein ["(ns+ anglican.core)\n"]+ anglican leinProc ["(ns-unmap *ns* 'docs)\n"]+ anglican leinProc ["(def docs " ++ clojureVector (map clojureShowVector docs) ++ ")\n", "nil\n"]+ anglican leinProc ["(use 'nstools.ns)\n"]+ anglican leinProc ["(ns+ anglican.core)\n"] anglican :: LeinProc -> [String] -> IO () anglican (LeinProc input output _) cmds = do -- execute command- mapM (hPutStr input) cmds+ mapM_ (hPutStr input) cmds hFlush input -- wait until all samples are produced waitForAnglican output -- | Wait until an Anglican program finishes waitForAnglican :: Handle -> IO ()-waitForAnglican handle = run where- run = do- l <- hGetLine handle- -- traceIO l- -- We recognize that Anglican is finished when the repl emits a line "nil"- if l == "nil" then- return ()- else- run+waitForAnglican handle = run+ where+ run = do+ l <- hGetLine handle+ -- traceIO l+ -- We recognize that Anglican is finished when the repl emits a line "nil"+ unless (l == "nil") run -- | Start a Leiningen process in a directory that contains benchmarks. startLein :: IO LeinProc startLein = do- let setup = (shell "lein repl"){cwd = Just anglicanPath, std_in = CreatePipe, std_out = CreatePipe}+ let setup = (shell "lein repl") {cwd = Just anglicanPath, std_in = CreatePipe, std_out = CreatePipe} (Just input, Just output, _, process) <- createProcess setup -- wait until Leiningen starts producing output to make sure it's ready _ <- hWaitForInput output (-1) -- wait for output indefinitely- let lein = LeinProc input output process- anglican lein ["(use 'nstools.ns)\n"]- return lein-+ let leinProc = LeinProc input output process+ anglican leinProc ["(use 'nstools.ns)\n"]+ return leinProc -- | Path to the WebPPL project with benchmarks. webpplPath :: String@@ -123,27 +115,32 @@ data Env = Env {rng :: GenIO, lein :: LeinProc} data ProbProgSys = MonadBayes | Anglican | WebPPL- deriving(Show)+ deriving (Show) -data Model = LR [(Double,Bool)] | HMM [Double] | LDA [[String]]+data Model = LR [(Double, Bool)] | HMM [Double] | LDA [[String]]+ instance Show Model where show (LR xs) = "LR" ++ show (length xs) show (HMM xs) = "HMM" ++ show (length xs) show (LDA xs) = "LDA" ++ show (length $ head xs)+ buildModel :: MonadInfer m => Model -> m String buildModel (LR dataset) = show <$> LogReg.logisticRegression dataset buildModel (HMM dataset) = show <$> HMM.hmm dataset buildModel (LDA dataset) = show <$> LDA.lda dataset+ modelLength :: Model -> Int modelLength (LR xs) = length xs modelLength (HMM xs) = length xs modelLength (LDA xs) = sum (map length xs) data Alg = MH Int | SMC Int | RMSMC Int Int+ instance Show Alg where show (MH n) = "MH" ++ show n show (SMC n) = "SMC" ++ show n show (RMSMC n t) = "RMSMC" ++ show n ++ "-" ++ show t+ runAlg :: Model -> Alg -> SamplerIO String runAlg model (MH n) = show <$> prior (mh n (buildModel model)) runAlg model (SMC n) = show <$> runPopulation (smcSystematic (modelLength model) n (buildModel model))@@ -151,103 +148,110 @@ prepareBenchmarkable :: GenIO -> ProbProgSys -> Model -> Alg -> Benchmarkable prepareBenchmarkable g MonadBayes model alg = nfIO $ sampleIOwith (runAlg model alg) g-prepareBenchmarkable _ Anglican _ _ = error "Anglican benchmarks not available"+prepareBenchmarkable _ Anglican _ _ = error "Anglican benchmarks not available" prepareBenchmarkable _ WebPPL _ _ = error "WebPPL benchmarks not available" prepareBenchmark :: Env -> ProbProgSys -> Model -> Alg -> Benchmark prepareBenchmark e MonadBayes model alg = bench (show MonadBayes ++ sep ++ show model ++ sep ++ show alg) $- prepareBenchmarkable (rng e) MonadBayes model alg where+ prepareBenchmarkable (rng e) MonadBayes model alg+ where sep = "_"-prepareBenchmark e Anglican model alg = env prepareData (const $ bench name $ whnfIO collect) where- name = show Anglican ++ sep ++ show model ++ sep ++ show alg- sep = "_"- algString (MH n) = "-a lmh -n " ++ show n- algString (SMC n) = "-a smc -n " ++ show n- algString (RMSMC _ _) = error "Anglican does not support resample-move SMC"- prepareData = anglicanData (lein e) model- collect = do- anglican (lein e) $ ["(time (m! " ++ anglicanModelName model ++ " " ++ algString alg ++ "))\n"]-prepareBenchmark _ WebPPL model alg = bench name $ whnfIO run where- name = show WebPPL ++ sep ++ show model ++ sep ++ show alg- sep = "_"- algString (MH n) = "--alg MCMC --samples " ++ show n ++ " --rejuv 0 "- algString (SMC n) = "--alg SMC --samples " ++ show n ++ " --rejuv 0 "- algString (RMSMC n t) = "--alg SMC --samples " ++ show n ++ " --rejuv " ++ show t ++ " "- dataString (LR dataset) = let (xs, labels) = unzip dataset in "--xs='" ++ javascriptList xs ++ "' --labels='" ++ javascriptList (map (\b -> if b then 1 else 0) labels) ++ "'"- dataString (HMM obs) = "--obs='" ++ javascriptList obs ++ "'"- dataString (LDA docs) = unwords $ map (\(i,doc) -> "--doc" ++ show i ++ "='" ++ unwords doc ++ "'") (zip [1..5] docs)- run = do- let command = "node " ++ webpplModelName model ++ ".js " ++ algString alg ++ dataString model- (_, _, _, process) <- createProcess $ (shell command){cwd = Just webpplPath, std_out = NoStream, std_err = NoStream}- exitCode <- waitForProcess process- case exitCode of- ExitSuccess -> return ()- ExitFailure i -> error $ "WebPPL terminated with exit code " ++ show i-+prepareBenchmark e Anglican model alg = env prepareData (const $ bench name $ whnfIO collect)+ where+ name = show Anglican ++ sep ++ show model ++ sep ++ show alg+ sep = "_"+ algString (MH n) = "-a lmh -n " ++ show n+ algString (SMC n) = "-a smc -n " ++ show n+ algString (RMSMC _ _) = error "Anglican does not support resample-move SMC"+ prepareData = anglicanData (lein e) model+ collect =+ anglican (lein e) ["(time (m! " ++ anglicanModelName model ++ " " ++ algString alg ++ "))\n"]+prepareBenchmark _ WebPPL model alg = bench name $ whnfIO run+ where+ name = show WebPPL ++ sep ++ show model ++ sep ++ show alg+ sep = "_"+ algString (MH n) = "--alg MCMC --samples " ++ show n ++ " --rejuv 0 "+ algString (SMC n) = "--alg SMC --samples " ++ show n ++ " --rejuv 0 "+ algString (RMSMC n t) = "--alg SMC --samples " ++ show n ++ " --rejuv " ++ show t ++ " "+ dataString (LR dataset) = let (xs, labels) = unzip dataset in "--xs='" ++ javascriptList xs ++ "' --labels='" ++ javascriptList (map (\b -> if b then (1 :: Int) else 0) labels) ++ "'"+ dataString (HMM obs) = "--obs='" ++ javascriptList obs ++ "'"+ dataString (LDA docs) = unwords $ zipWith (\i doc -> "--doc" ++ show (i :: Int) ++ "='" ++ unwords doc ++ "'") [1 .. 5] docs+ run = do+ let command = "node " ++ webpplModelName model ++ ".js " ++ algString alg ++ dataString model+ (_, _, _, process) <- createProcess $ (shell command) {cwd = Just webpplPath, std_out = NoStream, std_err = NoStream}+ exitCode <- waitForProcess process+ case exitCode of+ ExitSuccess -> return ()+ ExitFailure i -> error $ "WebPPL terminated with exit code " ++ show i -- | Checks if the requested benchmark is implemented. supported :: (ProbProgSys, Model, Alg) -> Bool supported (Anglican, _, RMSMC _ _) = False supported _ = True -systems = [- MonadBayes- -- Anglican,- -- WebPPL- ]+systems :: [ProbProgSys]+systems =+ [ MonadBayes+ -- Anglican,+ -- WebPPL+ ] -lengthBenchmarks e lrData hmmData ldaData = benchmarks where- lrLengths = [10] ++ map (*100) [1..10]- hmmLengths = [10] ++ map (*100) [1..10]- ldaLengths = [5] ++ map (*50) [1..10]- models =- map (LR . (`take` lrData)) lrLengths ++- map (HMM . (`take` hmmData)) hmmLengths ++- map (\n -> LDA $ map (take n) ldaData) ldaLengths- algs = [- MH 100,- SMC 100,- RMSMC 10 1- ]- benchmarks = map (uncurry3 (prepareBenchmark e)) $ filter supported xs where- uncurry3 f (x,y,z) = f x y z+lengthBenchmarks :: Env -> [(Double, Bool)] -> [Double] -> [[String]] -> [Benchmark]+lengthBenchmarks e lrData hmmData ldaData = benchmarks+ where+ lrLengths = 10 : map (* 100) [1 .. 10]+ hmmLengths = 10 : map (* 100) [1 .. 10]+ ldaLengths = 5 : map (* 50) [1 .. 10]+ models =+ map (LR . (`take` lrData)) lrLengths+ ++ map (HMM . (`take` hmmData)) hmmLengths+ ++ map (\n -> LDA $ map (take n) ldaData) ldaLengths+ algs =+ [ MH 100,+ SMC 100,+ RMSMC 10 1+ ]+ benchmarks = map (uncurry3 (prepareBenchmark e)) $ filter supported xs+ where+ uncurry3 f (x, y, z) = f x y z xs = do m <- models s <- systems a <- algs- return (s,m,a)+ return (s, m, a) -samplesBenchmarks e lrData hmmData ldaData = benchmarks where- lrLengths = [50]- hmmLengths = [20]- ldaLengths = [10]- models = map (LR . (`take` lrData)) lrLengths ++- map (HMM . (`take` hmmData)) hmmLengths ++- map (\n -> LDA $ map (take n) ldaData) ldaLengths- algs = map (\x -> MH (100*x)) [1..10] ++ map (\x -> SMC (100*x)) [1..10]- ++ map (\x -> RMSMC 10 (10*x)) [1..10]- benchmarks = map (uncurry3 (prepareBenchmark e)) $ filter supported xs where- uncurry3 f (x,y,z) = f x y z+samplesBenchmarks :: Env -> [(Double, Bool)] -> [Double] -> [[String]] -> [Benchmark]+samplesBenchmarks e lrData hmmData ldaData = benchmarks+ where+ lrLengths = [50]+ hmmLengths = [20]+ ldaLengths = [10]+ models =+ map (LR . (`take` lrData)) lrLengths+ ++ map (HMM . (`take` hmmData)) hmmLengths+ ++ map (\n -> LDA $ map (take n) ldaData) ldaLengths+ algs =+ map (\x -> MH (100 * x)) [1 .. 10] ++ map (\x -> SMC (100 * x)) [1 .. 10]+ ++ map (\x -> RMSMC 10 (10 * x)) [1 .. 10]+ benchmarks = map (uncurry3 (prepareBenchmark e)) $ filter supported xs+ where+ uncurry3 f (x, y, z) = f x y z xs = do a <- algs s <- systems m <- models- return (s,m,a)+ return (s, m, a) main :: IO () main = do- g <- createSystemRandom l <- startLein let e = Env g l- lrData <- sampleIOwith (LogReg.syntheticData 1000) g hmmData <- sampleIOwith (HMM.syntheticData 1000) g ldaData <- sampleIOwith (LDA.syntheticData 5 1000) g-- let configLength = defaultConfig{csvFile = Just "speed-length.csv", rawDataFile = Just "raw.dat"}+ let configLength = defaultConfig {csvFile = Just "speed-length.csv", rawDataFile = Just "raw.dat"} defaultMainWith configLength (lengthBenchmarks e lrData hmmData ldaData)-- let configSamples = defaultConfig{csvFile = Just "speed-samples.csv", rawDataFile = Just "raw.dat"}+ let configSamples = defaultConfig {csvFile = Just "speed-samples.csv", rawDataFile = Just "raw.dat"} defaultMainWith configSamples (samplesBenchmarks e lrData hmmData ldaData)
models/Dice.hs view
@@ -1,5 +1,3 @@-- module Dice where -- A toy model for dice rolling from http://dl.acm.org/citation.cfm?id=2804317@@ -10,23 +8,23 @@ -- | A toss of a six-sided die. die :: MonadSample m => m Int-die = uniformD [1..6]+die = uniformD [1 .. 6] -- | A sum of outcomes of n independent tosses of six-sided dice. dice :: MonadSample m => Int -> m Int dice 1 = die-dice n = liftM2 (+) die (dice (n-1))+dice n = liftM2 (+) die (dice (n -1)) -- | Toss of two dice where the output is greater than 4.-dice_hard :: MonadInfer m => m Int-dice_hard = do+diceHard :: MonadInfer m => m Int+diceHard = do result <- dice 2 condition (result > 4) return result -- | Toss of two dice with an artificial soft constraint.-dice_soft :: MonadInfer m => m Int-dice_soft = do+diceSoft :: MonadInfer m => m Int+diceSoft = do result <- dice 2 score (1 / fromIntegral result) return result
models/HMM.hs view
@@ -1,55 +1,72 @@--- HMM from Anglican (https://bitbucket.org/probprog/anglican-white-paper)+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE- FlexibleContexts,- TypeFamilies- #-}+-- HMM from Anglican (https://bitbucket.org/probprog/anglican-white-paper) -module HMM (- values,- hmm,- syntheticData- ) where+module HMM+ ( values,+ hmm,+ syntheticData,+ )+where --Hidden Markov Models import Control.Monad (replicateM)-import Data.Vector (fromList)- import Control.Monad.Bayes.Class+import Data.Vector (fromList) -- | Observed values values :: [Double]-values = [0.9,0.8,0.7,0,-0.025,-5,-2,-0.1,0,- 0.13,0.45,6,0.2,0.3,-1,-1]+values =+ [ 0.9,+ 0.8,+ 0.7,+ 0,+ -0.025,+ -5,+ -2,+ -0.1,+ 0,+ 0.13,+ 0.45,+ 6,+ 0.2,+ 0.3,+ -1,+ -1+ ] -- | The transition model. trans :: MonadSample m => Int -> m Int trans 0 = categorical $ fromList [0.1, 0.4, 0.5] trans 1 = categorical $ fromList [0.2, 0.6, 0.2]-trans 2 = categorical $ fromList [0.15,0.7,0.15]+trans 2 = categorical $ fromList [0.15, 0.7, 0.15]+trans _ = error "unreachable" -- | The emission model. emissionMean :: Int -> Double-emissionMean x = mean x where- mean 0 = -1- mean 1 = 1- mean 2 = 0+emissionMean 0 = -1+emissionMean 1 = 1+emissionMean 2 = 0+emissionMean _ = error "unreachable" -- | Initial state distribution start :: MonadSample m => m Int-start = uniformD [0,1,2]+start = uniformD [0, 1, 2] -- | Example HMM from http://dl.acm.org/citation.cfm?id=2804317 hmm :: (MonadInfer m) => [Double] -> m [Int]-hmm dataset = f dataset (const . return) where- expand x y = do- x' <- trans x- factor $ normalPdf (emissionMean x') 1 y- return x'- f [] k = start >>= k []- f (y:ys) k = f ys (\xs x -> expand x y >>= k (x:xs))+hmm dataset = f dataset (const . return)+ where+ expand x y = do+ x' <- trans x+ factor $ normalPdf (emissionMean x') 1 y+ return x'+ f [] k = start >>= k []+ f (y : ys) k = f ys (\xs x -> expand x y >>= k (x : xs)) syntheticData :: MonadSample m => Int -> m [Double]-syntheticData n = replicateM n syntheticPoint where- syntheticPoint = uniformD [0,1,2]+syntheticData n = replicateM n syntheticPoint+ where+ syntheticPoint = uniformD [0, 1, 2]
models/LDA.hs view
@@ -3,54 +3,52 @@ module LDA where -import Numeric.Log import qualified Control.Monad as List (replicateM)-import Data.Vector as V hiding (length, zip, mapM, mapM_)+import Control.Monad.Bayes.Class import qualified Data.Map as Map+import Data.Vector as V hiding (length, mapM, mapM_, zip)+import Numeric.Log -import Control.Monad.Bayes.Class+vocabulary :: [String]+vocabulary = ["bear", "wolf", "python", "prolog"] -vocabluary :: [String]-vocabluary = ["bear", "wolf", "python", "prolog"] topics :: [String] topics = ["topic1", "topic2"] -docs :: [[String]]-docs = [- words "bear wolf bear wolf bear wolf python wolf bear wolf",- words "python prolog python prolog python prolog python prolog python prolog",- words "bear wolf bear wolf bear wolf bear wolf bear wolf",- words "python prolog python prolog python prolog python prolog python prolog",- words "bear wolf bear python bear wolf bear wolf bear wolf"+documents :: [[String]]+documents =+ [ words "bear wolf bear wolf bear wolf python wolf bear wolf",+ words "python prolog python prolog python prolog python prolog python prolog",+ words "bear wolf bear wolf bear wolf bear wolf bear wolf",+ words "python prolog python prolog python prolog python prolog python prolog",+ words "bear wolf bear python bear wolf bear wolf bear wolf" ] -word_dist_prior :: MonadSample m => m (Vector Double)-word_dist_prior = dirichlet $ V.replicate (length vocabluary) 1+wordDistPrior :: MonadSample m => m (Vector Double)+wordDistPrior = dirichlet $ V.replicate (length vocabulary) 1 -topic_dist_prior :: MonadSample m => m (Vector Double)-topic_dist_prior = dirichlet $ V.replicate (length topics) 1+topicDistPrior :: MonadSample m => m (Vector Double)+topicDistPrior = dirichlet $ V.replicate (length topics) 1 -word_index :: Map.Map String Int-word_index = Map.fromList $ zip vocabluary [0..]+wordIndex :: Map.Map String Int+wordIndex = Map.fromList $ zip vocabulary [0 ..] lda :: MonadInfer m => [[String]] -> m [Int] lda docs = do word_dist_for_topic <- do- ts <- mapM (const word_dist_prior) [0 .. length topics]+ ts <- mapM (const wordDistPrior) [0 .. length topics] return $ Map.fromList $ zip [0 .. length topics] ts- let obs doc = do- topic_dist <- fmap categorical topic_dist_prior+ topic_dist <- fmap categorical topicDistPrior let f word = do topic <- topic_dist- factor $ (Exp . log) $ (word_dist_for_topic Map.! topic) V.! (word_index Map.! word)+ factor $ (Exp . log) $ (word_dist_for_topic Map.! topic) V.! (wordIndex Map.! word) mapM_ f doc- mapM_ obs docs- -- return samples since Discrete is not NFData mapM (categorical . snd) $ Map.toList word_dist_for_topic syntheticData :: MonadSample m => Int -> Int -> m [[String]]-syntheticData d w = List.replicateM d (List.replicateM w syntheticWord) where- syntheticWord = uniformD vocabluary+syntheticData d w = List.replicateM d (List.replicateM w syntheticWord)+ where+ syntheticWord = uniformD vocabulary
models/LogReg.hs view
@@ -4,9 +4,8 @@ module LogReg where import Control.Monad (replicateM)--import Numeric.Log import Control.Monad.Bayes.Class+import Numeric.Log xs :: [Double] xs = [-10, -5, 2, 6, 10]@@ -28,8 +27,9 @@ sigmoid 8 syntheticData :: MonadSample m => Int -> m [(Double, Bool)]-syntheticData n = replicateM n syntheticPoint where+syntheticData n = replicateM n syntheticPoint+ where syntheticPoint = do x <- uniform (-1) 1 label <- bernoulli 0.5- return (x,label)+ return (x, label)
models/NonlinearSSM.hs view
@@ -12,42 +12,46 @@ let sigmaY = 1 / sqrt precY return (sigmaX, sigmaY) +mean :: Double -> Int -> Double+mean x n = 0.5 * x + 25 * x / (1 + x * x) + 8 * cos (1.2 * fromIntegral n)+ -- | A nonlinear series model from Doucet et al. (2000) -- "On sequential Monte Carlo sampling methods" section VI.B-model :: (MonadInfer m)- => [Double] -- ^ observed data- -> (Double, Double) -- ^ prior on the parameters- -> m [Double] -- ^ list of latent states from t=1+model ::+ (MonadInfer m) =>+ -- | observed data+ [Double] ->+ -- | prior on the parameters+ (Double, Double) ->+ -- | list of latent states from t=1+ m [Double] model obs (sigmaX, sigmaY) = do let sq x = x * x simulate [] _ acc = return acc- simulate (y:ys) x acc = do+ simulate (y : ys) x acc = do let n = length acc- let mean = 0.5 * x + 25 * x / (1 + sq x) +- 8 * cos (1.2 * fromIntegral n)- x' <- normal mean sigmaX+ x' <- normal (mean x n) sigmaX factor $ normalPdf (sq x' / 20) sigmaY y- simulate ys x' (x':acc)-+ simulate ys x' (x' : acc) x0 <- normal 0 (sqrt 5) xs <- simulate obs x0 [] return $ reverse xs -generateData :: MonadSample m- => Int -- ^ T- -> m [(Double,Double)] -- ^ list of latent and observable states from t=1+generateData ::+ MonadSample m =>+ -- | T+ Int ->+ -- | list of latent and observable states from t=1+ m [(Double, Double)] generateData t = do (sigmaX, sigmaY) <- param let sq x = x * x simulate 0 _ acc = return acc simulate k x acc = do let n = length acc- let mean = 0.5 * x + 25 * x / (1 + sq x) +- 8 * cos (1.2 * fromIntegral n)- x' <- normal mean sigmaX+ x' <- normal (mean x n) sigmaX y' <- normal (sq x' / 20) sigmaY- simulate (k-1) x' ((x',y'):acc)-+ simulate (k -1) x' ((x', y') : acc) x0 <- normal 0 (sqrt 5) xys <- simulate t x0 [] return $ reverse xys
models/Sprinkler.hs view
@@ -1,17 +1,17 @@ module Sprinkler where import Control.Monad (when)- import Control.Monad.Bayes.Class hard :: MonadInfer m => m Bool hard = do rain <- bernoulli 0.3 sprinkler <- bernoulli $ if rain then 0.1 else 0.4- wet <- bernoulli $ case (rain,sprinkler) of (True,True) -> 0.98- (True,False) -> 0.8- (False,True) -> 0.9- (False,False) -> 0.0+ wet <- bernoulli $ case (rain, sprinkler) of+ (True, True) -> 0.98+ (True, False) -> 0.8+ (False, True) -> 0.9+ (False, False) -> 0.0 condition (not wet) return rain
monad-bayes.cabal view
@@ -1,141 +1,183 @@-cabal-version: 2.0-name: monad-bayes-version: 0.1.0.0-synopsis: A library for probabilistic programming.-description: A library for probabilistic programming using probability monads. The emphasis is on composition of inference algorithms implemented in terms of monad transformers.-homepage: http://github.com/tweag/monad-bayes#readme-license: MIT-license-file: LICENSE-author: Adam Scibior <adscib@gmail.com>-maintainer: leonhard.markert@tweag.io-copyright: 2015-2020 Adam Scibior-bug-reports: https://github.com/tweag/monad-bayes/issues-stability: experimental-category: Statistics-build-type: Simple+cabal-version: 2.0+name: monad-bayes+version: 0.1.1.0+license: MIT+license-file: LICENSE.md+copyright: 2015-2020 Adam Scibior+maintainer: leonhard.markert@tweag.io+author: Adam Scibior <adscib@gmail.com>+stability: experimental+tested-with: ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.1+homepage: http://github.com/tweag/monad-bayes#readme+bug-reports: https://github.com/tweag/monad-bayes/issues+synopsis: A library for probabilistic programming.+description:+ A library for probabilistic programming using probability monads. The+ emphasis is on composition of inference algorithms implemented in+ terms of monad transformers. -extra-source-files:- CHANGELOG.md+category: Statistics+build-type: Simple+extra-source-files: CHANGELOG.md -executable example- hs-source-dirs: benchmark, models- main-is: Single.hs- build-depends: base- , monad-bayes- , log-domain- , vector- , containers- , mwc-random- , time- , optparse-applicative- default-language: Haskell2010- other-modules: LogReg- , HMM- , LDA- , Dice+source-repository head+ type: git+ location: https://github.com/tweag/monad-bayes.git +flag dev+ description: Turn on development settings.+ default: False+ manual: True+ library- hs-source-dirs: src- exposed-modules: Control.Monad.Bayes.Class- , Control.Monad.Bayes.Free- , Control.Monad.Bayes.Sampler- , Control.Monad.Bayes.Weighted- , Control.Monad.Bayes.Sequential- , Control.Monad.Bayes.Population- , Control.Monad.Bayes.Traced.Static- , Control.Monad.Bayes.Traced.Dynamic- , Control.Monad.Bayes.Traced.Basic- , Control.Monad.Bayes.Traced- , Control.Monad.Bayes.Enumerator- , Control.Monad.Bayes.Helpers- , Control.Monad.Bayes.Inference.SMC- , Control.Monad.Bayes.Inference.RMSMC- , Control.Monad.Bayes.Inference.PMMH- , Control.Monad.Bayes.Inference.SMC2- other-modules: Control.Monad.Bayes.Traced.Common- -- See MAINTAINERS.md for when and how to update the version bounds.- build-depends: base >= 4.10.1 && < 4.14- , containers >= 0.5.10 && < 0.7- , free >= 5.0.2 && < 5.2- , ieee754 >= 0.8.0 && < 0.9- , log-domain >= 0.12 && < 0.14- , math-functions >= 0.2.1 && < 0.4- , monad-coroutine >= 0.9.0 && < 0.10- , mtl >= 2.2.2 && < 2.3- , mwc-random >= 0.13.6 && < 0.15- , safe >= 0.3.17 && < 0.4- , statistics >= 0.14.0 && < 0.16- , transformers >= 0.5.2 && < 0.6- , vector >= 0.12.0 && < 0.13- ghc-options: -Wall -fno-warn-redundant-constraints- default-language: Haskell2010- default-extensions: RankNTypes- , GeneralizedNewtypeDeriving- , StandaloneDeriving- , TypeFamilies- , FlexibleContexts- , FlexibleInstances- , TupleSections- , MultiParamTypeClasses- , GADTs- other-extensions: ScopedTypeVariables- , DeriveFunctor+ exposed-modules:+ Control.Monad.Bayes.Class+ Control.Monad.Bayes.Enumerator+ Control.Monad.Bayes.Free+ Control.Monad.Bayes.Helpers+ Control.Monad.Bayes.Inference.PMMH+ Control.Monad.Bayes.Inference.RMSMC+ Control.Monad.Bayes.Inference.SMC+ Control.Monad.Bayes.Inference.SMC2+ Control.Monad.Bayes.Population+ Control.Monad.Bayes.Sampler+ Control.Monad.Bayes.Sequential+ Control.Monad.Bayes.Traced+ Control.Monad.Bayes.Traced.Basic+ Control.Monad.Bayes.Traced.Dynamic+ Control.Monad.Bayes.Traced.Static+ Control.Monad.Bayes.Weighted + hs-source-dirs: src+ other-modules: Control.Monad.Bayes.Traced.Common+ default-language: Haskell2010+ default-extensions:+ MultiParamTypeClasses RankNTypes FlexibleContexts FlexibleInstances+ GeneralizedNewtypeDeriving TypeFamilies StandaloneDeriving GADTs+ TupleSections++ other-extensions: ScopedTypeVariables DeriveFunctor+ build-depends:+ base >=4.11 && <4.14,+ containers >=0.5.10 && <0.7,+ free >=5.0.2 && <5.2,+ ieee754 >=0.8.0 && <0.9,+ log-domain >=0.12 && <0.14,+ math-functions >=0.2.1 && <0.4,+ monad-coroutine >=0.9.0 && <0.10,+ mtl >=2.2.2 && <2.3,+ mwc-random >=0.13.6 && <0.15,+ safe >=0.3.17 && <0.4,+ statistics >=0.14.0 && <0.16,+ transformers >=0.5.2 && <0.6,+ vector >=0.12.0 && <0.13++ if flag(dev)+ ghc-options:+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances++ else+ ghc-options: -Wall++executable example+ main-is: Single.hs+ hs-source-dirs: benchmark models+ other-modules:+ Dice+ HMM+ LDA+ LogReg++ default-language: Haskell2010+ build-depends:+ base -any,+ containers -any,+ log-domain -any,+ monad-bayes -any,+ mwc-random -any,+ optparse-applicative -any,+ time -any,+ vector -any++ if flag(dev)+ ghc-options:+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances++ else+ ghc-options: -Wall+ test-suite monad-bayes-test- type: exitcode-stdio-1.0- hs-source-dirs: test, models- main-is: Spec.hs- build-depends: base- , monad-bayes- , hspec- , QuickCheck- , ieee754- , mtl- , math-functions- , transformers- , log-domain- , vector- ghc-options: -Wall -threaded -rtsopts "-with-rtsopts=-N -M1g -K1g"- default-language: Haskell2010- other-modules: Sprinkler,- TestEnumerator,- TestPopulation,- TestInference,- TestSequential,- TestWeighted+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test models+ other-modules:+ Sprinkler+ TestEnumerator+ TestInference+ TestPopulation+ TestSequential+ TestWeighted + default-language: Haskell2010+ build-depends:+ base -any,+ QuickCheck -any,+ hspec -any,+ ieee754 -any,+ log-domain -any,+ math-functions -any,+ monad-bayes -any,+ mtl -any,+ transformers -any,+ vector -any++ if flag(dev)+ ghc-options:+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances++ else+ ghc-options: -Wall+ benchmark ssm-bench- type: exitcode-stdio-1.0- hs-source-dirs: models- , benchmark- build-depends: base- , monad-bayes- default-language: Haskell2010- default-extensions: RankNTypes- main-is: SSM.hs- other-modules: NonlinearSSM+ type: exitcode-stdio-1.0+ main-is: SSM.hs+ hs-source-dirs: models benchmark+ other-modules: NonlinearSSM+ default-language: Haskell2010+ default-extensions: RankNTypes+ build-depends:+ base -any,+ monad-bayes -any benchmark speed-bench- type: exitcode-stdio-1.0- hs-source-dirs: models- , benchmark- build-depends: base- , monad-bayes- , criterion- , abstract-par- , log-domain- , vector- , mwc-random- , containers- , process- ghc-options: -rtsopts "-with-rtsopts=-M1g -K1g"- default-language: Haskell2010- default-extensions: RankNTypes- main-is: Speed.hs- other-modules: LogReg- , HMM- , LDA+ type: exitcode-stdio-1.0+ main-is: Speed.hs+ hs-source-dirs: models benchmark+ other-modules:+ HMM+ LDA+ LogReg -source-repository head- type: git- location: https://github.com/tweag/monad-bayes.git+ default-language: Haskell2010+ default-extensions: RankNTypes+ build-depends:+ base -any,+ abstract-par -any,+ containers -any,+ criterion -any,+ log-domain -any,+ monad-bayes -any,+ mwc-random -any,+ process -any,+ vector -any++ if flag(dev)+ ghc-options:+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wnoncanonical-monad-instances++ else+ ghc-options: -Wall
src/Control/Monad/Bayes/Class.hs view
@@ -1,141 +1,160 @@-{-|-Module : Control.Monad.Bayes.Class-Description : Types for probabilistic modelling-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--This module defines 'MonadInfer', which can be used to represent a simple model-like the following:+-- |+-- Module : Control.Monad.Bayes.Class+-- Description : Types for probabilistic modelling+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- This module defines 'MonadInfer', which can be used to represent a simple model+-- like the following:+--+-- @+-- import Control.Monad (when)+-- import Control.Monad.Bayes.Class+--+-- model :: MonadInfer m => m Bool+-- model = do+-- rain <- bernoulli 0.3+-- sprinkler <-+-- bernoulli $+-- if rain+-- then 0.1+-- else 0.4+-- let wetProb =+-- case (rain, sprinkler) of+-- (True, True) -> 0.98+-- (True, False) -> 0.80+-- (False, True) -> 0.90+-- (False, False) -> 0.00+-- score wetProb+-- return rain+-- @+module Control.Monad.Bayes.Class+ ( MonadSample,+ random,+ uniform,+ normal,+ gamma,+ beta,+ bernoulli,+ categorical,+ logCategorical,+ uniformD,+ geometric,+ poisson,+ dirichlet,+ MonadCond,+ score,+ factor,+ condition,+ MonadInfer,+ discrete,+ normalPdf,+ )+where -@ import Control.Monad (when)-import Control.Monad.Bayes.Class--model :: MonadInfer m => m Bool-model = do- rain <- bernoulli 0.3- sprinkler <-- bernoulli $- if rain- then 0.1- else 0.4- let wetProb =- case (rain, sprinkler) of- (True, True) -> 0.98- (True, False) -> 0.80- (False, True) -> 0.90- (False, False) -> 0.00- score wetProb- return rain-@--}--module Control.Monad.Bayes.Class (- MonadSample,- random,- uniform,- normal,- gamma,- beta,- bernoulli,- categorical,- logCategorical,- uniformD,- geometric,- poisson,- dirichlet,- MonadCond,- score,- factor,- condition,- MonadInfer,- normalPdf-) where- import Control.Monad.Trans.Class+import Control.Monad.Trans.Cont import Control.Monad.Trans.Identity+import Control.Monad.Trans.List import Control.Monad.Trans.Maybe+import Control.Monad.Trans.RWS hiding (tell)+import Control.Monad.Trans.Reader import Control.Monad.Trans.State import Control.Monad.Trans.Writer-import Control.Monad.Trans.Reader-import Control.Monad.Trans.RWS hiding (tell)-import Control.Monad.Trans.List-import Control.Monad.Trans.Cont-+import qualified Data.Vector as V+import Data.Vector.Generic as VG+import Numeric.Log import Statistics.Distribution-import Statistics.Distribution.Uniform (uniformDistr)-import Statistics.Distribution.Normal (normalDistr)-import Statistics.Distribution.Gamma (gammaDistr) import Statistics.Distribution.Beta (betaDistr)+import Statistics.Distribution.Gamma (gammaDistr) import Statistics.Distribution.Geometric (geometric0)+import Statistics.Distribution.Normal (normalDistr) import qualified Statistics.Distribution.Poisson as Poisson--import Numeric.Log--import Data.Vector.Generic as VG-import qualified Data.Vector as V-import Control.Monad (when)+import Statistics.Distribution.Uniform (uniformDistr) -- | Monads that can draw random variables. class Monad m => MonadSample m where -- | Draw from a uniform distribution.- random :: m Double -- ^ \(\sim \mathcal{U}(0, 1)\)+ random ::+ -- | \(\sim \mathcal{U}(0, 1)\)+ m Double -- | Draw from a uniform distribution. uniform ::- Double -- ^ lower bound a- -> Double -- ^ upper bound b- -> m Double -- ^ \(\sim \mathcal{U}(a, b)\).+ -- | lower bound a+ Double ->+ -- | upper bound b+ Double ->+ -- | \(\sim \mathcal{U}(a, b)\).+ m Double uniform a b = draw (uniformDistr a b) -- | Draw from a normal distribution. normal ::- Double -- ^ mean μ- -> Double -- ^ standard deviation σ- -> m Double -- ^ \(\sim \mathcal{N}(\mu, \sigma^2)\)+ -- | mean μ+ Double ->+ -- | standard deviation σ+ Double ->+ -- | \(\sim \mathcal{N}(\mu, \sigma^2)\)+ m Double normal m s = draw (normalDistr m s) -- | Draw from a gamma distribution. gamma ::- Double -- ^ shape k- -> Double -- ^ scale θ- -> m Double -- ^ \(\sim \Gamma(k, \theta)\)+ -- | shape k+ Double ->+ -- | scale θ+ Double ->+ -- | \(\sim \Gamma(k, \theta)\)+ m Double gamma shape scale = draw (gammaDistr shape scale) -- | Draw from a beta distribution. beta ::- Double -- ^ shape α- -> Double -- ^ shape β- -> m Double -- ^ \(\sim \mathrm{Beta}(\alpha, \beta)\)+ -- | shape α+ Double ->+ -- | shape β+ Double ->+ -- | \(\sim \mathrm{Beta}(\alpha, \beta)\)+ m Double beta a b = draw (betaDistr a b) -- | Draw from a Bernoulli distribution. bernoulli ::- Double -- ^ probability p- -> m Bool -- ^ \(\sim \mathrm{B}(1, p)\)+ -- | probability p+ Double ->+ -- | \(\sim \mathrm{B}(1, p)\)+ m Bool bernoulli p = fmap (< p) random -- | Draw from a categorical distribution. categorical ::- Vector v Double- => v Double -- ^ event probabilities- -> m Int -- ^ outcome category+ Vector v Double =>+ -- | event probabilities+ v Double ->+ -- | outcome category+ m Int categorical ps = fromPMF (ps !) -- | Draw from a categorical distribution in the log domain. logCategorical ::- (Vector v (Log Double), Vector v Double)- => v (Log Double) -- ^ event probabilities- -> m Int -- ^ outcome category+ (Vector v (Log Double), Vector v Double) =>+ -- | event probabilities+ v (Log Double) ->+ -- | outcome category+ m Int logCategorical = categorical . VG.map (exp . ln) -- | Draw from a discrete uniform distribution. uniformD ::- [a] -- ^ observable outcomes @xs@- -> m a -- ^ \(\sim \mathcal{U}\{\mathrm{xs}\}\)+ -- | observable outcomes @xs@+ [a] ->+ -- | \(\sim \mathcal{U}\{\mathrm{xs}\}\)+ m a uniformD xs = do let n = Prelude.length xs i <- categorical $ V.replicate n (1 / fromIntegral n)@@ -143,21 +162,27 @@ -- | Draw from a geometric distribution. geometric ::- Double -- ^ success rate p- -> m Int -- ^ \(\sim\) number of failed Bernoulli trials with success probability p before first success+ -- | success rate p+ Double ->+ -- | \(\sim\) number of failed Bernoulli trials with success probability p before first success+ m Int geometric = discrete . geometric0 -- | Draw from a Poisson distribution. poisson ::- Double -- ^ parameter λ- -> m Int -- ^ \(\sim \mathrm{Pois}(\lambda)\)+ -- | parameter λ+ Double ->+ -- | \(\sim \mathrm{Pois}(\lambda)\)+ m Int poisson = discrete . Poisson.poisson -- | Draw from a Dirichlet distribution. dirichlet ::- Vector v Double- => v Double -- ^ concentration parameters @as@- -> m (v Double) -- ^ \(\sim \mathrm{Dir}(\mathrm{as})\)+ Vector v Double =>+ -- | concentration parameters @as@+ v Double ->+ -- | \(\sim \mathrm{Dir}(\mathrm{as})\)+ m (v Double) dirichlet as = do xs <- VG.mapM (`gamma` 1) as let s = VG.sum xs@@ -172,13 +197,14 @@ -- | Draw from a discrete distribution using a sequence of draws from -- Bernoulli. fromPMF :: MonadSample m => (Int -> Double) -> m Int-fromPMF p = f 0 1 where- f i r = do- when (r < 0) $ error "fromPMF: total PMF above 1"- let q = p i- when (q < 0 || q > 1) $ error "fromPMF: invalid probability value"- b <- bernoulli (q / r)- if b then pure i else f (i+1) (r-q)+fromPMF p = f 0 1+ where+ f i r = do+ when (r < 0) $ error "fromPMF: total PMF above 1"+ let q = p i+ when (q < 0 || q > 1) $ error "fromPMF: invalid probability value"+ b <- bernoulli (q / r)+ if b then pure i else f (i + 1) (r - q) -- | Draw from a discrete distributions using the probability mass function. discrete :: (DiscreteDistr d, MonadSample m) => d -> m Int@@ -188,14 +214,16 @@ class Monad m => MonadCond m where -- | Record a likelihood. score ::- Log Double -- ^ likelihood of the execution path- -> m ()+ -- | likelihood of the execution path+ Log Double ->+ m () -- | Synonym for 'score'. factor ::- MonadCond m- => Log Double -- ^ likelihood of the execution path- -> m ()+ MonadCond m =>+ -- | likelihood of the execution path+ Log Double ->+ m () factor = score -- | Hard conditioning.@@ -207,10 +235,14 @@ -- | Probability density function of the normal distribution. normalPdf ::- Double -- ^ mean μ- -> Double -- ^ standard deviation σ- -> Double -- ^ sample x- -> Log Double -- ^ relative likelihood of observing sample x in \(\mathcal{N}(\mu, \sigma^2)\)+ -- | mean μ+ Double ->+ -- | standard deviation σ+ Double ->+ -- | sample x+ Double ->+ -- | relative likelihood of observing sample x in \(\mathcal{N}(\mu, \sigma^2)\)+ Log Double normalPdf mu sigma x = Exp $ logDensity (normalDistr mu sigma) x ----------------------------------------------------------------------------@@ -225,7 +257,6 @@ instance MonadInfer m => MonadInfer (IdentityT m) - instance MonadSample m => MonadSample (MaybeT m) where random = lift random @@ -234,7 +265,6 @@ instance MonadInfer m => MonadInfer (MaybeT m) - instance MonadSample m => MonadSample (ReaderT r m) where random = lift random bernoulli = lift . bernoulli@@ -244,7 +274,6 @@ instance MonadInfer m => MonadInfer (ReaderT r m) - instance (Monoid w, MonadSample m) => MonadSample (WriterT w m) where random = lift random bernoulli = lift . bernoulli@@ -255,7 +284,6 @@ instance (Monoid w, MonadInfer m) => MonadInfer (WriterT w m) - instance MonadSample m => MonadSample (StateT s m) where random = lift random bernoulli = lift . bernoulli@@ -266,7 +294,6 @@ instance MonadInfer m => MonadInfer (StateT s m) - instance (MonadSample m, Monoid w) => MonadSample (RWST r w s m) where random = lift random @@ -275,7 +302,6 @@ instance (MonadInfer m, Monoid w) => MonadInfer (RWST r w s m) - instance MonadSample m => MonadSample (ListT m) where random = lift random bernoulli = lift . bernoulli@@ -285,7 +311,6 @@ score = lift . score instance MonadInfer m => MonadInfer (ListT m)- instance MonadSample m => MonadSample (ContT r m) where random = lift random
src/Control/Monad/Bayes/Enumerator.hs view
@@ -1,16 +1,13 @@-{-|-Module : Control.Monad.Bayes.Enumerator-Description : Exhaustive enumeration of discrete random variables-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Enumerator (- Enumerator,+-- |+-- Module : Control.Monad.Bayes.Enumerator+-- Description : Exhaustive enumeration of discrete random variables+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Enumerator+ ( Enumerator, logExplicit, explicit, evidence,@@ -18,32 +15,31 @@ compact, enumerate, expectation,- normalForm- ) where+ normalForm,+ )+where -import Data.AEq (AEq, (===), (~==))-import Control.Applicative (Applicative, Alternative)-import Control.Monad (MonadPlus)+import Control.Applicative (Alternative) import Control.Arrow (second)+import Control.Monad (MonadPlus)+import Control.Monad.Bayes.Class+import Control.Monad.Trans.Writer+import Data.AEq ((===), AEq, (~==)) import qualified Data.Map as Map+import Data.Maybe+import Data.Monoid import qualified Data.Vector.Generic as V import Numeric.Log as Log-import Control.Monad.Trans.Writer-import Data.Monoid-import Data.Maybe -import Control.Monad.Bayes.Class-- -- | An exact inference transformer that integrates -- discrete random variables by enumerating all execution paths. newtype Enumerator a = Enumerator (WriterT (Product (Log Double)) [] a)- deriving(Functor, Applicative, Monad, Alternative, MonadPlus)+ deriving (Functor, Applicative, Monad, Alternative, MonadPlus) instance MonadSample Enumerator where random = error "Infinitely supported random variables not supported in Enumerator"- bernoulli p = fromList [(True, (Exp . log) p), (False, (Exp . log) (1-p))]- categorical v = fromList $ zip [0..] $ map (Exp . log) (V.toList v)+ bernoulli p = fromList [(True, (Exp . log) p), (False, (Exp . log) (1 - p))]+ categorical v = fromList $ zip [0 ..] $ map (Exp . log) (V.toList v) instance MonadCond Enumerator where score w = fromList [((), w)]@@ -60,7 +56,7 @@ logExplicit (Enumerator m) = map (second getProduct) $ runWriterT m -- | Same as `toList`, only weights are converted from log-domain.-explicit :: Enumerator a -> [(a,Double)]+explicit :: Enumerator a -> [(a, Double)] explicit = map (second (exp . ln)) . logExplicit -- | Returns the model evidence, that is sum of all weights.@@ -69,13 +65,14 @@ -- | Normalized probability mass of a specific value. mass :: Ord a => Enumerator a -> a -> Double-mass d = f where- f a = fromMaybe 0 $ lookup a m- m = enumerate d+mass d = f+ where+ f a = fromMaybe 0 $ lookup a m+ m = enumerate d -- | Aggregate weights of equal values. -- The resulting list is sorted ascendingly according to values.-compact :: (Num r, Ord a) => [(a,r)] -> [(a,r)]+compact :: (Num r, Ord a) => [(a, r)] -> [(a, r)] compact = Map.toAscList . Map.fromListWith (+) -- | Aggregate and normalize of weights.@@ -83,22 +80,25 @@ -- -- > enumerate = compact . explicit enumerate :: Ord a => Enumerator a -> [(a, Double)]-enumerate d = compact (zip xs ws) where- (xs, ws) = second (map (exp . ln) . normalize) $ unzip (logExplicit d)+enumerate d = compact (zip xs ws)+ where+ (xs, ws) = second (map (exp . ln) . normalize) $ unzip (logExplicit d) -- | Expectation of a given function computed using normalized weights. expectation :: (a -> Double) -> Enumerator a -> Double expectation f = Prelude.sum . map (\(x, w) -> f x * (exp . ln) w) . normalizeWeights . logExplicit normalize :: [Log Double] -> [Log Double]-normalize xs = map (/ z) xs where- z = Log.sum xs+normalize xs = map (/ z) xs+ where+ z = Log.sum xs -- | Divide all weights by their sum. normalizeWeights :: [(a, Log Double)] -> [(a, Log Double)]-normalizeWeights ls = zip xs ps where- (xs, ws) = unzip ls- ps = normalize ws+normalizeWeights ls = zip xs ps+ where+ (xs, ws) = unzip ls+ ps = normalize ws -- | 'compact' followed by removing values with zero weight. normalForm :: Ord a => Enumerator a -> [(a, Double)]@@ -108,9 +108,11 @@ p == q = normalForm p == normalForm q instance Ord a => AEq (Enumerator a) where- p === q = xs == ys && ps === qs where- (xs,ps) = unzip (normalForm p)- (ys,qs) = unzip (normalForm q)- p ~== q = xs == ys && ps ~== qs where- (xs,ps) = unzip $ filter (not . (~== 0) . snd) $ normalForm p- (ys,qs) = unzip $ filter (not . (~== 0) . snd) $ normalForm q+ p === q = xs == ys && ps === qs+ where+ (xs, ps) = unzip (normalForm p)+ (ys, qs) = unzip (normalForm q)+ p ~== q = xs == ys && ps ~== qs+ where+ (xs, ps) = unzip $ filter (not . (~== 0) . snd) $ normalForm p+ (ys, qs) = unzip $ filter (not . (~== 0) . snd) $ normalForm q
src/Control/Monad/Bayes/Free.hs view
@@ -1,32 +1,29 @@-{-|-Module : Control.Monad.Bayes.Free-Description : Free monad transformer over random sampling-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--'FreeSampler' is a free monad transformer over random sampling.--}--module Control.Monad.Bayes.Free (- FreeSampler,- hoist,- interpret,- withRandomness,- withPartialRandomness,- runWith-) where--import Data.Functor.Identity--import Control.Monad.Trans-import Control.Monad.Writer-import Control.Monad.State-import Control.Monad.Trans.Free.Church+-- |+-- Module : Control.Monad.Bayes.Free+-- Description : Free monad transformer over random sampling+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- 'FreeSampler' is a free monad transformer over random sampling.+module Control.Monad.Bayes.Free+ ( FreeSampler,+ hoist,+ interpret,+ withRandomness,+ withPartialRandomness,+ runWith,+ )+where import Control.Monad.Bayes.Class+import Control.Monad.State (evalStateT, get, put)+import Control.Monad.Trans (MonadTrans (..))+import Control.Monad.Trans.Free.Church (FT, MonadFree (..), hoistFT, iterT, iterTM, liftF)+import Control.Monad.Writer (WriterT (..), tell)+import Data.Functor.Identity (Identity, runIdentity) -- | Random sampling functor. newtype SamF a = Random (Double -> a)@@ -34,15 +31,11 @@ instance Functor SamF where fmap f (Random k) = Random (f . k) - -- | Free monad transformer over random sampling.-+-- -- Uses the Church-encoded version of the free monad for efficiency.-newtype FreeSampler m a = FreeSampler (FT SamF m a)- deriving(Functor,Applicative,Monad,MonadTrans)--runFreeSampler :: FreeSampler m a -> FT SamF m a-runFreeSampler (FreeSampler m) = m+newtype FreeSampler m a = FreeSampler {runFreeSampler :: FT SamF m a}+ deriving (Functor, Applicative, Monad, MonadTrans) instance Monad m => MonadFree SamF (FreeSampler m) where wrap = FreeSampler . wrap . fmap runFreeSampler@@ -56,32 +49,35 @@ -- | Execute random sampling in the transformed monad. interpret :: MonadSample m => FreeSampler m a -> m a-interpret (FreeSampler m) = iterT f m where- f (Random k) = random >>= k+interpret (FreeSampler m) = iterT f m+ where+ f (Random k) = random >>= k -- | Execute computation with supplied values for random choices. withRandomness :: Monad m => [Double] -> FreeSampler m a -> m a-withRandomness randomness (FreeSampler m) = evalStateT (iterTM f m) randomness where- f (Random k) = do- xs <- get- case xs of- [] -> error "FreeSampler: the list of randomness was too short"- y:ys -> put ys >> k y+withRandomness randomness (FreeSampler m) = evalStateT (iterTM f m) randomness+ where+ f (Random k) = do+ xs <- get+ case xs of+ [] -> error "FreeSampler: the list of randomness was too short"+ y : ys -> put ys >> k y -- | Execute computation with supplied values for a subset of random choices. -- Return the output value and a record of all random choices used, whether -- taken as input or drawn using the transformed monad. withPartialRandomness :: MonadSample m => [Double] -> FreeSampler m a -> m (a, [Double]) withPartialRandomness randomness (FreeSampler m) =- runWriterT $ evalStateT (iterTM f $ hoistFT lift m) randomness where+ runWriterT $ evalStateT (iterTM f $ hoistFT lift m) randomness+ where f (Random k) = do -- This block runs in StateT [Double] (WriterT [Double]) m. -- StateT propagates consumed randomness while WriterT records -- randomness used, whether old or new. xs <- get x <- case xs of- [] -> random- y:ys -> put ys >> return y+ [] -> random+ y : ys -> put ys >> return y tell [x] k x
src/Control/Monad/Bayes/Helpers.hs view
@@ -1,47 +1,52 @@-{-|-Module : Control.Monad.Bayes.Helpers-Description : Helper functions for working with inference monads-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Helpers (- W,- hoistW,- P,- hoistP,- S,- hoistS,- F,- hoistF,- T,- hoistT,- hoistWF,- hoistSP,- hoistSTP-) where+-- |+-- Module : Control.Monad.Bayes.Helpers+-- Description : Helper functions for working with inference monads+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Helpers+ ( W,+ hoistW,+ P,+ hoistP,+ S,+ hoistS,+ F,+ hoistF,+ T,+ hoistT,+ hoistWF,+ hoistSP,+ hoistSTP,+ )+where -import Control.Monad.Bayes.Weighted as Weighted+import Control.Monad.Bayes.Free as Free import Control.Monad.Bayes.Population as Pop import Control.Monad.Bayes.Sequential as Seq-import Control.Monad.Bayes.Free as Free import Control.Monad.Bayes.Traced as Tr+import Control.Monad.Bayes.Weighted as Weighted type W = Weighted+ type P = Population+ type S = Sequential+ type F = FreeSampler+ type T = Traced hoistW :: (forall x. m x -> n x) -> W m a -> W n a hoistW = Weighted.hoist -hoistP :: (Monad m, Monad n)- => (forall x. m x -> n x) -> P m a -> P n a+hoistP ::+ (Monad m, Monad n) =>+ (forall x. m x -> n x) ->+ P m a ->+ P n a hoistP = Pop.hoist hoistS :: (forall x. m x -> m x) -> S m a -> S m a@@ -50,18 +55,23 @@ hoistF :: (Monad m, Monad n) => (forall x. m x -> n x) -> F m a -> F n a hoistF = Free.hoist --hoistWF :: (Monad m, Monad n)- => (forall x. m x -> n x)- -> W (F m) a -> W (F n) a+hoistWF ::+ (Monad m, Monad n) =>+ (forall x. m x -> n x) ->+ W (F m) a ->+ W (F n) a hoistWF m = hoistW $ hoistF m -hoistSP :: Monad m- => (forall x. m x -> m x)- -> S (P m) a -> S (P m) a+hoistSP ::+ Monad m =>+ (forall x. m x -> m x) ->+ S (P m) a ->+ S (P m) a hoistSP m = hoistS $ hoistP m -hoistSTP :: Monad m- => (forall x. m x -> m x)- -> S (T (P m)) a -> S (T (P m)) a+hoistSTP ::+ Monad m =>+ (forall x. m x -> m x) ->+ S (T (P m)) a ->+ S (T (P m)) a hoistSTP m = hoistS $ hoistT $ hoistP m
src/Control/Monad/Bayes/Inference/PMMH.hs view
@@ -1,38 +1,41 @@-{-|-Module : Control.Monad.Bayes.Inference.PMMH-Description : Particle Marginal Metropolis-Hastings (PMMH)-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--Particle Marginal Metropolis-Hastings (PMMH) sampling.--Christophe Andrieu, Arnaud Doucet, and Roman Holenstein. 2010. Particle Markov chain Monte Carlo Methods. /Journal of the Royal Statistical Society/ 72 (2010), 269-342. <http://www.stats.ox.ac.uk/~doucet/andrieu_doucet_holenstein_PMCMC.pdf>--}--module Control.Monad.Bayes.Inference.PMMH (- pmmh-) where--import Numeric.Log--import Control.Monad.Trans (lift)+-- |+-- Module : Control.Monad.Bayes.Inference.PMMH+-- Description : Particle Marginal Metropolis-Hastings (PMMH)+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- Particle Marginal Metropolis-Hastings (PMMH) sampling.+--+-- Christophe Andrieu, Arnaud Doucet, and Roman Holenstein. 2010. Particle Markov chain Monte Carlo Methods. /Journal of the Royal Statistical Society/ 72 (2010), 269-342. <http://www.stats.ox.ac.uk/~doucet/andrieu_doucet_holenstein_PMCMC.pdf>+module Control.Monad.Bayes.Inference.PMMH+ ( pmmh,+ )+where import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Sequential+import Control.Monad.Bayes.Inference.SMC import Control.Monad.Bayes.Population as Pop+import Control.Monad.Bayes.Sequential import Control.Monad.Bayes.Traced-import Control.Monad.Bayes.Inference.SMC+import Control.Monad.Trans (lift)+import Numeric.Log -- | Particle Marginal Metropolis-Hastings sampling.-pmmh :: MonadInfer m- => Int -- ^ number of Metropolis-Hastings steps- -> Int -- ^ number of time steps- -> Int -- ^ number of particles- -> Traced m b -- ^ model parameters prior- -> (b -> Sequential (Population m) a) -- ^ model- -> m [[(a, Log Double)]]+pmmh ::+ MonadInfer m =>+ -- | number of Metropolis-Hastings steps+ Int ->+ -- | number of time steps+ Int ->+ -- | number of particles+ Int ->+ -- | model parameters prior+ Traced m b ->+ -- | model+ (b -> Sequential (Population m) a) ->+ m [[(a, Log Double)]] pmmh t k n param model = mh t (param >>= runPopulation . pushEvidence . Pop.hoist lift . smcSystematic k n . model)
src/Control/Monad/Bayes/Inference/RMSMC.hs view
@@ -1,69 +1,83 @@-{-|-Module : Control.Monad.Bayes.Inference.RMSMC-Description : Resample-Move Sequential Monte Carlo (RM-SMC)-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--Resample-move Sequential Monte Carlo (RM-SMC) sampling.--Walter Gilks and Carlo Berzuini. 2001. Following a moving target - Monte Carlo inference for dynamic Bayesian models. /Journal of the Royal Statistical Society/ 63 (2001), 127-146. <http://www.mathcs.emory.edu/~whalen/Papers/BNs/MonteCarlo-DBNs.pdf>--}--module Control.Monad.Bayes.Inference.RMSMC (- rmsmc,- rmsmcLocal,- rmsmcBasic-) where+-- |+-- Module : Control.Monad.Bayes.Inference.RMSMC+-- Description : Resample-Move Sequential Monte Carlo (RM-SMC)+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- Resample-move Sequential Monte Carlo (RM-SMC) sampling.+--+-- Walter Gilks and Carlo Berzuini. 2001. Following a moving target - Monte Carlo inference for dynamic Bayesian models. /Journal of the Royal Statistical Society/ 63 (2001), 127-146. <http://www.mathcs.emory.edu/~whalen/Papers/BNs/MonteCarlo-DBNs.pdf>+module Control.Monad.Bayes.Inference.RMSMC+ ( rmsmc,+ rmsmcLocal,+ rmsmcBasic,+ )+where import Control.Monad.Bayes.Class+import Control.Monad.Bayes.Helpers import Control.Monad.Bayes.Population import Control.Monad.Bayes.Sequential as Seq import Control.Monad.Bayes.Traced as Tr-import qualified Control.Monad.Bayes.Traced.Dynamic as TrDyn import qualified Control.Monad.Bayes.Traced.Basic as TrBas-import Control.Monad.Bayes.Helpers+import qualified Control.Monad.Bayes.Traced.Dynamic as TrDyn -- | Resample-move Sequential Monte Carlo.-rmsmc :: MonadSample m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Int -- ^ number of Metropolis-Hastings transitions after each resampling- -> Sequential (Traced (Population m)) a -- ^ model- -> Population m a+rmsmc ::+ MonadSample m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | number of Metropolis-Hastings transitions after each resampling+ Int ->+ -- | model+ Sequential (Traced (Population m)) a ->+ Population m a rmsmc k n t =- marginal .- sis (composeCopies t mhStep . hoistT resampleSystematic) k .- hoistS (hoistT (spawn n >>))+ marginal+ . sis (composeCopies t mhStep . hoistT resampleSystematic) k+ . hoistS (hoistT (spawn n >>)) -- | Resample-move Sequential Monte Carlo with a more efficient -- tracing representation.-rmsmcBasic :: MonadSample m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Int -- ^ number of Metropolis-Hastings transitions after each resampling- -> Sequential (TrBas.Traced (Population m)) a -- ^ model- -> Population m a+rmsmcBasic ::+ MonadSample m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | number of Metropolis-Hastings transitions after each resampling+ Int ->+ -- | model+ Sequential (TrBas.Traced (Population m)) a ->+ Population m a rmsmcBasic k n t =- TrBas.marginal .- sis (composeCopies t TrBas.mhStep . TrBas.hoistT resampleSystematic) k .- hoistS (TrBas.hoistT (spawn n >>))+ TrBas.marginal+ . sis (composeCopies t TrBas.mhStep . TrBas.hoistT resampleSystematic) k+ . hoistS (TrBas.hoistT (spawn n >>)) -- | A variant of resample-move Sequential Monte Carlo -- where only random variables since last resampling are considered -- for rejuvenation.-rmsmcLocal :: MonadSample m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Int -- ^ number of Metropolis-Hastings transitions after each resampling- -> Sequential (TrDyn.Traced (Population m)) a -- ^ model- -> Population m a+rmsmcLocal ::+ MonadSample m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | number of Metropolis-Hastings transitions after each resampling+ Int ->+ -- | model+ Sequential (TrDyn.Traced (Population m)) a ->+ Population m a rmsmcLocal k n t =- TrDyn.marginal .- sis (TrDyn.freeze . composeCopies t TrDyn.mhStep . TrDyn.hoistT resampleSystematic) k .- hoistS (TrDyn.hoistT (spawn n >>))+ TrDyn.marginal+ . sis (TrDyn.freeze . composeCopies t TrDyn.mhStep . TrDyn.hoistT resampleSystematic) k+ . hoistS (TrDyn.hoistT (spawn n >>)) -- | Apply a function a given number of times. composeCopies :: Int -> (a -> a) -> (a -> a)
src/Control/Monad/Bayes/Inference/SMC.hs view
@@ -1,24 +1,23 @@-{-|-Module : Control.Monad.Bayes.Inference.SMC-Description : Sequential Monte Carlo (SMC)-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--Sequential Monte Carlo (SMC) sampling.--Arnaud Doucet and Adam M. Johansen. 2011. A tutorial on particle filtering and smoothing: fifteen years later. In /The Oxford Handbook of Nonlinear Filtering/, Dan Crisan and Boris Rozovskii (Eds.). Oxford University Press, Chapter 8.--}--module Control.Monad.Bayes.Inference.SMC (- sir,- smcMultinomial,- smcSystematic,- smcMultinomialPush,- smcSystematicPush-) where+-- |+-- Module : Control.Monad.Bayes.Inference.SMC+-- Description : Sequential Monte Carlo (SMC)+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- Sequential Monte Carlo (SMC) sampling.+--+-- Arnaud Doucet and Adam M. Johansen. 2011. A tutorial on particle filtering and smoothing: fifteen years later. In /The Oxford Handbook of Nonlinear Filtering/, Dan Crisan and Boris Rozovskii (Eds.). Oxford University Press, Chapter 8.+module Control.Monad.Bayes.Inference.SMC+ ( sir,+ smcMultinomial,+ smcSystematic,+ smcMultinomialPush,+ smcSystematicPush,+ )+where import Control.Monad.Bayes.Class import Control.Monad.Bayes.Population@@ -26,48 +25,69 @@ -- | Sequential importance resampling. -- Basically an SMC template that takes a custom resampler.-sir :: Monad m- => (forall x. Population m x -> Population m x) -- ^ resampler- -> Int -- ^ number of timesteps- -> Int -- ^ population size- -> Sequential (Population m) a -- ^ model- -> Population m a+sir ::+ Monad m =>+ -- | resampler+ (forall x. Population m x -> Population m x) ->+ -- | number of timesteps+ Int ->+ -- | population size+ Int ->+ -- | model+ Sequential (Population m) a ->+ Population m a sir resampler k n = sis resampler k . Seq.hoistFirst (spawn n >>) -- | Sequential Monte Carlo with multinomial resampling at each timestep. -- Weights are not normalized.-smcMultinomial :: MonadSample m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Sequential (Population m) a -- ^ model- -> Population m a+smcMultinomial ::+ MonadSample m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | model+ Sequential (Population m) a ->+ Population m a smcMultinomial = sir resampleMultinomial -- | Sequential Monte Carlo with systematic resampling at each timestep. -- Weights are not normalized.-smcSystematic :: MonadSample m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Sequential (Population m) a -- ^ model- -> Population m a+smcSystematic ::+ MonadSample m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | model+ Sequential (Population m) a ->+ Population m a smcSystematic = sir resampleSystematic -- | Sequential Monte Carlo with multinomial resampling at each timestep. -- Weights are normalized at each timestep and the total weight is pushed -- as a score into the transformed monad.-smcMultinomialPush :: MonadInfer m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Sequential (Population m) a -- ^ model- -> Population m a+smcMultinomialPush ::+ MonadInfer m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | model+ Sequential (Population m) a ->+ Population m a smcMultinomialPush = sir (pushEvidence . resampleMultinomial) -- | Sequential Monte Carlo with systematic resampling at each timestep. -- Weights are normalized at each timestep and the total weight is pushed -- as a score into the transformed monad.-smcSystematicPush :: MonadInfer m- => Int -- ^ number of timesteps- -> Int -- ^ number of particles- -> Sequential (Population m) a -- ^ model- -> Population m a+smcSystematicPush ::+ MonadInfer m =>+ -- | number of timesteps+ Int ->+ -- | number of particles+ Int ->+ -- | model+ Sequential (Population m) a ->+ Population m a smcSystematicPush = sir (pushEvidence . resampleSystematic)
src/Control/Monad/Bayes/Inference/SMC2.hs view
@@ -1,33 +1,32 @@-{-|-Module : Control.Monad.Bayes.Inference.SMC2-Description : Sequential Monte Carlo squared (SMC²)-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--Sequential Monte Carlo squared (SMC²) sampling.--Nicolas Chopin, Pierre E. Jacob, and Omiros Papaspiliopoulos. 2013. SMC²: an efficient algorithm for sequential analysis of state space models. /Journal of the Royal Statistical Society Series B: Statistical Methodology/ 75 (2013), 397-426. Issue 3. <https://doi.org/10.1111/j.1467-9868.2012.01046.x>--}--module Control.Monad.Bayes.Inference.SMC2 (- smc2-) where--import Numeric.Log-import Control.Monad.Trans+-- |+-- Module : Control.Monad.Bayes.Inference.SMC2+-- Description : Sequential Monte Carlo squared (SMC²)+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- Sequential Monte Carlo squared (SMC²) sampling.+--+-- Nicolas Chopin, Pierre E. Jacob, and Omiros Papaspiliopoulos. 2013. SMC²: an efficient algorithm for sequential analysis of state space models. /Journal of the Royal Statistical Society Series B: Statistical Methodology/ 75 (2013), 397-426. Issue 3. <https://doi.org/10.1111/j.1467-9868.2012.01046.x>+module Control.Monad.Bayes.Inference.SMC2+ ( smc2,+ )+where import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Population as Pop-import Control.Monad.Bayes.Inference.SMC-import Control.Monad.Bayes.Inference.RMSMC import Control.Monad.Bayes.Helpers+import Control.Monad.Bayes.Inference.RMSMC+import Control.Monad.Bayes.Inference.SMC+import Control.Monad.Bayes.Population as Pop+import Control.Monad.Trans+import Numeric.Log -- | Helper monad transformer for preprocessing the model for 'smc2'. newtype SMC2 m a = SMC2 (S (T (P m)) a)- deriving(Functor,Applicative,Monad)+ deriving (Functor, Applicative, Monad)+ setup :: SMC2 m a -> S (T (P m)) a setup (SMC2 m) = m @@ -43,13 +42,20 @@ instance MonadSample m => MonadInfer (SMC2 m) -- | Sequential Monte Carlo squared.-smc2 :: MonadSample m- => Int -- ^ number of time steps- -> Int -- ^ number of inner particles- -> Int -- ^ number of outer particles- -> Int -- ^ number of MH transitions- -> S (T (P m)) b -- ^ model parameters- -> ( b -> S (P (SMC2 m)) a) -- ^ model- -> P m [(a, Log Double)]+smc2 ::+ MonadSample m =>+ -- | number of time steps+ Int ->+ -- | number of inner particles+ Int ->+ -- | number of outer particles+ Int ->+ -- | number of MH transitions+ Int ->+ -- | model parameters+ S (T (P m)) b ->+ -- | model+ (b -> S (P (SMC2 m)) a) ->+ P m [(a, Log Double)] smc2 k n p t param model = rmsmc k p t (param >>= setup . runPopulation . smcSystematicPush k n . model)
src/Control/Monad/Bayes/Population.hs view
@@ -1,17 +1,15 @@-{-|-Module : Control.Monad.Bayes.Population-Description : Representation of distributions using multiple samples-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--'Population' turns a single sample into a collection of weighted samples.--}--module Control.Monad.Bayes.Population (- Population,+-- |+-- Module : Control.Monad.Bayes.Population+-- Description : Representation of distributions using multiple samples+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- 'Population' turns a single sample into a collection of weighted samples.+module Control.Monad.Bayes.Population+ ( Population, runPopulation, explicitPopulation, fromWeightedList,@@ -27,26 +25,24 @@ normalize, popAvg, flatten,- hoist- ) where--import Prelude hiding (sum, all)+ hoist,+ )+where import Control.Arrow (second)+import Control.Monad (replicateM)+import Control.Monad.Bayes.Class+import Control.Monad.Bayes.Weighted hiding (flatten, hoist) import Control.Monad.Trans import Control.Monad.Trans.List-import Control.Monad (replicateM)- import qualified Data.List import qualified Data.Vector as V- import Numeric.Log-import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Weighted hiding (flatten, hoist)+import Prelude hiding (all, sum) -- | A collection of weighted samples, or particles. newtype Population m a = Population (Weighted (ListT m) a)- deriving(Functor,Applicative,Monad,MonadIO,MonadSample,MonadCond,MonadInfer)+ deriving (Functor, Applicative, Monad, MonadIO, MonadSample, MonadCond, MonadInfer) instance MonadTrans Population where lift = Population . lift . lift@@ -61,7 +57,7 @@ explicitPopulation = fmap (map (second (exp . ln))) . runPopulation -- | Initialize 'Population' with a concrete weighted sample.-fromWeightedList :: Monad m => m [(a,Log Double)] -> Population m a+fromWeightedList :: Monad m => m [(a, Log Double)] -> Population m a fromWeightedList = Population . withWeight . ListT -- | Increase the sample size by a given factor.@@ -71,41 +67,46 @@ spawn :: Monad m => Int -> Population m () spawn n = fromWeightedList $ pure $ replicate n ((), 1 / fromIntegral n) -resampleGeneric :: MonadSample m- => (V.Vector Double -> m [Int]) -- ^ resampler- -> Population m a -> Population m a+resampleGeneric ::+ MonadSample m =>+ -- | resampler+ (V.Vector Double -> m [Int]) ->+ Population m a ->+ Population m a resampleGeneric resampler m = fromWeightedList $ do pop <- runPopulation m let (xs, ps) = unzip pop let n = length xs let z = sum ps- if z > 0 then do- let weights = V.fromList (map (exp . ln . (/z)) ps)- ancestors <- resampler weights- let xvec = V.fromList xs- let offsprings = map (xvec V.!) ancestors- return $ map (, z / fromIntegral n) offsprings- else- -- if all weights are zero do not resample- return pop+ if z > 0+ then do+ let weights = V.fromList (map (exp . ln . (/ z)) ps)+ ancestors <- resampler weights+ let xvec = V.fromList xs+ let offsprings = map (xvec V.!) ancestors+ return $ map (,z / fromIntegral n) offsprings+ else-- if all weights are zero do not resample+ return pop -- | Systematic resampling helper. systematic :: Double -> V.Vector Double -> [Int]-systematic u ps = f 0 (u / fromIntegral n) 0 0 [] where- prob i = ps V.! i- n = length ps- inc = 1 / fromIntegral n- f i _ _ _ acc | i == n = acc- f i v j q acc =- if v < q then- f (i+1) (v+inc) j q (j-1:acc)- else- f i v (j + 1) (q + prob j) acc+systematic u ps = f 0 (u / fromIntegral n) 0 0 []+ where+ prob i = ps V.! i+ n = length ps+ inc = 1 / fromIntegral n+ f i _ _ _ acc | i == n = acc+ f i v j q acc =+ if v < q+ then f (i + 1) (v + inc) j q (j -1 : acc)+ else f i v (j + 1) (q + prob j) acc -- | Resample the population using the underlying monad and a systematic resampling scheme. -- The total weight is preserved.-resampleSystematic :: (MonadSample m)- => Population m a -> Population m a+resampleSystematic ::+ (MonadSample m) =>+ Population m a ->+ Population m a resampleSystematic = resampleGeneric (\ps -> (`systematic` ps) <$> random) -- | Multinomial resampler.@@ -114,14 +115,18 @@ -- | Resample the population using the underlying monad and a multinomial resampling scheme. -- The total weight is preserved.-resampleMultinomial :: (MonadSample m)- => Population m a -> Population m a+resampleMultinomial ::+ (MonadSample m) =>+ Population m a ->+ Population m a resampleMultinomial = resampleGeneric multinomial -- | Separate the sum of weights into the 'Weighted' transformer. -- Weights are normalized after this operation.-extractEvidence :: Monad m- => Population m a -> Population (Weighted m) a+extractEvidence ::+ Monad m =>+ Population m a ->+ Population (Weighted m) a extractEvidence m = fromWeightedList $ do pop <- lift $ runPopulation m let (xs, ps) = unzip pop@@ -132,14 +137,18 @@ -- | Push the evidence estimator as a score to the transformed monad. -- Weights are normalized after this operation.-pushEvidence :: MonadCond m- => Population m a -> Population m a+pushEvidence ::+ MonadCond m =>+ Population m a ->+ Population m a pushEvidence = hoist applyWeight . extractEvidence -- | A properly weighted single sample, that is one picked at random according -- to the weights, with the sum of all weights.-proper :: (MonadSample m)- => Population m a -> Weighted m a+proper ::+ (MonadSample m) =>+ Population m a ->+ Weighted m a proper m = do pop <- runPopulation $ extractEvidence m let (xs, ps) = unzip pop@@ -155,13 +164,18 @@ -- in the transformed monad. -- This way a single sample can be selected from a population without -- introducing bias.-collapse :: (MonadInfer m)- => Population m a -> m a+collapse ::+ (MonadInfer m) =>+ Population m a ->+ m a collapse = applyWeight . proper -- | Applies a random transformation to a population.-mapPopulation :: (Monad m) => ([(a, Log Double)] -> m [(a, Log Double)]) ->- Population m a -> Population m a+mapPopulation ::+ (Monad m) =>+ ([(a, Log Double)] -> m [(a, Log Double)]) ->+ Population m a ->+ Population m a mapPopulation f m = fromWeightedList $ runPopulation m >>= f -- | Normalizes the weights in the population so that their sum is 1.@@ -173,20 +187,24 @@ popAvg :: (Monad m) => (a -> Double) -> Population m a -> m Double popAvg f p = do xs <- explicitPopulation p- let ys = map (\(x,w) -> f x * w) xs+ let ys = map (\(x, w) -> f x * w) xs let t = Data.List.sum ys return t -- | Combine a population of populations into a single population. flatten :: Monad m => Population (Population m) a -> Population m a-flatten m = Population $ withWeight $ ListT t where- t = f <$> (runPopulation . runPopulation) m- f d = do- (x,p) <- d- (y,q) <- x- return (y, p*q)+flatten m = Population $ withWeight $ ListT t+ where+ t = f <$> (runPopulation . runPopulation) m+ f d = do+ (x, p) <- d+ (y, q) <- x+ return (y, p * q) -- | Applies a transformation to the inner monad.-hoist :: (Monad m, Monad n)- => (forall x. m x -> n x) -> Population m a -> Population n a+hoist ::+ (Monad m, Monad n) =>+ (forall x. m x -> n x) ->+ Population m a ->+ Population n a hoist f = fromWeightedList . f . runPopulation
src/Control/Monad/Bayes/Sampler.hs view
@@ -1,40 +1,38 @@-{-|-Module : Control.Monad.Bayes.Sampler-Description : Pseudo-random sampling monads-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--'SamplerIO' and 'SamplerST' are instances of 'MonadSample'. Apply a 'MonadCond'-transformer to obtain a 'MonadInfer' that can execute probabilistic models.--}--module Control.Monad.Bayes.Sampler (- SamplerIO,+-- |+-- Module : Control.Monad.Bayes.Sampler+-- Description : Pseudo-random sampling monads+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- 'SamplerIO' and 'SamplerST' are instances of 'MonadSample'. Apply a 'MonadCond'+-- transformer to obtain a 'MonadInfer' that can execute probabilistic models.+module Control.Monad.Bayes.Sampler+ ( SamplerIO, sampleIO, sampleIOfixed, sampleIOwith, Seed,- SamplerST(SamplerST),+ SamplerST (SamplerST), runSamplerST, sampleST,- sampleSTfixed- ) where+ sampleSTfixed,+ )+where +import Control.Monad.Bayes.Class import Control.Monad.ST (ST, runST, stToIO)+import Control.Monad.State (State, state)+import Control.Monad.Trans (MonadIO, lift)+import Control.Monad.Trans.Reader (ReaderT, ask, mapReaderT, runReaderT) import System.Random.MWC import qualified System.Random.MWC.Distributions as MWC-import Control.Monad.State (State, state)-import Control.Monad.Trans (lift, MonadIO)-import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask, mapReaderT) -import Control.Monad.Bayes.Class- -- | An 'IO' based random sampler using the MWC-Random package. newtype SamplerIO a = SamplerIO (ReaderT GenIO IO a)- deriving(Functor, Applicative, Monad, MonadIO)+ deriving (Functor, Applicative, Monad, MonadIO) -- | Initialize a pseudo-random number generator using randomness supplied by -- the operating system.@@ -58,9 +56,6 @@ instance MonadSample SamplerIO where random = fromSamplerST random --- -- | An 'ST' based random sampler using the @mwc-random@ package. newtype SamplerST a = SamplerST (forall s. ReaderT (GenST s) (ST s) a) @@ -100,7 +95,7 @@ instance MonadSample SamplerST where random = fromMWC System.Random.MWC.uniform - uniform a b = fromMWC $ uniformR (a,b)+ uniform a b = fromMWC $ uniformR (a, b) normal m s = fromMWC $ MWC.normal m s gamma shape scale = fromMWC $ MWC.gamma shape scale beta a b = fromMWC $ MWC.beta a b
src/Control/Monad/Bayes/Sequential.hs view
@@ -1,40 +1,38 @@-{-|-Module : Control.Monad.Bayes.Sequential-Description : Suspendable probabilistic computation-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--'Sequential' represents a computation that can be suspended.--}--module Control.Monad.Bayes.Sequential (- Sequential,+-- |+-- Module : Control.Monad.Bayes.Sequential+-- Description : Suspendable probabilistic computation+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- 'Sequential' represents a computation that can be suspended.+module Control.Monad.Bayes.Sequential+ ( Sequential, suspend, finish, advance, finished, hoistFirst, hoist,- sis- ) where+ sis,+ )+where -import Control.Monad.Trans+import Control.Monad.Bayes.Class import Control.Monad.Coroutine hiding (suspend) import Control.Monad.Coroutine.SuspensionFunctors+import Control.Monad.Trans import Data.Either -import Control.Monad.Bayes.Class- -- | Represents a computation that can be suspended at certain points. -- The intermediate monadic effects can be extracted, which is particularly -- useful for implementation of Sequential Monte Carlo related methods. -- All the probabilistic effects are lifted from the transformed monad, but -- also `suspend` is inserted after each `factor`. newtype Sequential m a = Sequential {runSequential :: Coroutine (Await ()) m a}- deriving(Functor,Applicative,Monad,MonadTrans,MonadIO)+ deriving (Functor, Applicative, Monad, MonadTrans, MonadIO) extract :: Await () a -> a extract (Await f) = f ()@@ -76,8 +74,11 @@ -- | Transform the inner monad. -- The transformation is applied recursively through all the suspension points.-hoist :: (Monad m, Monad n) =>- (forall x. m x -> n x) -> Sequential m a -> Sequential n a+hoist ::+ (Monad m, Monad n) =>+ (forall x. m x -> n x) ->+ Sequential m a ->+ Sequential n a hoist f = Sequential . mapMonad f . runSequential -- | Apply a function a given number of times.@@ -86,9 +87,12 @@ -- | Sequential importance sampling. -- Applies a given transformation after each time step.-sis :: Monad m- => (forall x. m x -> m x) -- ^ transformation- -> Int -- ^ number of time steps- -> Sequential m a- -> m a+sis ::+ Monad m =>+ -- | transformation+ (forall x. m x -> m x) ->+ -- | number of time steps+ Int ->+ Sequential m a ->+ m a sis f k = finish . composeCopies k (advance . hoistFirst f)
src/Control/Monad/Bayes/Traced.hs view
@@ -1,16 +1,14 @@-{-|-Module : Control.Monad.Bayes.Traced-Description : Distributions on execution traces-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Traced (- module Control.Monad.Bayes.Traced.Static-) where+-- |+-- Module : Control.Monad.Bayes.Traced+-- Description : Distributions on execution traces+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Traced+ ( module Control.Monad.Bayes.Traced.Static,+ )+where import Control.Monad.Bayes.Traced.Static
src/Control/Monad/Bayes/Traced/Basic.hs view
@@ -1,41 +1,35 @@-{-|-Module : Control.Monad.Bayes.Traced.Basic-Description : Distributions on full execution traces of full programs-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Traced.Basic (- Traced,- hoistT,- marginal,- mhStep,- mh-) where+-- |+-- Module : Control.Monad.Bayes.Traced.Basic+-- Description : Distributions on full execution traces of full programs+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Traced.Basic+ ( Traced,+ hoistT,+ marginal,+ mhStep,+ mh,+ )+where -import Data.Functor.Identity import Control.Applicative (liftA2)- import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Weighted as Weighted-import Control.Monad.Bayes.Free as FreeSampler-+import Control.Monad.Bayes.Free (FreeSampler) import Control.Monad.Bayes.Traced.Common+import Control.Monad.Bayes.Weighted (Weighted)+import Data.Functor.Identity (Identity) -- | Tracing monad that records random choices made in the program.--- The first component is used to run the program with a modified trace,--- while the second records a trace and an output value from a run.-data Traced m a = Traced (Weighted (FreeSampler Identity) a) (m (Trace a))--traceDist :: Traced m a -> m (Trace a)-traceDist (Traced _ d) = d--model :: Traced m a -> Weighted (FreeSampler Identity) a-model (Traced m _) = m+data Traced m a+ = Traced+ { -- | Run the program with a modified trace.+ model :: Weighted (FreeSampler Identity) a,+ -- | Record trace and output.+ traceDist :: m (Trace a)+ } instance Monad m => Functor (Traced m) where fmap f (Traced m d) = Traced (fmap f m) (fmap (fmap f) d)@@ -45,9 +39,10 @@ (Traced mf df) <*> (Traced mx dx) = Traced (mf <*> mx) (liftA2 (<*>) df dx) instance Monad m => Monad (Traced m) where- (Traced mx dx) >>= f = Traced my dy where- my = mx >>= model . f- dy = dx `bind` (traceDist . f)+ (Traced mx dx) >>= f = Traced my dy+ where+ my = mx >>= model . f+ dy = dx `bind` (traceDist . f) instance MonadSample m => MonadSample (Traced m) where random = Traced random (fmap singleton random)@@ -64,18 +59,19 @@ marginal :: Monad m => Traced m a -> m a marginal (Traced _ d) = fmap output d --- | A single step of the Trace MH algorithm.+-- | A single step of the Trace Metropolis-Hastings algorithm. mhStep :: MonadSample m => Traced m a -> Traced m a-mhStep (Traced m d) = Traced m d' where- d' = d >>= mhTrans' m+mhStep (Traced m d) = Traced m d'+ where+ d' = d >>= mhTrans' m --- | Full run of the Trace MH algorithm with a specified+-- | Full run of the Trace Metropolis-Hastings algorithm with a specified -- number of steps. mh :: MonadSample m => Int -> Traced m a -> m [a]-mh n (Traced m d) = fmap (map output) t where- t = f n- f 0 = fmap (:[]) d- f k = do- ~(x:xs) <- f (k-1)- y <- mhTrans' m x- return (y:x:xs)+mh n (Traced m d) = fmap (map output) (f n)+ where+ f 0 = fmap (: []) d+ f k = do+ ~(x : xs) <- f (k -1)+ y <- mhTrans' m x+ return (y : x : xs)
src/Control/Monad/Bayes/Traced/Common.hs view
@@ -1,52 +1,57 @@-{-|-Module : Control.Monad.Bayes.Traced.Common-Description : Numeric code for Trace MCMC-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Traced.Common (- Trace,- singleton,- output,- scored,- bind,- mhTrans,- mhTrans'-) where+-- |+-- Module : Control.Monad.Bayes.Traced.Common+-- Description : Numeric code for Trace MCMC+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Traced.Common+ ( Trace,+ singleton,+ output,+ scored,+ bind,+ mhTrans,+ mhTrans',+ )+where +import Control.Monad.Bayes.Class+import Control.Monad.Bayes.Free as FreeSampler+import Control.Monad.Bayes.Weighted as Weighted import Control.Monad.Trans.Writer-import qualified Data.Vector as V import Data.Functor.Identity- import Numeric.Log (Log, ln)--import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Weighted as Weighted-import Control.Monad.Bayes.Free as FreeSampler+import Statistics.Distribution.DiscreteUniform (discreteUniformAB) -data Trace a =- Trace {- variables :: [Double],- output :: a,- density :: Log Double- }+-- | Collection of random variables sampled during the program's execution.+data Trace a+ = Trace+ { -- | Sequence of random variables sampled during the program's execution.+ variables :: [Double],+ -- |+ output :: a,+ -- | The probability of observing this particular sequence.+ density :: Log Double+ } instance Functor Trace where fmap f t = t {output = f (output t)} instance Applicative Trace where pure x = Trace {variables = [], output = x, density = 1}- tf <*> tx = Trace {variables = variables tf ++ variables tx, output = output tf (output tx), density = density tf * density tx}+ tf <*> tx =+ Trace+ { variables = variables tf ++ variables tx,+ output = output tf (output tx),+ density = density tf * density tx+ } instance Monad Trace where t >>= f =- let t' = f (output t) in- t' {variables = variables t ++ variables t', density = density t * density t'}+ let t' = f (output t)+ in t' {variables = variables t ++ variables t', density = density t * density t'} singleton :: Double -> Trace Double singleton u = Trace {variables = [u], output = u, density = 1}@@ -62,17 +67,15 @@ -- | A single Metropolis-corrected transition of single-site Trace MCMC. mhTrans :: MonadSample m => Weighted (FreeSampler m) a -> Trace a -> m (Trace a)-mhTrans m t = do- let us = variables t- p = density t+mhTrans m t@Trace {variables = us, density = p} = do+ let n = length us us' <- do- let n = length us- i <- categorical $ V.replicate n (1 / fromIntegral n)+ i <- discrete $ discreteUniformAB 0 (n -1) u' <- random- let (xs, _:ys) = splitAt i us- return $ xs ++ (u':ys)+ let (xs, _ : ys) = splitAt i us+ return $ xs ++ (u' : ys) ((b, q), vs) <- runWriterT $ runWeighted $ Weighted.hoist (WriterT . withPartialRandomness us') m- let ratio = (exp . ln) $ min 1 (q * fromIntegral (length us) / (p * fromIntegral (length vs)))+ let ratio = (exp . ln) $ min 1 (q * fromIntegral n / (p * fromIntegral (length vs))) accept <- bernoulli ratio return $ if accept then Trace vs b q else t
src/Control/Monad/Bayes/Traced/Dynamic.hs view
@@ -1,37 +1,31 @@-{-|-Module : Control.Monad.Bayes.Traced.Dynamic-Description : Distributions on execution traces that can be dynamically frozen-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Traced.Dynamic (- Traced,- hoistT,- marginal,- freeze,- mhStep,- mh-) where+-- |+-- Module : Control.Monad.Bayes.Traced.Dynamic+-- Description : Distributions on execution traces that can be dynamically frozen+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Traced.Dynamic+ ( Traced,+ hoistT,+ marginal,+ freeze,+ mhStep,+ mh,+ )+where import Control.Monad (join)-import Control.Monad.Trans- import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Weighted as Weighted-import Control.Monad.Bayes.Free as FreeSampler-+import Control.Monad.Bayes.Free (FreeSampler) import Control.Monad.Bayes.Traced.Common+import Control.Monad.Bayes.Weighted (Weighted)+import Control.Monad.Trans (MonadTrans (..)) --- | A tracing monad where only a subset of random choices are traced--- and this subset can be adjusted dynamically.-newtype Traced m a = Traced (m (Weighted (FreeSampler m) a, Trace a))-runTraced :: Traced m a -> m (Weighted (FreeSampler m) a, Trace a)-runTraced (Traced c) = c+-- | A tracing monad where only a subset of random choices are traced and this+-- subset can be adjusted dynamically.+newtype Traced m a = Traced {runTraced :: m (Weighted (FreeSampler m) a, Trace a)} pushM :: Monad m => m (Weighted (FreeSampler m) a) -> Weighted (FreeSampler m) a pushM = join . lift . lift@@ -71,31 +65,35 @@ hoistT :: (forall x. m x -> m x) -> Traced m a -> Traced m a hoistT f (Traced c) = Traced (f c) +-- | Discard the trace and supporting infrastructure. marginal :: Monad m => Traced m a -> m a marginal (Traced c) = fmap (output . snd) c --- | Freeze all traced random choices to their current--- values and stop tracing them.+-- | Freeze all traced random choices to their current values and stop tracing+-- them. freeze :: Monad m => Traced m a -> Traced m a freeze (Traced c) = Traced $ do (_, t) <- c let x = output t return (return x, pure x) +-- | A single step of the Trace Metropolis-Hastings algorithm. mhStep :: MonadSample m => Traced m a -> Traced m a mhStep (Traced c) = Traced $ do (m, t) <- c t' <- mhTrans m t return (m, t') +-- | Full run of the Trace Metropolis-Hastings algorithm with a specified+-- number of steps. mh :: MonadSample m => Int -> Traced m a -> m [a] mh n (Traced c) = do- (m,t) <- c+ (m, t) <- c let f 0 = return [t] f k = do- ~(x:xs) <- f (k-1)+ ~(x : xs) <- f (k -1) y <- mhTrans m x- return (y:x:xs)+ return (y : x : xs) ts <- f n let xs = map output ts return xs
src/Control/Monad/Bayes/Traced/Static.hs view
@@ -1,41 +1,36 @@-{-|-Module : Control.Monad.Bayes.Traced.Static-Description : Distributions on execution traces of full programs-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC---}--module Control.Monad.Bayes.Traced.Static (- Traced,- hoistT,- marginal,- mhStep,- mh-) where+-- |+-- Module : Control.Monad.Bayes.Traced.Static+-- Description : Distributions on execution traces of full programs+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+module Control.Monad.Bayes.Traced.Static+ ( Traced,+ hoistT,+ marginal,+ mhStep,+ mh,+ )+where -import Control.Monad.Trans import Control.Applicative (liftA2)- import Control.Monad.Bayes.Class-import Control.Monad.Bayes.Weighted as Weighted-import Control.Monad.Bayes.Free as FreeSampler-+import Control.Monad.Bayes.Free (FreeSampler) import Control.Monad.Bayes.Traced.Common+import Control.Monad.Bayes.Weighted (Weighted)+import Control.Monad.Trans (MonadTrans (..)) -- | A tracing monad where only a subset of random choices are traced.--- The random choices that are not to be traced should be lifted--- from the transformed monad.-data Traced m a = Traced (Weighted (FreeSampler m) a) (m (Trace a))--traceDist :: Traced m a -> m (Trace a)-traceDist (Traced _ d) = d--model :: Traced m a -> Weighted (FreeSampler m) a-model (Traced m _) = m+--+-- The random choices that are not to be traced should be lifted from the+-- transformed monad.+data Traced m a+ = Traced+ { model :: Weighted (FreeSampler m) a,+ traceDist :: m (Trace a)+ } instance Monad m => Functor (Traced m) where fmap f (Traced m d) = Traced (fmap f m) (fmap (fmap f) d)@@ -45,9 +40,10 @@ (Traced mf df) <*> (Traced mx dx) = Traced (mf <*> mx) (liftA2 (<*>) df dx) instance Monad m => Monad (Traced m) where- (Traced mx dx) >>= f = Traced my dy where- my = mx >>= model . f- dy = dx `bind` (traceDist . f)+ (Traced mx dx) >>= f = Traced my dy+ where+ my = mx >>= model . f+ dy = dx `bind` (traceDist . f) instance MonadTrans Traced where lift m = Traced (lift $ lift m) (fmap pure m)@@ -63,18 +59,23 @@ hoistT :: (forall x. m x -> m x) -> Traced m a -> Traced m a hoistT f (Traced m d) = Traced m (f d) +-- | Discard the trace and supporting infrastructure. marginal :: Monad m => Traced m a -> m a marginal (Traced _ d) = fmap output d +-- | A single step of the Trace Metropolis-Hastings algorithm. mhStep :: MonadSample m => Traced m a -> Traced m a-mhStep (Traced m d) = Traced m d' where- d' = d >>= mhTrans m+mhStep (Traced m d) = Traced m d'+ where+ d' = d >>= mhTrans m +-- | Full run of the Trace Metropolis-Hastings algorithm with a specified+-- number of steps. mh :: MonadSample m => Int -> Traced m a -> m [a]-mh n (Traced m d) = fmap (map output) t where- t = f n- f 0 = fmap (:[]) d- f k = do- ~(x:xs) <- f (k-1)- y <- mhTrans m x- return (y:x:xs)+mh n (Traced m d) = fmap (map output) (f n)+ where+ f 0 = fmap (: []) d+ f k = do+ ~(x : xs) <- f (k -1)+ y <- mhTrans m x+ return (y : x : xs)
src/Control/Monad/Bayes/Weighted.hs view
@@ -1,18 +1,16 @@-{-|-Module : Control.Monad.Bayes.Weighted-Description : Probability monad accumulating the likelihood-Copyright : (c) Adam Scibior, 2015-2020-License : MIT-Maintainer : leonhard.markert@tweag.io-Stability : experimental-Portability : GHC--'Weighted' is an instance of 'MonadCond'. Apply a 'MonadSample' transformer to-obtain a 'MonadInfer' that can execute probabilistic models.--}--module Control.Monad.Bayes.Weighted (- Weighted,+-- |+-- Module : Control.Monad.Bayes.Weighted+-- Description : Probability monad accumulating the likelihood+-- Copyright : (c) Adam Scibior, 2015-2020+-- License : MIT+-- Maintainer : leonhard.markert@tweag.io+-- Stability : experimental+-- Portability : GHC+--+-- 'Weighted' is an instance of 'MonadCond'. Apply a 'MonadSample' transformer to+-- obtain a 'MonadInfer' that can execute probabilistic models.+module Control.Monad.Bayes.Weighted+ ( Weighted, withWeight, runWeighted, extractWeight,@@ -20,18 +18,18 @@ flatten, applyWeight, hoist,- ) where--import Control.Monad.Trans-import Control.Monad.Trans.State+ )+where -import Numeric.Log import Control.Monad.Bayes.Class+import Control.Monad.Trans (MonadIO, MonadTrans (..))+import Control.Monad.Trans.State (StateT (..), mapStateT, modify)+import Numeric.Log (Log) -- | Execute the program using the prior distribution, while accumulating likelihood. newtype Weighted m a = Weighted (StateT (Log Double) m a)- -- StateT is more efficient than WriterT- deriving(Functor, Applicative, Monad, MonadIO, MonadTrans, MonadSample)+ -- StateT is more efficient than WriterT+ deriving (Functor, Applicative, Monad, MonadIO, MonadTrans, MonadSample) instance Monad m => MonadCond (Weighted m) where score w = Weighted (modify (* w))@@ -42,27 +40,28 @@ runWeighted :: (Functor m) => Weighted m a -> m (a, Log Double) runWeighted (Weighted m) = runStateT m 1 +-- | Compute the sample and discard the weight.+--+-- This operation introduces bias.+prior :: Functor m => Weighted m a -> m a+prior = fmap fst . runWeighted+ -- | Compute the weight and discard the sample. extractWeight :: Functor m => Weighted m a -> m (Log Double)-extractWeight m = snd <$> runWeighted m+extractWeight = fmap snd . runWeighted -- | Embed a random variable with explicitly given likelihood. -- -- > runWeighted . withWeight = id withWeight :: (Monad m) => m (a, Log Double) -> Weighted m a withWeight m = Weighted $ do- (x,w) <- lift m+ (x, w) <- lift m modify (* w) return x --- | Discard the weight.--- This operation introduces bias.-prior :: (Functor m) => Weighted m a -> m a-prior = fmap fst . runWeighted- -- | Combine weights from two different levels. flatten :: Monad m => Weighted (Weighted m) a -> Weighted m a-flatten m = withWeight $ (\((x,p),q) -> (x, p*q)) <$> runWeighted (runWeighted m)+flatten m = withWeight $ (\((x, p), q) -> (x, p * q)) <$> runWeighted (runWeighted m) -- | Use the weight as a factor in the transformed monad. applyWeight :: MonadCond m => Weighted m a -> m a
test/Spec.hs view
@@ -1,18 +1,17 @@ import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck--import qualified TestWeighted import qualified TestEnumerator+import qualified TestInference import qualified TestPopulation import qualified TestSequential-import qualified TestInference-+import qualified TestWeighted main :: IO () main = hspec $ do- describe "Weighted" $- it "accumulates likelihood correctly" $ do+ describe "Weighted"+ $ it "accumulates likelihood correctly"+ $ do passed <- TestWeighted.passed passed `shouldBe` True describe "Dist" $ do@@ -24,7 +23,7 @@ TestEnumerator.passed4 `shouldBe` True describe "Population" $ do context "controlling population" $ do- it "preserves the population when not expicitly altered" $ do+ it "preserves the population when not explicitly altered" $ do popSize <- TestPopulation.popSize popSize `shouldBe` 5 it "multiplies the number of samples when spawn invoked twice" $ do@@ -60,9 +59,9 @@ observations >= 0 && particles >= 1 ==> ioProperty $ do checkParticles <- TestInference.checkParticles observations particles return $ checkParticles == particles- describe "SMC with systematic resampling" $ do- prop "number of particles is equal to its second parameter" $- \observations particles ->- observations >= 0 && particles >= 1 ==> ioProperty $ do- checkParticles <- TestInference.checkParticlesSystematic observations particles- return $ checkParticles == particles+ describe "SMC with systematic resampling"+ $ prop "number of particles is equal to its second parameter"+ $ \observations particles ->+ observations >= 0 && particles >= 1 ==> ioProperty $ do+ checkParticles <- TestInference.checkParticlesSystematic observations particles+ return $ checkParticles == particles
test/TestEnumerator.hs view
@@ -1,31 +1,30 @@ module TestEnumerator where +import Control.Monad.Bayes.Class+import Control.Monad.Bayes.Enumerator import Data.AEq import qualified Data.Vector as V- import Numeric.Log-import Control.Monad.Bayes.Enumerator-import Control.Monad.Bayes.Class import Sprinkler unnorm :: MonadSample m => m Int-unnorm = categorical $ V.fromList [0.5,0.8]+unnorm = categorical $ V.fromList [0.5, 0.8] passed1 :: Bool passed1 = (exp . ln) (evidence unnorm) ~== 1 agg :: MonadSample m => m Int agg = do- x <- uniformD [0,1]- y <- uniformD [2,1]- return (x+y)+ x <- uniformD [0, 1]+ y <- uniformD [2, 1]+ return (x + y) passed2 :: Bool-passed2 = enumerate agg ~== [(1,0.25), (2,0.5), (3,0.25)]+passed2 = enumerate agg ~== [(1, 0.25), (2, 0.5), (3, 0.25)] passed3 :: Bool passed3 = enumerate Sprinkler.hard ~== enumerate Sprinkler.soft passed4 :: Bool passed4 =- expectation (^ (2 :: Int)) (fmap (fromIntegral . (+1)) $ categorical $ V.fromList [0.5, 0.5]) ~== 2.5+ expectation (^ (2 :: Int)) (fmap (fromIntegral . (+ 1)) $ categorical $ V.fromList [0.5, 0.5]) ~== 2.5
test/TestInference.hs view
@@ -1,18 +1,15 @@-{-# LANGUAGE- Rank2Types,- TypeFamilies- #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-} module TestInference where -import Data.AEq-import Numeric.Log- import Control.Monad.Bayes.Class import Control.Monad.Bayes.Enumerator-import Control.Monad.Bayes.Sampler-import Control.Monad.Bayes.Population import Control.Monad.Bayes.Inference.SMC+import Control.Monad.Bayes.Population+import Control.Monad.Bayes.Sampler+import Data.AEq+import Numeric.Log import Sprinkler sprinkler :: MonadInfer m => m Bool@@ -31,5 +28,6 @@ checkTerminateSMC = sampleIOfixed (runPopulation $ smcMultinomial 2 5 sprinkler) checkPreserveSMC :: Bool-checkPreserveSMC = (enumerate . collapse . smcMultinomial 2 2) sprinkler ~==- enumerate sprinkler+checkPreserveSMC =+ (enumerate . collapse . smcMultinomial 2 2) sprinkler+ ~== enumerate sprinkler
test/TestPopulation.hs view
@@ -1,11 +1,10 @@ module TestPopulation where -import Data.AEq- import Control.Monad.Bayes.Class import Control.Monad.Bayes.Enumerator-import Control.Monad.Bayes.Sampler import Control.Monad.Bayes.Population as Population+import Control.Monad.Bayes.Sampler+import Data.AEq import Sprinkler weightedSampleSize :: MonadSample m => Population m a -> m Int@@ -26,18 +25,22 @@ --all_check = (mass (Population.all id (spawn 2 >> sprinkler)) True) ~== 0.09 transCheck1 :: Bool-transCheck1 = enumerate (collapse sprinkler) ~==- sprinklerExact+transCheck1 =+ enumerate (collapse sprinkler)+ ~== sprinklerExact+ transCheck2 :: Bool-transCheck2 = enumerate (collapse (spawn 2 >> sprinkler)) ~==- sprinklerExact+transCheck2 =+ enumerate (collapse (spawn 2 >> sprinkler))+ ~== sprinklerExact resampleCheck :: Int -> Bool resampleCheck n =- (enumerate . collapse . resampleMultinomial) (spawn n >> sprinkler) ~==- sprinklerExact+ (enumerate . collapse . resampleMultinomial) (spawn n >> sprinkler)+ ~== sprinklerExact popAvgCheck :: Bool-popAvgCheck = expectation f Sprinkler.soft ~== expectation id (popAvg f $ pushEvidence Sprinkler.soft) where- f True = 10- f False = 4+popAvgCheck = expectation f Sprinkler.soft ~== expectation id (popAvg f $ pushEvidence Sprinkler.soft)+ where+ f True = 10+ f False = 4
test/TestSequential.hs view
@@ -1,29 +1,29 @@ module TestSequential where -import Data.AEq- import Control.Monad.Bayes.Class import Control.Monad.Bayes.Enumerator as Dist import Control.Monad.Bayes.Sequential+import Data.AEq import Sprinkler twoSync :: MonadInfer m => m Int twoSync = do- x <- uniformD[0,1]+ x <- uniformD [0, 1] factor (fromIntegral x)- y <- uniformD[0,1]+ y <- uniformD [0, 1] factor (fromIntegral y)- return (x+y)+ return (x + y) finishedTwoSync :: MonadInfer m => Int -> m Bool-finishedTwoSync n = finished (run n twoSync) where- run 0 d = d- run k d = run (k-1) (advance d)+finishedTwoSync n = finished (run n twoSync)+ where+ run 0 d = d+ run k d = run (k -1) (advance d) checkTwoSync :: Int -> Bool checkTwoSync 0 = mass (finishedTwoSync 0) False ~== 1 checkTwoSync 1 = mass (finishedTwoSync 1) False ~== 1-checkTwoSync 2 = mass (finishedTwoSync 2) True ~== 1+checkTwoSync 2 = mass (finishedTwoSync 2) True ~== 1 checkTwoSync _ = error "Unexpected argument" sprinkler :: MonadInfer m => m Bool@@ -39,9 +39,10 @@ pFinished _ = error "Unexpected argument" isFinished :: MonadInfer m => Int -> m Bool-isFinished n = finished (run n sprinkler) where- run 0 d = d- run k d = run (k-1) (advance d)+isFinished n = finished (run n sprinkler)+ where+ run 0 d = d+ run k d = run (k -1) (advance d) checkSync :: Int -> Bool checkSync n = mass (isFinished n) True ~== pFinished n
test/TestWeighted.hs view
@@ -1,34 +1,31 @@-{-# LANGUAGE- TypeFamilies- #-}+{-# LANGUAGE TypeFamilies #-} module TestWeighted where -import Data.AEq-import Control.Monad.State-import Data.Bifunctor (second)-import Numeric.Log- import Control.Monad.Bayes.Class import Control.Monad.Bayes.Sampler import Control.Monad.Bayes.Weighted+import Control.Monad.State+import Data.AEq+import Data.Bifunctor (second)+import Numeric.Log -model :: MonadInfer m => m (Int,Double)+model :: MonadInfer m => m (Int, Double) model = do- n <- uniformD [0,1,2]+ n <- uniformD [0, 1, 2] unless (n == 0) (factor 0.5) x <- if n == 0 then return 1 else normal 0 1- when (n == 2) (factor $ (Exp . log) (x*x))- return (n,x)+ when (n == 2) (factor $ (Exp . log) (x * x))+ return (n, x) -result :: MonadSample m => m ((Int,Double), Double)+result :: MonadSample m => m ((Int, Double), Double) result = second (exp . ln) <$> runWeighted model passed :: IO Bool passed = fmap check (sampleIOfixed result) -check :: ((Int,Double), Double) -> Bool-check ((0,1),1) = True-check ((1,_),y) = y ~== 0.5-check ((2,x),y) = y ~== 0.5 * x * x+check :: ((Int, Double), Double) -> Bool+check ((0, 1), 1) = True+check ((1, _), y) = y ~== 0.5+check ((2, x), y) = y ~== 0.5 * x * x check _ = False