An example using CPNest
Here we show a standalone example of using CPNest to
estimate the parameters of a straight line model in data with Gaussian noise. The
data and model used in this example are defined in createdata.py, which can be downloaded
from here. The
script shown below can be downloaded from here.
Example code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Example of running CPNest to fit the parameters of a straight line.
"""
from __future__ import print_function, division
import os
import sys
import numpy as np
# import CPNest
from cpnest.model import Model
import cpnest
# import model and data
from createdata import *
LN2PI = np.log(2.*np.pi)
LNSIGMA = np.log(sigma)
# set the model, likelihood and prior
class StraightLineModel(Model):
"""
A simple straight line model, with a Gaussian likelihood.
Args:
data (:class:`numpy.ndarray`): an array containing the observed data
abscissa (:class:`numpy.ndarray`): an array containing the points at which the data were taken
modelfunc (function): a function defining the model
sigma (float): the standard deviation of the noise in the data
"""
names = ['m','c'] # parameter names (this is a required variables for the class)
# define the boundaries using for initial sampling of the live points
cmin = -10. # lower range on c (the same as the uniform c prior lower bound)
cmax = 10. # upper range on c (the same as the uniform c prior upper bound)
mmu = 0. # mean of the Gaussian prior on m
msigma = 10. # standard deviation of the Gaussian prior on m
cbounds = [cmin, cmax]
# set the m bounds to be +/- 10sigma about the mean
mbounds = [mmu-10.*msigma, mmu+10.*msigma]
# NOTE: the bounds could instead be set through arguments passed to the __init__
# function for the class if wanted
bounds=[mbounds, cbounds] # upper and lower bounds on each parameter (required for the class)
def __init__(self, data, abscissa, modelfunc, sigma):
# set the data
self._data = data # oberserved data
self._abscissa = abscissa # points at which the observed data are taken
self._sigma = sigma # standard deviation(s) of the data
self._logsigma = np.log(sigma) # log sigma here to save computations in the likelihood
self._ndata = len(data) # number of data points
self._model = modelfunc # model function
def log_likelihood(self, params):
"""
The natural logarithm of the likelihood function.
Args:
params (dict): a dictionary keyed to the parameter names.
Returns:
float: the log likelihood value.
"""
# extract the parameters
m = params['m']
c = params['c']
# calculate the model
model = self._model(x, m, c)
# normalisation
norm = -0.5*self._ndata*LN2PI - self._ndata*self._logsigma
# chi-squared
chisq = np.sum(((self._data - model)/(self._sigma))**2)
return norm - 0.5*chisq
def log_prior(self,p):
"""
The natural logarithm of the prior function.
Args:
p (dict): a dictionary keyed to parameter names.
Returns:
float: The log prior value.
"""
# the uniform priors are dealt with by just checking that we're within the bounds
if not self.in_bounds(p): return -np.inf # check parameters are all in bounds
# Gaussian prior on m
lp = 0.
m = p['m']
lp -= 0.5*((m-self.mmu)/self.msigma)**2 # no need for normalisation constant on the prior
return lp
nlive = 1024 # number of live point
maxmcmc = 1024 # maximum MCMC chain length
nthreads = 1 # use one CPU core
# set up the algorithm
work = cpnest.CPNest(StraightLineModel(data, x, straight_line, sigma), verbose=0,
nthreads=nthreads, nlive=nlive, maxmcmc=maxmcmc);
# run the algorithm
work.run();
logZcpnest = work.NS.logZ # value of log Z
infogaincpnest = work.NS.state.info # value of the information gain
logZerrcpnest = np.sqrt(infogaincpnest/nlive) # estimate of the statistcal uncertainty on logZ
# get the null log likelihood (evidence that the data is Gaussian noise with zero mean, and given standard devaition)
logZnull = work.user.log_likelihood({'m': 0., 'c': 0.})
print('Marginalised evidence is {} ± {}'.format(logZcpnest, logZerrcpnest))
# output the log Bayes factor
print('log Bayes factor is {}'.format(logZcpnest - logZnull))
mchain_cpnest = work.posterior_samples['m'] # extract chain of m values
cchain_cpnest = work.posterior_samples['c'] # extract chain if c values
postsamples = np.vstack((mchain_cpnest, cchain_cpnest)).T
# plot posterior samples (if corner.py is installed)
try:
import matplotlib as mpl
mpl.use("Agg") # force Matplotlib backend to Agg
import corner # import corner.py
except ImportError:
sys.exit(1)
fig = corner.corner(postsamples, labels=[r"$m$", r"$c$"], truths=[m, c])
fig.savefig('CPNest.png')
Running the code
A description of installing CPNest is given here. If you have downloaded the createdata.py and test_CPNest.py scripts into the directory ${HOME}, then you can run it using:
python test_CPNest.py
If you have Matplotlib installed then the script will produce a plot of the posterior distributions on the straight line parameters $m$ and $c$.
A Python 3 Docker image with CPNest installed is available, which can be used with:
docker run -it -v ${HOME}:/work mattpitkin/samplers:python3
to enter an interactive container, and then within the container the test script can be run with:
python test_CPNest.py