Representing Probability Distributions

Introduction

Probability distributions such as prior distributions over model parameters are reprented in QInfer by objects of type Distribution that are responsible for producing samples according to those distributions. This is especially useful, for instance, when drawing initial particles for use with an SMCUpdater.

The approach to representing distributions taken by QInfer is somewhat different to that taken by, for example, scipy.stats, in that a QInfer Distribution is a class that produces samples according to that distribution. This means that QInfer Distribution objects provide much less information than do those represented by objects in scipy.stats, but that they are much easier to write and combine.

Sampling Pre-made Distributions

QInfer comes along with several distributions, listed in Specific Distributions. Each of these is a subclass of Distribution, and hence has a method sample() that produces an array of samples.

>>> from qinfer import NormalDistribution
>>> dist = NormalDistribution(0, 1)
>>> samples = dist.sample(n=5)
>>> samples.shape == (5, 1)
True

Combining Distributions

Distribution objects can be combined using other distribution objects. For instance, if \(a \sim \mathcal{N}(0, 1)\) and \(b \sim \text{Uni}(0, 1)\), then the product distribution on \((a,b)\) can be produced by using ProductDistribution:

>>> from qinfer import UniformDistribution, ProductDistribution
>>> a = NormalDistribution(0, 1)
>>> b = UniformDistribution([0, 1])
>>> ab = ProductDistribution(a, b)
>>> samples = ab.sample(n=5)
>>> samples.shape == (5, 2)
True

Making Custom Distributions

To make a custom distribution, one need only implement sample() and set the property n_rvs to indicate how many random variables the new distribution class represents.

For example, to implement a distribution over \(x\) and \(y\) such that \(\sqrt{x^2 + y^2} \sim \mathcal{N}(1, 0.1)\) and such that the angle between \(x\) and \(y\) is drawn from \(\text{Uni}(0, 2 \pi)\):

from qinfer import Distribution

class RingDistribution(Distribution):
    @property
    def n_rvs(self):
        return 2

    def sample(self, n=1):
        r = np.random.randn(n, 1) * 0.1 + 1
        th = np.random.random((n, 1)) * 2 * np.pi

        x = r * np.cos(th)
        y = r * np.sin(th)

        return np.concatenate([x, y], axis=1)