The first toy model#
from pathlib import Path
import numpy as np
from mpi4py import MPI
import tjsampler as ts
from tjsampler.constants import TWOPI
TMP_DIR = Path("../.tmp")
In this tutotial, we will demonstrate the basic usage of Sampler by a toy model:
generating multivariate normal posterior with regard to \(x,\,y\).
The covariance is
We wiil adopt a uniform prior and hence the likelihood takes the form of posterior.
# ============== MPI ===============
nwalker = 4
ntemp = 2
comm = MPI.COMM_WORLD
iproc = comm.Get_rank()
ts.random.seed(iproc)
# ============== Distribution ================
cov = np.array([[1, .5], [.5, .5]])
log_cov_det = np.log(np.linalg.det(cov))
cov_inv = np.linalg.inv(cov)
The code snippet implies \(2\) temperatue chains and \(4\) walkers are involved.
To ensure independent sampling between processes, we must use different seeds to initialize
the ramdom variable generator, which are the process IDs in this simple case.
(A better method is numpy SeedSequence.)
# ============== MCMC ================
priors = ts.prior.Prior({"x": ts.prior.Uniform(-3, 3), "y": ts.prior.Uniform(-3, 3)})
class ToyLikelihood:
def calc_log_likelihood(self, state):
params = state.params
ans = -.5 * params @ cov_inv @ params - .5 * log_cov_det - np.log(TWOPI)
return ans
toylikelihood = ToyLikelihood()
move = ts.move.StretchMove(2, nwalker, comm=comm.Dup())
temp = ts.temp.Temperature(ntemp)
The Likelihood should implement calc_log_likelihood method. The type of parameter passed in
is defined in state module. We use the affine invariant stretch move is this demo.
This proposal need to transfer message between processes and hence we copy the communicator to
avoid interference with temperature swap.
nstep = 1000
nburn = 500
thin = 20
outfile = TMP_DIR / "toy_model.h5"
params0 = priors.draw_sample()
sampler = ts.sampler.Sampler(priors, toylikelihood, move, nwalker, temp,
outfile=outfile, comm=comm)
sampler.run(params0, nstep, nburn, thin)
The result will save to a .hdf5 file. The user may manipulate it with h5py.
Alternatively, Result module encapsulate some functions. For example,
filename = "../examples/toy_model.h5"
result = tj.result.Result(filename)
samples = result.samples_params()
np.cov(samples[:, 0], samples[:, 1]) #[[1.04255178, 0.51451805], [0.51451805, 0.53013195]])
_ = result.plot_dist_params()
Notes about CLI#
This demo is simple enough to run with
mpiexec -n 8 python toy_model.py
directly. While cluster is often eqiupped with a job scheduling system, we take Slurm as an example. The program can be run by submitting a script like
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks=8
#SBATCH --cpus-per-task=1
mpiexec -n 8 python -u toy_model.py
Each MCMC chain is a mpi process by design of the package.
It is common for the number of processes to exceed the usable cores of CPU.
If this happens, ignore the ntask and OVERSUBSCRIB your hosts:
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
mpiexec --mca mpi_yield_when_idle 1 --map-by :OVERSUBSCRIB 8 python -u toy_model.py