- Notifications
You must be signed in to change notification settings - Fork48
A C++ library of Markov Chain Monte Carlo (MCMC) methods
License
kthohr/mcmc
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
MCMCLib is a lightweight C++ library of Markov Chain Monte Carlo (MCMC) methods.
Features:
- A C++11/14/17 library of well-known MCMC algorithms.
- Parallelized samplers designed for multi-modal distributions, including:
- Adaptive Equi-Energy Sampler (AEES)
- Differential Evolution (DE)
- For fast and efficient matrix-based computation, MCMCLib supports the following templated linear algebra libraries:
- Automatic differentiation functionality is available through use of theAutodiff library
- OpenMP-accelerated algorithms for parallel computation.
- Straightforward linking with parallelized BLAS libraries, such asOpenBLAS.
- Available as a single precision (
float
) or double precision (double
) library. - Available as a header-only library, or as a compiled shared library.
- Released under a permissive, non-GPL license.
- Algorithms
- Documentation
- General API
- Installation
- R Compatibility
- Examples
- Automatic Differentiation
- Author and License
A list of currently available algorithms includes:
- Adaptive Equi-Energy Sampler (AEES)
- Differential Evolution (DE-MCMC)
- Hamiltonian Monte Carlo (HMC)
- Metropolis-adjusted Langevin algorithm (MALA)
- No-U-Turn Sampler (NUTS)
- Random Walk Metropolis-Hastings (RWMH)
- Riemannian Manifold Hamiltonian Monte Carlo (RM-HMC)
Full documentation is available online:
A PDF version of the documentation is availablehere.
The MCMCLib API follows a relatively simple convention, with most algorithms called in the following manner:
algorithm_id(<initial values>, <log posterior kernel function of the target distribution>, <storage for posterior draws>, <additional data for the log posterior kernel function>);
The inputs, in order, are:
- A vector of initial values used to define the starting point of the algorithm.
- A user-specified function that returns the log posterior kernel value of the target distribution.
- An array to store the posterior draws.
- The final input is optional: it is any object that contains additional data necessary to evaluate the log posterior kernel function.
For example, the RWMH algorithm is called using:
boolrwmh(const ColVec_t& initial_vals, std::function<fp_t (const ColVec_t& vals_inp,void* target_data)> target_log_kernel, Mat_t& draws_out, void* target_data);
whereColVec_t
is used to represent, e.g.,arma::vec
orEigen::VectorXd
types.
MCMCLib is available as a compiled shared library, or as header-only library, for Unix-alike systems only (e.g., popular Linux-based distros, as well as macOS). Use of this library with Windows-based systems, with or without MSVC,is not supported.
MCMCLib requires either the Armadillo or Eigen C++ linear algebra libraries. (Note that Eigen version 3.4.0 requires a C++14-compatible compiler.)
Before including the header files, defineone of the following:
#defineMCMC_ENABLE_ARMA_WRAPPERS#defineMCMC_ENABLE_EIGEN_WRAPPERS
Example:
#defineMCMC_ENABLE_EIGEN_WRAPPERS#include"mcmc.hpp"
The library can be installed on Unix-alike systems via the standard./configure && make
method.
First clone the library and any necessary submodules:
# clone mcmc into the current directorygit clone https://github.com/kthohr/mcmc ./mcmc# change directorycd ./mcmc# clone necessary submodulesgit submodule update --init
Set (one) of the following environment variablesbefore runningconfigure
:
export ARMA_INCLUDE_PATH=/path/to/armadilloexport EIGEN_INCLUDE_PATH=/path/to/eigen
Finally:
# build and install with Eigen./configure -i"/usr/local" -l eigen -pmakemake install
The final command will install MCMCLib into/usr/local
.
Configuration options (see./configure -h
):
Primary
-h
print help-i
installation path; default: the build directory-f
floating-point precision mode; default:double
-l
specify the choice of linear algebra library; choosearma
oreigen
-m
specify the BLAS and Lapack libraries to link with; for example,-m "-lopenblas"
or-m "-framework Accelerate"
-o
compiler optimization options; defaults to-O3 -march=native -ffp-contract=fast -flto -DARMA_NO_DEBUG
-p
enable OpenMP parallelization features (recommended)
Secondary
-c
a coverage build (used with Codecov)-d
a 'development' build-g
a debugging build (optimization flags set to-O0 -g
)
Special
--header-only-version
generate a header-only version of MCMCLib (seebelow)
MCMCLib is also available as a header-only library (i.e., without the need to compile a shared library). Simply runconfigure
with the--header-only-version
option:
./configure --header-only-version
This will create a new directory,header_only_version
, containing a copy of MCMCLib, modified to work on an inline basis. With this header-only version, simply include the header files (#include "mcmc.hpp
) and set the include path to thehead_only_version
directory (e.g.,-I/path/to/mcmclib/header_only_version
).
To use MCMCLib with an R package, first generate a header-only version of the library (seeabove). Then simply add a compiler definition before including the MCMCLib files.
- For RcppArmadillo:
#defineMCMC_USE_RCPP_ARMADILLO#include"mcmc.hpp"
At this time, builds usingRcppEigen
are not supported as MCMCLib requires a version of Eigen >= v3.4.0.
To illustrate MCMCLib at work, consider the problem of sampling values of the mean parameter of a normal distribution.
Code:
#defineMCMC_ENABLE_EIGEN_WRAPPERS#include"mcmc.hpp"inlineEigen::VectorXdeigen_randn_colvec(size_t nr){static std::mt19937 gen{ std::random_device{}() };static std::normal_distribution<> dist;return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x);returndist(gen); });}structnorm_data_t {double sigma; Eigen::VectorXd x;double mu_0;double sigma_0;};doublell_dens(const Eigen::VectorXd& vals_inp,void* ll_data){constdouble pi =3.14159265358979;//constdouble mu =vals_inp(0);norm_data_t* dta =reinterpret_cast<norm_data_t*>(ll_data);constdouble sigma = dta->sigma;const Eigen::VectorXd x = dta->x;constint n_vals = x.size();//constdouble ret = - n_vals * (0.5 *std::log(2*pi) +std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma);//return ret;}doublelog_pr_dens(const Eigen::VectorXd& vals_inp,void* ll_data){constdouble pi =3.14159265358979;//norm_data_t* dta =reinterpret_cast<norm_data_t* >(ll_data);constdouble mu_0 = dta->mu_0;constdouble sigma_0 = dta->sigma_0;constdouble x =vals_inp(0);constdouble ret = -0.5*std::log(2*pi) -std::log(sigma_0) -std::pow(x - mu_0,2) / (2*sigma_0*sigma_0);return ret;}doublelog_target_dens(const Eigen::VectorXd& vals_inp,void* ll_data){returnll_dens(vals_inp,ll_data) +log_pr_dens(vals_inp,ll_data);}intmain(){constint n_data =100;constdouble mu =2.0;norm_data_t dta; dta.sigma =1.0; dta.mu_0 =1.0; dta.sigma_0 =2.0; Eigen::VectorXd x_dta = mu +eigen_randn_colvec(n_data).array(); dta.x = x_dta; Eigen::VectorXdinitial_val(1);initial_val(0) =1.0;// mcmc::algo_settings_t settings; settings.rwmh_settings.par_scale =0.4; settings.rwmh_settings.n_burnin_draws =2000; settings.rwmh_settings.n_keep_draws =2000;// Eigen::MatrixXd draws_out;mcmc::rwmh(initial_val, log_target_dens, draws_out, &dta, settings);// std::cout <<"rwmh mean:\n" << draws_out.colwise().mean() << std::endl; std::cout <<"acceptance rate:" <<static_cast<double>(settings.rwmh_settings.n_accept_draws) / settings.rwmh_settings.n_keep_draws << std::endl;//return0;}
On x86-based computers, this example can be compiled using:
g++ -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ rwmh_normal_mean.cpp -o rwmh_normal_mean.out -L./../.. -lmcmc
Check the/examples
directory for additional examples, andhttps://mcmclib.readthedocs.io/en/latest/ for a detailed description of each algorithm.
By combining Eigen with theAutodiff library, MCMCLib provides experimental support for automatic differentiation.
The example below uses forward-mode automatic differentiation to compute the gradient of the Gaussian likelihood function, and the HMC algorithm to sample from the posterior distribution of the mean and variance parameters.
#defineMCMC_ENABLE_EIGEN_WRAPPERS#include"mcmc.hpp"#include<autodiff/forward/real.hpp>#include<autodiff/forward/real/eigen.hpp>inlineEigen::VectorXdeigen_randn_colvec(size_t nr){static std::mt19937 gen{ std::random_device{}() };static std::normal_distribution<> dist;return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x);returndist(gen); });}structnorm_data_t { Eigen::VectorXd x;};doublell_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out,void* ll_data){constdouble pi =3.14159265358979;norm_data_t* dta =reinterpret_cast<norm_data_t*>(ll_data);const Eigen::VectorXd x = dta->x;// autodiff::real u; autodiff::ArrayXreal xd = vals_inp.eval(); std::function<autodiff::real (const autodiff::ArrayXreal& vals_inp)> normal_dens_log_form \ = [x, pi](const autodiff::ArrayXreal& vals_inp) -> autodiff::real { autodiff::real mu =vals_inp(0); autodiff::real sigma =vals_inp(1);return - x.size() * (0.5 *std::log(2*pi) +autodiff::detail::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); };//if (grad_out) { Eigen::VectorXd grad_tmp =autodiff::gradient(normal_dens_log_form,autodiff::wrt(xd),autodiff::at(xd), u); *grad_out = grad_tmp; }else { u =normal_dens_log_form(xd); }//return u.val();}doublelog_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out,void* ll_data){returnll_dens(vals_inp,grad_out,ll_data);}intmain(){constint n_data =1000;constdouble mu =2.0;constdouble sigma =2.0;norm_data_t dta; Eigen::VectorXd x_dta = mu + sigma *eigen_randn_colvec(n_data).array(); dta.x = x_dta; Eigen::VectorXdinitial_val(2);initial_val(0) = mu +1;// muinitial_val(1) = sigma +1;// sigma mcmc::algo_settings_t settings; settings.hmc_settings.step_size =0.08; settings.hmc_settings.n_burnin_draws =2000; settings.hmc_settings.n_keep_draws =2000;// Eigen::MatrixXd draws_out;mcmc::hmc(initial_val, log_target_dens, draws_out, &dta, settings);// std::cout <<"hmc mean:\n" << draws_out.colwise().mean() << std::endl; std::cout <<"acceptance rate:" <<static_cast<double>(settings.hmc_settings.n_accept_draws) / settings.hmc_settings.n_keep_draws << std::endl;//return0;}
Compile with:
g++ -Wall -std=c++17 -O3 -march=native -ffp-contract=fast -I/path/to/eigen -I/path/to/autodiff -I/path/to/mcmc/include hmc_normal_autodiff.cpp -o hmc_normal_autodiff.cpp -L/path/to/mcmc/lib -lmcmc
See thedocumentation for more details on this topic.
Keith O'Hara
Apache Version 2
About
A C++ library of Markov Chain Monte Carlo (MCMC) methods
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.