Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
LazyPPL is a library for Bayesian probabilistic programming. It supports lazy use of probability, and we provide new Metropolis-Hastings simulation algorithms to allow this. Laziness appears to be a good paradigm for non-parametric statistics.
Reference paper: Affine Monads and Lazy Structures for Bayesian Programming. POPL 2023.
Illustrations: https://lazyppl-team.github.io.
LazyPPL is inspired by recent ideas in synthetic probability theory and synthetic measure theory, such as quasi-Borel spaces and Markov categories. LazyPPL is inspired by many other languages, including Church, Anglican, and Monad-Bayes. Monad-Bayes now includes a LazyPPL-inspired simulation algorithm.
This module defines
- Two monads:
Prob
(for probability measures) andMeas
(for unnormalized measures), with interfaceuniform
,sample
,score
. Monte Carlo inference methods produce samples from an unnormalized measure. We provide three inference methods:
a.
mh
(Metropolis-Hastings algorithm based on lazily mutating parts of the tree at random).b.
mhirreducible
, which randomly restarts for a properly irreducible Metropolis-Hastings kernel.c.
wis
(simple reference weighted importance sampling)See also the SingleSite module for a separate single-site Metropolis-Hastings algorithm via GHC.Exts.Heap and System.IO.Unsafe.
- Various useful helpful functions.
A typical usage would be
import LazyPPL (Prob, Meas, uniform, sample, score, mh, every)
Most of the structure here will not be needed in typical models. We expose more of the structure for more experimental uses.
The Distributions
module provides many useful distributions, and further non-parametric distributions are in DirichletP
, GP
, IBP
, and Memoization
.
Synopsis
- data Tree = Tree Double [Tree]
- newtype Prob a = Prob (Tree -> a)
- newtype Meas a = Meas (WriterT (Product (Log Double)) Prob a)
- uniform :: Prob Double
- sample :: Prob a -> Meas a
- score :: Double -> Meas ()
- mh :: forall a. Double -> Meas a -> IO [(a, Product (Log Double))]
- mhirreducible :: forall a. Double -> Double -> Meas a -> IO [(a, Product (Log Double))]
- weightedsamples :: forall a. Meas a -> IO [(a, Log Double)]
- wis :: Int -> Meas a -> IO [a]
- every :: Int -> [a] -> [a]
- randomTree :: RandomGen g => g -> Tree
- runProb :: Prob a -> Tree -> a
Rose tree type
Our source of randomness will be an infinitely wide and deep lazy rose tree, regarded as initialized with uniform [0,1] choices for each label.
A Tree
here is a lazy, infinitely wide and infinitely deep rose tree, labelled by Doubles.
Monads
A probability distribution over a is
a function Tree -> a
.
We can think of this as the law of a random variable, indexed by the source of randomness, which is Tree
.
According to the monad implementation, a program uses up bits of the tree as it runs. The tree being infinitely wide and deep allows for lazy computation.
Instances
Applicative Prob Source # | |
Functor Prob Source # | |
Monad Prob Source # | Sequencing is done by splitting the tree and using different bits for different computations. This monad structure is strongly inspired by the probability monad of quasi-Borel space. |
MonadMemo Prob Table Source # | |
MonadMemo Prob Dish Source # | |
An unnormalized measure is represented by a probability distribution over pairs of a weight and a result.
Basic interface
There are three building blocks for measures: uniform
for probability measures; sample
and score
for unnormalized measures. Combined with the monad structure, these give all s-finite measures.
uniform :: Prob Double Source #
A uniform sample is a building block for probability distributions.
This is implemented by getting the label at the head of the tree and discarding the rest.
score :: Double -> Meas () Source #
A one point measure with a given score (or weight, or mass, or likelihood), which should be a positive real number.
A score of 0 describes impossibility. To avoid numeric issues, we encode it as exp(-300)
instead.
Monte Carlo simulation
The Meas
type describes unnormalized measures. Monte Carlo simulation allows us to sample from an unnormalized measure. Our main Monte Carlo simulator is mh
.
:: forall a. Double | The chance |
-> Meas a | The unnormalized measure to sample from |
-> IO [(a, Product (Log Double))] | Returns a stream of (result,weight) pairs |
Produce a stream of samples, using Metropolis Hastings simulation.
The weights are also returned. Often the weights can be discarded, but sometimes we may search for a sample of maximum score.
The algorithm works as follows.
At each step, we randomly change some sites (nodes in the tree). We then accept or reject these proposed changes, using a probability that is determined by the weight of the measure at the new tree. If rejected, we repeat the previous sample.
This kernel is related to the one introduced by Wingate, Stuhlmuller, Goodman, AISTATS 2011, but it is different in that it works when the number of sites is unknown. Moreover, since a site is a path through the tree, the address is more informative than a number, which avoids some addressing issues.
When 1/p
is roughly the number of used sites, then this will be a bit like "single-site lightweight" MH.
If p
= 1 then this is "multi-site lightweight" MH.
Tip: if m :: Prob a
then use map fst $ (mh 1 $ sample m)
to get a stream of samples from a probability distribution without conditioning.
-
:: forall a. Double | The chance |
-> Double | The chance |
-> Meas a | The unnormalized measure |
-> IO [(a, Product (Log Double))] | Returns a stream of (result,weight) pairs |
Irreducible form of mh
. Takes p
like mh
, but also q
, which is the chance of proposing an all-sites change. Irreducibility means that, asymptotically, the sequence of samples will converge in distribution to the renormalized version of m
.
The kernel in mh
is not formally irreducible in the usual sense, although it is an open question whether this is a problem for asymptotic convergence in any definable model. In any case, convergence is only asymptotic, and so it can be helpful to use mhirreducible
is that in some situations.
Roughly this avoids mh
getting stuck in one particular mode, although it is a rather brutal method.
weightedsamples :: forall a. Meas a -> IO [(a, Log Double)] Source #
Runs an unnormalized measure and gets out a stream of (result,weight) pairs.
These are not samples from the renormalized distribution, just plain (result,weight) pairs. This is useful when the distribution is known to be normalized already.
:: Int |
|
-> Meas a |
|
-> IO [a] | Returns a stream of samples |
Weighted importance sampling first draws n weighted samples, and then samples a stream of results from that, regarded as an empirical distribution. Sometimes called "likelihood weighted importance sampling".
This is a reference implementation. It will not usually be very efficient at all, but may be useful for debugging.
Useful functions
every :: Int -> [a] -> [a] Source #
Useful function which thins out a stream of results, as is common in Markov Chain Monte Carlo simulation.
every n xs
returns only the elements at indices that are multiples of n.-