{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- 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 (..),
    hoist,
    marginal,
    mhStep,
    mh,
  )
where

import Control.Applicative (liftA2)
import Control.Monad.Bayes.Class
  ( MonadDistribution (random),
    MonadFactor (..),
    MonadMeasure,
  )
import Control.Monad.Bayes.Density.Free (Density)
import Control.Monad.Bayes.Traced.Common
  ( Trace (..),
    bind,
    mhTransFree,
    scored,
    singleton,
  )
import Control.Monad.Bayes.Weighted (Weighted)
import Control.Monad.Trans (MonadTrans (..))
import Data.List.NonEmpty as NE (NonEmpty ((:|)), toList)

-- | 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
  { forall (m :: * -> *) a. Traced m a -> Weighted (Density m) a
model :: Weighted (Density m) a,
    forall (m :: * -> *) a. Traced m a -> m (Trace a)
traceDist :: m (Trace a)
  }

instance Monad m => Functor (Traced m) where
  fmap :: forall a b. (a -> b) -> Traced m a -> Traced m b
fmap a -> b
f (Traced Weighted (Density m) a
m m (Trace a)
d) = Weighted (Density m) b -> m (Trace b) -> Traced m b
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced ((a -> b) -> Weighted (Density m) a -> Weighted (Density m) b
forall a b.
(a -> b) -> Weighted (Density m) a -> Weighted (Density m) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f Weighted (Density m) a
m) ((Trace a -> Trace b) -> m (Trace a) -> m (Trace b)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> b) -> Trace a -> Trace b
forall a b. (a -> b) -> Trace a -> Trace b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f) m (Trace a)
d)

instance Monad m => Applicative (Traced m) where
  pure :: forall a. a -> Traced m a
pure a
x = Weighted (Density m) a -> m (Trace a) -> Traced m a
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced (a -> Weighted (Density m) a
forall a. a -> Weighted (Density m) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x) (Trace a -> m (Trace a)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Trace a
forall a. a -> Trace a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x))
  (Traced Weighted (Density m) (a -> b)
mf m (Trace (a -> b))
df) <*> :: forall a b. Traced m (a -> b) -> Traced m a -> Traced m b
<*> (Traced Weighted (Density m) a
mx m (Trace a)
dx) = Weighted (Density m) b -> m (Trace b) -> Traced m b
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced (Weighted (Density m) (a -> b)
mf Weighted (Density m) (a -> b)
-> Weighted (Density m) a -> Weighted (Density m) b
forall a b.
Weighted (Density m) (a -> b)
-> Weighted (Density m) a -> Weighted (Density m) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Weighted (Density m) a
mx) ((Trace (a -> b) -> Trace a -> Trace b)
-> m (Trace (a -> b)) -> m (Trace a) -> m (Trace b)
forall a b c. (a -> b -> c) -> m a -> m b -> m c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 Trace (a -> b) -> Trace a -> Trace b
forall a b. Trace (a -> b) -> Trace a -> Trace b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
(<*>) m (Trace (a -> b))
df m (Trace a)
dx)

instance Monad m => Monad (Traced m) where
  (Traced Weighted (Density m) a
mx m (Trace a)
dx) >>= :: forall a b. Traced m a -> (a -> Traced m b) -> Traced m b
>>= a -> Traced m b
f = Weighted (Density m) b -> m (Trace b) -> Traced m b
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced Weighted (Density m) b
my m (Trace b)
dy
    where
      my :: Weighted (Density m) b
my = Weighted (Density m) a
mx Weighted (Density m) a
-> (a -> Weighted (Density m) b) -> Weighted (Density m) b
forall a b.
Weighted (Density m) a
-> (a -> Weighted (Density m) b) -> Weighted (Density m) b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Traced m b -> Weighted (Density m) b
forall (m :: * -> *) a. Traced m a -> Weighted (Density m) a
model (Traced m b -> Weighted (Density m) b)
-> (a -> Traced m b) -> a -> Weighted (Density m) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Traced m b
f
      dy :: m (Trace b)
dy = m (Trace a)
dx m (Trace a) -> (a -> m (Trace b)) -> m (Trace b)
forall (m :: * -> *) a b.
Monad m =>
m (Trace a) -> (a -> m (Trace b)) -> m (Trace b)
`bind` (Traced m b -> m (Trace b)
forall (m :: * -> *) a. Traced m a -> m (Trace a)
traceDist (Traced m b -> m (Trace b))
-> (a -> Traced m b) -> a -> m (Trace b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Traced m b
f)

instance MonadTrans Traced where
  lift :: forall (m :: * -> *) a. Monad m => m a -> Traced m a
lift m a
m = Weighted (Density m) a -> m (Trace a) -> Traced m a
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced (Density m a -> Weighted (Density m) a
forall (m :: * -> *) a. Monad m => m a -> Weighted m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Density m a -> Weighted (Density m) a)
-> Density m a -> Weighted (Density m) a
forall a b. (a -> b) -> a -> b
$ m a -> Density m a
forall (m :: * -> *) a. Monad m => m a -> Density m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m a
m) ((a -> Trace a) -> m a -> m (Trace a)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Trace a
forall a. a -> Trace a
forall (f :: * -> *) a. Applicative f => a -> f a
pure m a
m)

instance MonadDistribution m => MonadDistribution (Traced m) where
  random :: Traced m Double
random = Weighted (Density m) Double -> m (Trace Double) -> Traced m Double
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced Weighted (Density m) Double
forall (m :: * -> *). MonadDistribution m => m Double
random ((Double -> Trace Double) -> m Double -> m (Trace Double)
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Double -> Trace Double
singleton m Double
forall (m :: * -> *). MonadDistribution m => m Double
random)

instance MonadFactor m => MonadFactor (Traced m) where
  score :: Log Double -> Traced m ()
score Log Double
w = Weighted (Density m) () -> m (Trace ()) -> Traced m ()
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced (Log Double -> Weighted (Density m) ()
forall (m :: * -> *). MonadFactor m => Log Double -> m ()
score Log Double
w) (Log Double -> m ()
forall (m :: * -> *). MonadFactor m => Log Double -> m ()
score Log Double
w m () -> m (Trace ()) -> m (Trace ())
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Trace () -> m (Trace ())
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Log Double -> Trace ()
scored Log Double
w))

instance MonadMeasure m => MonadMeasure (Traced m)

hoist :: (forall x. m x -> m x) -> Traced m a -> Traced m a
hoist :: forall (m :: * -> *) a.
(forall x. m x -> m x) -> Traced m a -> Traced m a
hoist forall x. m x -> m x
f (Traced Weighted (Density m) a
m m (Trace a)
d) = Weighted (Density m) a -> m (Trace a) -> Traced m a
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced Weighted (Density m) a
m (m (Trace a) -> m (Trace a)
forall x. m x -> m x
f m (Trace a)
d)

-- | Discard the trace and supporting infrastructure.
marginal :: Monad m => Traced m a -> m a
marginal :: forall (m :: * -> *) a. Monad m => Traced m a -> m a
marginal (Traced Weighted (Density m) a
_ m (Trace a)
d) = (Trace a -> a) -> m (Trace a) -> m a
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Trace a -> a
forall a. Trace a -> a
output m (Trace a)
d

-- | A single step of the Trace Metropolis-Hastings algorithm.
mhStep :: MonadDistribution m => Traced m a -> Traced m a
mhStep :: forall (m :: * -> *) a.
MonadDistribution m =>
Traced m a -> Traced m a
mhStep (Traced Weighted (Density m) a
m m (Trace a)
d) = Weighted (Density m) a -> m (Trace a) -> Traced m a
forall (m :: * -> *) a.
Weighted (Density m) a -> m (Trace a) -> Traced m a
Traced Weighted (Density m) a
m m (Trace a)
d'
  where
    d' :: m (Trace a)
d' = m (Trace a)
d m (Trace a) -> (Trace a -> m (Trace a)) -> m (Trace a)
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Weighted (Density m) a -> Trace a -> m (Trace a)
forall (m :: * -> *) a.
MonadDistribution m =>
Weighted (Density m) a -> Trace a -> m (Trace a)
mhTransFree Weighted (Density m) a
m

-- $setup
-- >>> import Control.Monad.Bayes.Class
-- >>> import Control.Monad.Bayes.Sampler.Strict
-- >>> import Control.Monad.Bayes.Weighted

-- | Full run of the Trace Metropolis-Hastings algorithm with a specified
-- number of steps. Newest samples are at the head of the list.
--
-- For example:
--
-- * I have forgotten what day it is.
-- * There are ten buses per hour in the week and three buses per hour at the weekend.
-- * I observe four buses in a given hour.
-- * What is the probability that it is the weekend?
--
-- >>> :{
--  let
--    bus = do x <- bernoulli (2/7)
--             let rate = if x then 3 else 10
--             factor $ poissonPdf rate 4
--             return x
--    mhRunBusSingleObs = do
--      let nSamples = 2
--      sampleIOfixed $ unweighted $ mh nSamples bus
--  in mhRunBusSingleObs
-- :}
-- [True,True,True]
--
-- Of course, it will need to be run more than twice to get a reasonable estimate.
mh :: MonadDistribution m => Int -> Traced m a -> m [a]
mh :: forall (m :: * -> *) a.
MonadDistribution m =>
Int -> Traced m a -> m [a]
mh Int
n (Traced Weighted (Density m) a
m m (Trace a)
d) = (NonEmpty (Trace a) -> [a]) -> m (NonEmpty (Trace a)) -> m [a]
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Trace a -> a) -> [Trace a] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map Trace a -> a
forall a. Trace a -> a
output ([Trace a] -> [a])
-> (NonEmpty (Trace a) -> [Trace a]) -> NonEmpty (Trace a) -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty (Trace a) -> [Trace a]
forall a. NonEmpty a -> [a]
NE.toList) (Int -> m (NonEmpty (Trace a))
forall {t}. (Ord t, Num t) => t -> m (NonEmpty (Trace a))
f Int
n)
  where
    f :: t -> m (NonEmpty (Trace a))
f t
k
      | t
k t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
0 = (Trace a -> NonEmpty (Trace a))
-> m (Trace a) -> m (NonEmpty (Trace a))
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Trace a -> [Trace a] -> NonEmpty (Trace a)
forall a. a -> [a] -> NonEmpty a
:| []) m (Trace a)
d
      | Bool
otherwise = do
          (Trace a
x :| [Trace a]
xs) <- t -> m (NonEmpty (Trace a))
f (t
k t -> t -> t
forall a. Num a => a -> a -> a
- t
1)
          Trace a
y <- Weighted (Density m) a -> Trace a -> m (Trace a)
forall (m :: * -> *) a.
MonadDistribution m =>
Weighted (Density m) a -> Trace a -> m (Trace a)
mhTransFree Weighted (Density m) a
m Trace a
x
          NonEmpty (Trace a) -> m (NonEmpty (Trace a))
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Trace a
y Trace a -> [Trace a] -> NonEmpty (Trace a)
forall a. a -> [a] -> NonEmpty a
:| Trace a
x Trace a -> [Trace a] -> [Trace a]
forall a. a -> [a] -> [a]
: [Trace a]
xs)