Reshi
Reshi

Reputation: 33

Defining a data type as MonadSample

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

Answers (1)

Steven Fontanella
Steven Fontanella

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

Related Questions