Reputation: 33
I am trying to define a toy probabilistic programming language to test various inference algorithms and their effectiveness. I followed this tutorial to create a Scheme like language with a basic structure. Now I want to use the monad-bayes library to add the probabilistic backend. My end goal is to support sampling from and observing from distributions. This is the definition of my expressions
data LispVal = Atom String -- Stores a string naming the atom
| List [LispVal] -- List of expressions
| DottedList [LispVal] LispVal -- List of elements but last
| Integer Integer -- Int
| Float Double
| String String -- Str
| Bool Bool -- Bool
| Port Handle
| PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
| IOFunc ([LispVal] -> IOThrowsError LispVal)
| Func { params :: [String], vararg :: Maybe String,
body :: [LispVal], closure :: Env }
Now, I want to add MonadSample and MonadInfer types to support the functions in the library. However, simply adding | ModelSample MonadSample LispVal
does not work. I went over the source code of the library many times, but I dont seem to understand it well enough, there are quite some monad-transformations going on. This is how they define basic distributions in the library
class Monad m => MonadSample m where
-- | Draw from a uniform distribution.
random ::
-- | \(\sim \mathcal{U}(0, 1)\)
m Double
-- | Draw from a uniform distribution.
uniform ::
-- | 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 ::
-- | 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 ::
-- | shape k
Double ->
-- | scale θ
Double ->
-- | \(\sim \Gamma(k, \theta)\)
m Double
gamma shape scale = draw (gammaDistr shape scale)
-- | Draw from a beta distribution.
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 ::
-- | probability p
Double ->
-- | \(\sim \mathrm{B}(1, p)\)
m Bool
bernoulli p = fmap (< p) random
-- | Draw from a categorical distribution.
categorical ::
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) =>
-- | event probabilities
v (Log Double) ->
-- | outcome category
m Int
logCategorical = categorical . VG.map (exp . ln)
-- | Draw from a discrete uniform distribution.
uniformD ::
-- | 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)
return (xs !! i)
-- | Draw from a geometric distribution.
geometric ::
-- | 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 ::
-- | parameter λ
Double ->
-- | \(\sim \mathrm{Pois}(\lambda)\)
m Int
poisson = discrete . Poisson.poisson
Is there a way to make all of these be included in the LispVal data I have? Or am I just following a wrong logic here and is there a better way to do this?
I would also welcome any more suggestions to how to go about integrating this library in my language. And as a side note, my goal is not to integrate all the functionality, but just the bare minimum to make a functioning probabilistic programming language.
Thanks in advance!
Edit: To clarify, here is an example program that I want to be able to run with my language.
(define (model1 upper lower) (sample (uniform upper lower)))
This function will just return a sample from a uniform distribution with the given constraints.The haskell library allows to do this via following
a = sampleIO $ (uniform upper lower)
I want to be able to use the same functionalities. What I initially tried was just to put | ModelSample MonadSample LispVal
which gives the error "Expected a type, but 'MonadSample LispVal' has type Constraint" I looked up the error but couldnt find a way to solidify this Monad into a type which can be used by my expression
Upvotes: 3
Views: 105
Reputation: 784
A data declaration needs to use concrete types, but MonadSample
is a constraint. It describes behaviors instead of implementations. From hackage, one instance of MonadSample is SamplerIO
which you can use in your data declaration. e.g.
data LispVal =
-- [...]
| Sample (SamplerIO LispVal)
From a quick look at what you have though I would instead recommend 'compiling' calls to sample
into your existing constructor for IOFunc
. e.g. your example
(sample (uniform upper lower))
can compile into IOFunc (\_ -> sampleIO $ uniform upper lower)
This way you don't need to add every future monad that you decide to add as a constructor. They can all just run in IO.
Upvotes: 1