diff --git a/benchmark/Single.hs b/benchmark/Single.hs index d7473b4c..3c1f749b 100644 --- a/benchmark/Single.hs +++ b/benchmark/Single.hs @@ -1,92 +1,113 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ImportQualifiedPost #-} -import Control.Monad.Bayes.Class +import Control.Monad.Bayes.Class (MonadInfer) import Control.Monad.Bayes.Inference.MCMC (MCMCConfig (..), Proposal (SingleSiteMH)) -import Control.Monad.Bayes.Inference.RMSMC +import Control.Monad.Bayes.Inference.RMSMC (rmsmcBasic) import Control.Monad.Bayes.Inference.SMC + ( SMCConfig (SMCConfig, numParticles, numSteps, resampler), + smc, + ) import Control.Monad.Bayes.Population -import Control.Monad.Bayes.Population (population) + ( population, + resampleSystematic, + ) import Control.Monad.Bayes.Sampler -import Control.Monad.Bayes.Traced -import Control.Monad.Bayes.Weighted + ( Sampler, + sampleSTfixed, + sampleWith, + ) +import Control.Monad.Bayes.Traced (mh) +import Control.Monad.Bayes.Weighted (unweighted) import Control.Monad.ST (runST) -import Data.Time +import Data.Time (diffUTCTime, getCurrentTime) import HMM qualified import LDA qualified import LogReg qualified import Options.Applicative + ( Applicative (liftA2), + ParserInfo, + auto, + execParser, + fullDesc, + help, + info, + long, + maybeReader, + option, + short, + ) import System.Random.MWC (GenIO, createSystemRandom) -data Model = LR Int | HMM Int | LDA (Int, Int) - deriving stock (Show, Read) +-- data Model = LR Int | HMM Int | LDA (Int, Int) +-- deriving stock (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) - _ -> Nothing +-- 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) +-- _ -> 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 - program (LR n) = show <$> (LogReg.logisticRegression (runST $ sampleSTfixed (LogReg.syntheticData n))) - program (HMM n) = show <$> (HMM.hmm (runST $ sampleSTfixed (HMM.syntheticData n))) - program (LDA (d, w)) = show <$> (LDA.lda (runST $ sampleSTfixed (LDA.syntheticData d w))) +-- 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 +-- program (LR n) = show <$> (LogReg.logisticRegression (runST $ sampleSTfixed (LogReg.syntheticData n))) +-- program (HMM n) = show <$> (HMM.hmm (runST $ sampleSTfixed (HMM.syntheticData n))) +-- program (LDA (d, w)) = show <$> (LDA.lda (runST $ sampleSTfixed (LDA.syntheticData d w))) -data Alg = SMC | MH | RMSMC - deriving stock (Read, Show) +-- data Alg = SMC | MH | RMSMC +-- deriving stock (Read, Show) -runAlg :: Model -> Alg -> Sampler GenIO IO String -runAlg model alg = - case alg of - SMC -> - let n = 100 - (k, m) = getModel model - in show <$> population (smc SMCConfig {numSteps = k, numParticles = n, resampler = resampleSystematic} m) - MH -> - let t = 100 - (_, m) = getModel model - in show <$> unweighted (mh t m) - RMSMC -> - let n = 10 - t = 1 - (k, m) = getModel model - in show <$> population (rmsmcBasic MCMCConfig {numMCMCSteps = t, numBurnIn = 0, proposal = SingleSiteMH} (SMCConfig {numSteps = k, numParticles = n, resampler = resampleSystematic}) m) +-- runAlg :: Model -> Alg -> Sampler GenIO IO String +-- runAlg model alg = +-- case alg of +-- SMC -> +-- let n = 100 +-- (k, m) = getModel model +-- in show <$> population (smc SMCConfig {numSteps = k, numParticles = n, resampler = resampleSystematic} m) +-- MH -> +-- let t = 100 +-- (_, m) = getModel model +-- in show <$> unweighted (mh t m) +-- RMSMC -> +-- let n = 10 +-- t = 1 +-- (k, m) = getModel model +-- in show <$> population (rmsmcBasic MCMCConfig {numMCMCSteps = t, numBurnIn = 0, proposal = SingleSiteMH} (SMCConfig {numSteps = k, numParticles = n, resampler = resampleSystematic}) m) -infer :: Model -> Alg -> IO () -infer model alg = do - g <- createSystemRandom - x <- sampleWith (runAlg model alg) g - print x +-- infer :: Model -> Alg -> IO () +-- infer model alg = do +-- g <- createSystemRandom +-- x <- sampleWith (runAlg model alg) g +-- 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 :: 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" +-- ) -main :: IO () -main = do - (model, alg) <- execParser opts - startTime <- getCurrentTime - infer model alg - endTime <- getCurrentTime - print (diffUTCTime endTime startTime) +-- main :: IO () +-- main = do +-- (model, alg) <- execParser opts +-- startTime <- getCurrentTime +-- infer model alg +-- endTime <- getCurrentTime +-- print (diffUTCTime endTime startTime) diff --git a/docs/source/usage.md b/docs/source/usage.md index 8b4ac610..b7272ccd 100644 --- a/docs/source/usage.md +++ b/docs/source/usage.md @@ -391,6 +391,7 @@ Summary of key info on `Sequential`: - `instance MonadSample m => instance MonadSample (Sequential m)` - `instance MonadCond m => instance MonadCond (Sequential m)` + ```haskell newtype Sequential m a = Sequential {runSequential :: Coroutine (Await ()) m a} @@ -474,34 +475,36 @@ hoistFirst :: (forall x. m x -> m x) -> Sequential m a -> Sequential m a hoistFirst f = Sequential . Coroutine . f . resume . runSequential ``` - - When `m` is `Population n` for some other `n`, then `resampleGeneric` gives us one example of the natural transformation we want. In other words, operating in `Sequential (Population n)` works, and not only works but does something statistically interesting: particle filtering (aka SMC). -### FreeSampler +### Density -Summary of key info on `FreeSampler`: +Summary of key info on `Density`: -- `FreeSampler :: (Type -> Type) -> (Type -> Type)` -- `instance MonadSample (FreeSampler m)` +- `Density :: (Type -> Type) -> (Type -> Type)` +- `instance MonadSample (Density m)` - **No** instance for `MonadCond` -`FreeSampler m` is not often going to be used on its own, but instead as part of the `Traced` type, defined below. A `FreeSampler m a` represents a reified execution of the program. +A *trace* of a program of type `MonadSample m => m a` is an execution of the program, so a choice for each of the random values. Recall that `random` underlies all of the random values in a program, so a trace for a program is fully specified by a list of `Double`s, giving the value of each call to `random`. + +With this in mind, a `Density m a` is an interpretation of a probabilistic program as a function from a trace to the *density* of that execution of the program. + +Monad-bayes offers two implementations, in `Control.Monad.Bayes.Density.State` and `Control.Monad.Bayes.Density.Free`. The first is slow but easy to understand, the second is more sophisticated, but faster. -`FreeSampler m` is best understood if you're familiar with the standard use of a free monad to construct a domain specific language. For probability in particular, see this [blog post](https://jtobin.io/simple-probabilistic-programming). Here's the definition: +The former is relatively straightforward: the `MonadSample` instance implements `random` as `get`ting the trace (using `get` from `MonadState`), using (and removing) the first element (`put` from `MonadState`), and writing that element to the output (using `tell` from `MonadWriter`). If the trace is empty, the `random` from the underlying monad is used, but the result is still written with `tell`. + +The latter is best understood if you're familiar with the standard use of a free monad to construct a domain specific language. For probability in particular, see this [blog post](https://jtobin.io/simple-probabilistic-programming). Here's the definition: ```haskell newtype SamF a = Random (Double -> a) -newtype FreeSampler m a = - FreeSampler {runFreeSampler :: FT SamF m a} +newtype Density m a = + Density {density :: FT SamF m a} -instance Monad m => MonadSample (FreeSampler m) where - random = FreeSampler $ liftF (Random id) +instance Monad m => MonadSample (Density m) where + random = Density $ liftF (Random id) ``` The monad-bayes implementation uses a more efficient implementation of `FreeT`, namely `FT` from the `free` package, known as the *Church transformed Free monad*. This is a technique explained in https://begriffs.com/posts/2016-02-04-difference-lists-and-codennsity.html. But that only changes the operational semantics - performance aside, it works just the same as the standard `FreeT` datatype. @@ -509,21 +512,16 @@ The monad-bayes implementation uses a more efficient implementation of `FreeT`, If you unpack the definition, you get: ```haskell -FreeSampler m a ~ m (Either a (Double -> (FreeSampler m a))) +Density m a ~ m (Either a (Double -> (Density m a))) ``` -As you can see, this is rather like `Coroutine`, except to "resume", you must provide a new `Double`, corresponding to the value of some particular random choice. - Since `FreeT` is a transformer, we can use `lift` to get a `MonadSample` instance. - -A *trace* of a program of type `MonadSample m => m a` is an execution of the program, so a choice for each of the random values. Recall that `random` underlies all of the random values in a program, so a trace for a program is fully specified by a list of `Double`s, giving the value of each call to `random`. - -Given a probabilistic program interpreted in `FreeSampler m`, we can "run" it to produce a program in the underlying monad `m`. For simplicity, consider the case of a program `bernoulli 0.5 :: FreeSampler SamplerIO Bool`. We can then use the following function: +`density` is then defined using the canonical property of the free monad (transformer), embodied by `iterFT`, which interprets `SamF` in the appropriate way: ```haskell -withPartialRandomness :: MonadSample m => [Double] -> FreeSampler m a -> m (a, [Double]) -withPartialRandomness randomness (FreeSampler m) = +density :: MonadSample m => [Double] -> Density m a -> m (a, [Double]) +density randomness (Density m) = runWriterT $ evalStateT (iterTM f $ hoistFT lift m) randomness where f (Random k) = do @@ -538,7 +536,7 @@ withPartialRandomness randomness (FreeSampler m) = k x ``` -This takes a list of `Double`s (a representation of a trace), and a probabilistic program like `example`, and gives back a `SamplerIO (Bool, [Double])`. At each call to `random` in `example`, the next double in the list is used. If the list of doubles runs out, calls are made to `random` using the underlying monad, which in our example is `SamplerIO`. Hence "with*Partial*Randomness". +This takes a list of `Double`s (a representation of a trace), and a probabilistic program like `example`, and gives back a `SamplerIO (Bool, [Double])`. At each call to `random` in `example`, the next double in the list is used. If the list of doubles runs out, calls are made to `random` using the underlying monad. @@ -554,7 +552,7 @@ Summary of key info on `Traced`: - `instance MonadSample m => MonadSample (Traced m)` - `instance MonadCond m => MonadCond (Traced m)` -`Traced m` is actually several related interpretations, each built on top of `FreeSampler`. These range in complexity. +`Traced m` is actually several related interpretations, each built on top of `Density`. These range in complexity. @@ -576,12 +574,12 @@ data Trace a = Trace } ``` -We also need a specification of the probabilistic program in question, free of any particular interpretation. That is precisely what `FreeSampler` is for. +We also need a specification of the probabilistic program in question, free of any particular interpretation. That is precisely what `Density` is for. The simplest version of `Traced` is in `Control.Monad.Bayes.Traced.Basic` ```haskell -Traced m a ~ (FreeSampler Identity a, Log Double), m (Trace a)) +Traced m a ~ (Density Identity a, Log Double), m (Trace a)) ``` A `Traced` interpretation of a model is a particular run of the model with its corresponding probability, alongside a distribution over `Trace` info, which records: the value of each call to `random`, the value of the final output, and the density of this program trace. @@ -707,7 +705,7 @@ A single step in this chain (in Metropolis Hasting MCMC) looks like this: ```haskell mhTrans :: MonadSample m => - Weighted (FreeSampler m) a -> Trace a -> m (Trace a) + Weighted (Density m) a -> Trace a -> m (Trace a) mhTrans m t@Trace {variables = us, density = p} = do let n = length us us' <- do @@ -717,15 +715,14 @@ mhTrans m t@Trace {variables = us, density = p} = do (xs, _ : ys) -> return $ xs ++ (u' : ys) _ -> error "impossible" ((b, q), vs) <- - runWriterT $ weighted - $ Weighted.hoist (WriterT . withPartialRandomness us') m + runWriterT $ weighted $ Weighted.hoist (WriterT . density us') m 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 ``` -Our probabilistic program is interpreted in the type `Weighted (FreeSampler m) a`, which is an instance of `MonadInfer`. We use this to define our kernel on traces. We begin by perturbing the list of doubles contained in the trace by selecting a random position in the list and resampling there. We could do this *proposal* in a variety of ways, but here, we do so by choosing a double from the list at random and resampling it (hence, *single site* trace MCMC). We then run the program on this new list of doubles; `((b,q), vs)` is the outcome, probability, and result of all calls to `random`, respectively (recalling that the list of doubles may be shorter than the number of calls to `random`). The value of these is probabilistic in the underlying monad `m`. We then use the MH criterion to decide whether to accept the new list of doubles as our trace. +Our probabilistic program is interpreted in the type `Weighted (Density m) a`, which is an instance of `MonadInfer`. We use this to define our kernel on traces. We begin by perturbing the list of doubles contained in the trace by selecting a random position in the list and resampling there. We could do this *proposal* in a variety of ways, but here, we do so by choosing a double from the list at random and resampling it (hence, *single site* trace MCMC). We then run the program on this new list of doubles; `((b,q), vs)` is the outcome, probability, and result of all calls to `random`, respectively (recalling that the list of doubles may be shorter than the number of calls to `random`). The value of these is probabilistic in the underlying monad `m`. We then use the MH criterion to decide whether to accept the new list of doubles as our trace. MH is then easily defined as taking steps with this kernel, in the usual fashion. Note that it works for any probabilistic program whatsoever. @@ -736,7 +733,7 @@ MH is then easily defined as taking steps with this kernel, in the usual fashion This is provided by ```haskell -sis :: +sequentially :: Monad m => -- | transformation (forall x. m x -> m x) -> @@ -744,10 +741,10 @@ sis :: Int -> Sequential m a -> m a -sis f k = finish . composeCopies k (advance . hoistFirst f) +sequentially f k = finish . composeCopies k (advance . hoistFirst f) ``` -in Control.Monad.Bayes.Sequential. You provide a natural transformation in the underlying monad `m`, and `sis` applies that natural transformation at each point of conditioning in your program. The main use case is in defining `smc`, below, but here is a nice alternative use case: +in `Control.Monad.Bayes.Sequential.Coroutine`. You provide a natural transformation in the underlying monad `m`, and `sequentially` applies that natural transformation at each point of conditioning in your program. The main use case is in defining `smc`, below, but here is a nice didactic use case: Consider the program: diff --git a/flake.lock b/flake.lock index 0d7b434a..0967fb94 100644 --- a/flake.lock +++ b/flake.lock @@ -697,4 +697,4 @@ }, "root": "root", "version": 7 -} +} \ No newline at end of file diff --git a/models/HMM.hs b/models/HMM.hs index 2cd44c63..e6b1ee4d 100644 --- a/models/HMM.hs +++ b/models/HMM.hs @@ -17,88 +17,88 @@ import Pipes (MFunctor (hoist), MonadTrans (lift), each, yield, (>->)) import Pipes.Core (Producer) import qualified Pipes.Prelude as Pipes --- | 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 - ] +-- -- | 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 +-- ] --- | 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 _ = error "unreachable" +-- -- | 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 _ = error "unreachable" --- | The emission model. -emissionMean :: Int -> Double -emissionMean 0 = -1 -emissionMean 1 = 1 -emissionMean 2 = 0 -emissionMean _ = error "unreachable" +-- -- | The emission model. +-- emissionMean :: Int -> Double +-- 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] +-- -- | Initial state distribution +-- start :: MonadSample m => m Int +-- 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)) +-- -- | 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)) -syntheticData :: MonadSample m => Int -> m [Double] -syntheticData n = replicateM n syntheticPoint - where - syntheticPoint = uniformD [0, 1, 2] +-- syntheticData :: MonadSample m => Int -> m [Double] +-- syntheticData n = replicateM n syntheticPoint +-- where +-- syntheticPoint = uniformD [0, 1, 2] --- | Equivalent model, but using pipes for simplicity +-- -- | Equivalent model, but using pipes for simplicity --- | Prior expressed as a stream -hmmPrior :: MonadSample m => Producer Int m b -hmmPrior = do - x <- lift start - yield x - Pipes.unfoldr (fmap (Right . (\k -> (k, k))) . trans) x +-- -- | Prior expressed as a stream +-- hmmPrior :: MonadSample m => Producer Int m b +-- hmmPrior = do +-- x <- lift start +-- yield x +-- Pipes.unfoldr (fmap (Right . (\k -> (k, k))) . trans) x --- | Observations expressed as a stream -hmmObservations :: Functor m => [a] -> Producer (Maybe a) m () -hmmObservations dataset = each (Nothing : (Just <$> reverse dataset)) +-- -- | Observations expressed as a stream +-- hmmObservations :: Functor m => [a] -> Producer (Maybe a) m () +-- hmmObservations dataset = each (Nothing : (Just <$> reverse dataset)) --- | Posterior expressed as a stream -hmmPosterior :: (MonadInfer m) => [Double] -> Producer Int m () -hmmPosterior dataset = - zipWithM - hmmLikelihood - hmmPrior - (hmmObservations dataset) - where - hmmLikelihood :: MonadCond f => (Int, Maybe Double) -> f () - hmmLikelihood (l, o) = when (isJust o) (factor $ normalPdf (emissionMean l) 1 (fromJust o)) +-- -- | Posterior expressed as a stream +-- hmmPosterior :: (MonadInfer m) => [Double] -> Producer Int m () +-- hmmPosterior dataset = +-- zipWithM +-- hmmLikelihood +-- hmmPrior +-- (hmmObservations dataset) +-- where +-- hmmLikelihood :: MonadCond f => (Int, Maybe Double) -> f () +-- hmmLikelihood (l, o) = when (isJust o) (factor $ normalPdf (emissionMean l) 1 (fromJust o)) - zipWithM f p1 p2 = Pipes.zip p1 p2 >-> Pipes.chain f >-> Pipes.map fst +-- zipWithM f p1 p2 = Pipes.zip p1 p2 >-> Pipes.chain f >-> Pipes.map fst -hmmPosteriorPredictive :: MonadSample m => [Double] -> Producer Double m () -hmmPosteriorPredictive dataset = - Pipes.hoist enumerateToDistribution (hmmPosterior dataset) - >-> Pipes.mapM (\x -> normal (emissionMean x) 1) +-- hmmPosteriorPredictive :: MonadSample m => [(Real m)] -> Producer (Real m) m () +-- hmmPosteriorPredictive dataset = +-- Pipes.hoist enumerateToDistribution (hmmPosterior dataset) +-- >-> Pipes.mapM (\x -> normal (emissionMean x) 1) diff --git a/models/LDA.hs b/models/LDA.hs index 97aa69b1..86830054 100644 --- a/models/LDA.hs +++ b/models/LDA.hs @@ -26,59 +26,59 @@ import Numeric.Log (Log (Exp)) import Text.Pretty.Simple (pPrint) import Prelude hiding (words) -vocabulary :: [Text] -vocabulary = ["bear", "wolf", "python", "prolog"] +-- vocabulary :: [Text] +-- vocabulary = ["bear", "wolf", "python", "prolog"] -topics :: [Text] -topics = ["topic1", "topic2"] +-- topics :: [Text] +-- topics = ["topic1", "topic2"] -type Documents = [[Text]] +-- type Documents = [[Text]] -documents :: Documents -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" - ] +-- documents :: Documents +-- 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" +-- ] -wordDistPrior :: MonadSample m => m (V.Vector Double) -wordDistPrior = dirichlet $ V.replicate (length vocabulary) 1 +-- wordDistPrior :: MonadSample m => m (V.Vector Double) +-- wordDistPrior = dirichlet $ V.replicate (length vocabulary) 1 -topicDistPrior :: MonadSample m => m (V.Vector Double) -topicDistPrior = dirichlet $ V.replicate (length topics) 1 +-- topicDistPrior :: MonadSample m => m (V.Vector Double) +-- topicDistPrior = dirichlet $ V.replicate (length topics) 1 -wordIndex :: Map.Map Text Int -wordIndex = Map.fromList $ zip vocabulary [0 ..] +-- wordIndex :: Map.Map Text Int +-- wordIndex = Map.fromList $ zip vocabulary [0 ..] -lda :: - MonadInfer m => - Documents -> - m (Map.Map Text (V.Vector (Text, Double)), [(Text, V.Vector (Text, Double))]) -lda docs = do - word_dist_for_topic <- do - ts <- List.replicateM (length topics) wordDistPrior - return $ Map.fromList $ zip topics ts - let obs doc = do - topic_dist <- topicDistPrior - let f word = do - topic <- (fmap (topics !!) . categorical) topic_dist - factor $ (Exp . log) $ (word_dist_for_topic Map.! topic) V.! (wordIndex Map.! word) - mapM_ f doc - return topic_dist - td <- mapM obs docs - return - ( fmap (V.zip (V.fromList vocabulary)) word_dist_for_topic, - zip (fmap (foldr1 (\x y -> x <> " " <> y)) docs) (fmap (V.zip $ V.fromList ["topic1", "topic2"]) td) - ) +-- lda :: +-- MonadInfer m => +-- Documents -> +-- m (Map.Map Text (V.Vector (Text, Double)), [(Text, V.Vector (Text, Double))]) +-- lda docs = do +-- word_dist_for_topic <- do +-- ts <- List.replicateM (length topics) wordDistPrior +-- return $ Map.fromList $ zip topics ts +-- let obs doc = do +-- topic_dist <- topicDistPrior +-- let f word = do +-- topic <- (fmap (topics !!) . categorical) topic_dist +-- factor $ (Exp . log) $ (word_dist_for_topic Map.! topic) V.! (wordIndex Map.! word) +-- mapM_ f doc +-- return topic_dist +-- td <- mapM obs docs +-- return +-- ( fmap (V.zip (V.fromList vocabulary)) word_dist_for_topic, +-- zip (fmap (foldr1 (\x y -> x <> " " <> y)) docs) (fmap (V.zip $ V.fromList ["topic1", "topic2"]) td) +-- ) -syntheticData :: MonadSample m => Int -> Int -> m [[Text]] -syntheticData d w = List.replicateM d (List.replicateM w syntheticWord) - where - syntheticWord = uniformD vocabulary +-- syntheticData :: MonadSample m => Int -> Int -> m [[Text]] +-- syntheticData d w = List.replicateM d (List.replicateM w syntheticWord) +-- where +-- syntheticWord = uniformD vocabulary -runLDA :: IO () -runLDA = do - s <- sampleIOfixed $ unweighted $ mh 1000 $ lda documents - pPrint (head s) +-- runLDA :: IO () +-- runLDA = do +-- s <- sampleIOfixed $ unweighted $ mh 1000 $ lda documents +-- pPrint (head s) diff --git a/models/LogReg.hs b/models/LogReg.hs index 65bdfda9..0cf335da 100644 --- a/models/LogReg.hs +++ b/models/LogReg.hs @@ -3,7 +3,7 @@ -- Logistic regression model from Anglican -- (https://bitbucket.org/probprog/anglican-white-paper) -module LogReg (logisticRegression, syntheticData, xs, labels) where +module LogReg () where import Control.Monad (replicateM) import Control.Monad.Bayes.Class @@ -13,29 +13,29 @@ import Control.Monad.Bayes.Class ) import Numeric.Log (Log (Exp)) -logisticRegression :: MonadInfer m => [(Double, Bool)] -> m Double -logisticRegression dat = do - m <- normal 0 1 - b <- normal 0 1 - sigma <- gamma 1 1 - let y x = normal (m * x + b) sigma - sigmoid x = y x >>= \t -> return $ 1 / (1 + exp (-t)) - obs x label = do - p <- sigmoid x - factor $ (Exp . log) $ if label then p else 1 - p - mapM_ (uncurry obs) dat - sigmoid 8 +-- logisticRegression :: MonadInfer m => [(Double, Bool)] -> m Double +-- logisticRegression dat = do +-- m <- normal 0 1 +-- b <- normal 0 1 +-- sigma <- gamma 1 1 +-- let y x = normal (m * x + b) sigma +-- sigmoid x = y x >>= \t -> return $ 1 / (1 + exp (-t)) +-- obs x label = do +-- p <- sigmoid x +-- factor $ (Exp . log) $ if label then p else 1 - p +-- mapM_ (uncurry obs) dat +-- sigmoid 8 --- make a synthetic dataset by randomly choosing input-label pairs -syntheticData :: MonadSample m => Int -> m [(Double, Bool)] -syntheticData n = replicateM n do - x <- uniform (-1) 1 - label <- bernoulli 0.5 - return (x, label) +-- -- make a synthetic dataset by randomly choosing input-label pairs +-- syntheticData :: MonadSample m => Int -> m [(Double, Bool)] +-- syntheticData n = replicateM n do +-- x <- uniform (-1) 1 +-- label <- bernoulli 0.5 +-- return (x, label) --- a tiny test dataset, for sanity-checking -xs :: [Double] -xs = [-10, -5, 2, 6, 10] +-- -- a tiny test dataset, for sanity-checking +-- xs :: [Double] +-- xs = [-10, -5, 2, 6, 10] -labels :: [Bool] -labels = [False, False, True, True, True] +-- labels :: [Bool] +-- labels = [False, False, True, True, True] diff --git a/models/NestedInference.hs b/models/NestedInference.hs index 79aaa5c7..fa8abc88 100644 --- a/models/NestedInference.hs +++ b/models/NestedInference.hs @@ -6,30 +6,30 @@ import Control.Monad.Bayes.Class (MonadInfer, MonadSample (uniformD), factor) import Control.Monad.Bayes.Enumerator (mass) import Numeric.Log (Log (Exp)) -data Utterance = ASquare | AShape deriving (Eq, Show, Ord) - -data State = Square | Circle deriving (Eq, Show, Ord) - -data Action = Speak Utterance | DoNothing deriving (Eq, Show, Ord) - --- | uniformly likely to say any true utterance to convey the given state -truthfulAgent :: MonadSample m => State -> m Action -truthfulAgent state = uniformD case state of - Square -> [Speak ASquare, Speak AShape, DoNothing] - Circle -> [Speak AShape, DoNothing] - --- | a listener which applies Bayes rule to infer the state --- given an observed action of the other agent -listener :: MonadInfer m => Action -> m State -listener observedAction = do - state <- uniformD [Square, Circle] - factor $ log $ Exp $ mass (truthfulAgent state) observedAction - return state - --- | an agent which produces an action by reasoning about --- how the listener would interpret it -informativeAgent :: MonadInfer m => State -> m Action -informativeAgent state = do - utterance <- uniformD [Speak ASquare, Speak AShape, DoNothing] - factor $ log $ Exp $ mass (listener utterance) state - return utterance +-- data Utterance = ASquare | AShape deriving (Eq, Show, Ord) + +-- data State = Square | Circle deriving (Eq, Show, Ord) + +-- data Action = Speak Utterance | DoNothing deriving (Eq, Show, Ord) + +-- -- | uniformly likely to say any true utterance to convey the given state +-- truthfulAgent :: MonadSample m => State -> m Action +-- truthfulAgent state = uniformD case state of +-- Square -> [Speak ASquare, Speak AShape, DoNothing] +-- Circle -> [Speak AShape, DoNothing] + +-- -- | a listener which applies Bayes rule to infer the state +-- -- given an observed action of the other agent +-- listener :: MonadInfer m => Action -> m State +-- listener observedAction = do +-- state <- uniformD [Square, Circle] +-- factor $ log $ Exp $ mass (truthfulAgent state) observedAction +-- return state + +-- -- | an agent which produces an action by reasoning about +-- -- how the listener would interpret it +-- informativeAgent :: MonadInfer m => State -> m Action +-- informativeAgent state = do +-- utterance <- uniformD [Speak ASquare, Speak AShape, DoNothing] +-- factor $ log $ Exp $ mass (listener utterance) state +-- return utterance diff --git a/monad-bayes-site/AdvancedSampling.html b/monad-bayes-site/AdvancedSampling.html index 61baff06..b9cdb665 100644 --- a/monad-bayes-site/AdvancedSampling.html +++ b/monad-bayes-site/AdvancedSampling.html @@ -14592,7 +14592,7 @@ import Control.Monad.Bayes.Enumerator import Control.Monad.Bayes.Weighted import Control.Monad.Bayes.Sampler -import Control.Monad.Bayes.Free +import Control.Monad.Bayes.Density.Free import Control.Monad.Bayes.Population import Control.Monad.Bayes.Sequential import Control.Monad.Bayes.Inference.SMC diff --git a/monad-bayes-site/Functional_PPLs.html b/monad-bayes-site/Functional_PPLs.html index 4b3fff56..a444e2a0 100644 --- a/monad-bayes-site/Functional_PPLs.html +++ b/monad-bayes-site/Functional_PPLs.html @@ -16843,7 +16843,7 @@