An example using PyPolyChord

Here we show a standalone example of using PyPolyChord 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

download
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Example of running PyPolyChord to fit the parameters of a straight line.
"""

from __future__ import print_function, division

import os
import sys
from scipy.special import ndtri
import numpy as np
import resource

# import PolyChord
import pypolychord
from pypolychord.settings import PolyChordSettings
from pypolychord.priors import UniformPrior

# import model and data
from createdata import *

LN2PI = np.log(2.*np.pi)
LNSIGMA = np.log(sigma)


def prior_transform_polychord(cube):
    """
    A function defining the tranform between the parameterisation in the unit hypercube
    to the true parameters.
    
    Args:
        cube (array, list): a list containing the parameters as drawn from a unit hypercube.
        
    Returns:
        list: the transformed parameters.
    """
    
    #mprime, cprime = cube # unpack the parameters (in their unit hypercube form)
    mprime = cube[0]
    cprime = cube[1]

    cmin = -10.  # lower bound on uniform prior on c
    cmax = 10.   # upper bound on uniform prior on c

    mmu = 0.     # mean of Gaussian prior on m
    msigma = 10. # standard deviation of Gaussian prior on m

    m = mmu + msigma*ndtri(mprime) # convert back to m
    c = UniformPrior(cmin, cmax)(cprime) # convert back to c using UniformPrior class

    theta = [m, c]
    
    return theta


def loglikelihood_polychord(theta):
    """
    The log-likelihood function.
    
    Args:
        theta (array, list): the set of parameter values.
        
    Returns:
        float: the log-likelihood value.
        list: A list of any derived parameters (an empty list if there are none)
    """

    # unpack the parameters
    m = theta[0]
    c = theta[1]

    # normalisation
    norm = -0.5*M*LN2PI - M*LNSIGMA

    # chi-squared (data, sigma and x are global variables defined early on in this notebook)
    chisq = np.sum(((data-straight_line(x, m, c))/sigma)**2)

    return norm - 0.5*chisq, []


nlive = 1024   # number of live points
ndims = 2      # number of parameters
nderived = 0   # number of derived parameters (this is zero)
tol = 0.5      # stopping criterion
basedir = os.path.join(os.getcwd(), 'polychord')   # output base directory
if not os.path.isdir(basedir):
    os.makedirs(basedir)                           # create base directory
    os.makedirs(os.path.join(basedir, 'clusters')) # 'clusters' directory

fileroot = 'straightline'                          # output file name
broot = os.path.join(basedir, fileroot)

# set an unlimited stack-size of PolyChord
curlimit = resource.getrlimit(resource.RLIMIT_STACK) # get current stack resource size
resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY,resource.RLIM_INFINITY)) # set to unlimited

# setup run settings using the PolyChordSetting class
pargs = {'nlive': nlive,
         'precision_criterion': tol,
         'base_dir': basedir,
         'file_root': fileroot,
         'write_resume': False, # don't output a resume file
         'read_resume': False}  # don't read a resume file
settings = PolyChordSettings(ndims, nderived, **pargs)

# run nested sampling
output = pypolychord.run_polychord(loglikelihood_polychord, ndims, nderived, settings, prior_transform_polychord)

# reset stack resource size
resource.setrlimit(resource.RLIMIT_STACK, curlimit)

# output marginal likelihood
print('Marginalised evidence is {} ± {}'.format(output.logZ, output.logZerr))

# 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)

samplefile = broot+'_equal_weights.txt'
samples = np.loadtxt(samplefile)
postsamples = samples[:,2:]

print('Number of posterior samples is {}'.format(postsamples.shape[0]))

fig = corner.corner(postsamples, labels=[r"$m$", r"$c$"], truths=[m, c])
fig.savefig('PyPolyChord.png')

Running the code

A description of installing PyPolyChord is given here. If you have downloaded the createdata.py and test_PyPolyChord.py scripts into the directory ${HOME}, then you can run it using:

python test_PyPolyChord.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 PyPolyChord 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_PyPolyChord.py

Comments