├── docs ├── source │ ├── _static │ │ └── main_stylesheet.css │ ├── conf.py │ ├── box_constraints.rst │ ├── index.rst │ ├── installation.rst │ ├── autodiff.rst │ ├── settings.rst │ └── examples_and_tests.rst ├── environment.yml ├── Doxyfile ├── Makefile └── make.bat ├── .gitmodules ├── .readthedocs.requirements.txt ├── NOTICE.txt ├── .readthedocs.yml ├── .gitignore ├── include ├── mcmc │ ├── mcmc_algos.hpp │ ├── aees.ipp │ ├── mala.ipp │ ├── rwmh.hpp │ ├── aees.hpp │ ├── de.hpp │ ├── hmc.hpp │ ├── nuts.hpp │ ├── mala.hpp │ ├── rmhmc.hpp │ └── nuts.ipp ├── misc │ ├── misc.hpp │ ├── bounds_check.hpp │ ├── determine_bounds_type.hpp │ ├── inv_jacobian_adjust.hpp │ ├── log_jacobian.hpp │ ├── transform_vals.hpp │ ├── mcmc_structs.hpp │ └── mcmc_options.hpp ├── mcmc.hpp └── stats │ ├── seed_values.hpp │ ├── mcmc_stats.hpp │ ├── dmvnorm.hpp │ └── dnorm.hpp ├── .travis.yml ├── Makefile.in ├── examples ├── armadillo │ ├── nuts_normal.cpp │ ├── hmc_normal.cpp │ ├── mala_normal.cpp │ ├── rwmh_normal_mean.cpp │ ├── de_normal_mean.cpp │ ├── aees_mixture.cpp │ └── rmhmc_normal.cpp ├── eigen │ ├── nuts_normal.cpp │ ├── hmc_normal.cpp │ ├── mala_normal.cpp │ ├── rwmh_normal_mean.cpp │ ├── de_normal_mean.cpp │ ├── rmhmc_normal.cpp │ └── aees_mixture.cpp └── autodiff │ └── hmc_normal_autodiff.cpp ├── .github └── workflows │ └── main.yml └── src ├── rwmh.cpp ├── mala.cpp ├── hmc.cpp └── de.cpp /docs/source/_static/main_stylesheet.css: -------------------------------------------------------------------------------- 1 | .wy-nav-content{ 2 | max-width: 1050px !important; 3 | margin: auto; 4 | } 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "include/BaseMatrixOps"] 2 | path = include/BaseMatrixOps 3 | url = https://github.com/kthohr/BaseMatrixOps.git 4 | -------------------------------------------------------------------------------- /.readthedocs.requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx == 5.1.1 2 | breathe == 4.34.0 3 | sphinx-rtd-theme == 1.0.0 4 | sphinxcontrib-katex == 0.8.6 5 | sphinxcontrib-contentui == 0.2.5 6 | docutils == 0.17.1 -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | MCMCLib: A C++ library of Markov Chain Monte Carlo (MCMC) methods 2 | Copyright 2011-2023 Keith O'Hara 3 | 4 | This product includes software developed by Keith O'Hara (http://www.kthohr.com) -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: "ubuntu-22.04" 5 | tools: 6 | python: "mambaforge-22.9" 7 | 8 | formats: 9 | - pdf 10 | 11 | conda: 12 | environment: docs/environment.yml 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Rproj.user 3 | .Rhistory 4 | Makefile 5 | *.dll 6 | *.exe 7 | *.log 8 | *.lp 9 | *.o 10 | *.so 11 | *.test 12 | *.tmp 13 | *.vscode 14 | include/armadillo* 15 | *.gcda 16 | *.gcno 17 | *.gcov 18 | *.dSYM 19 | *.out 20 | 21 | !docs/Makefile 22 | docs/build 23 | docs/xml 24 | 25 | header_only_version 26 | -------------------------------------------------------------------------------- /docs/environment.yml: -------------------------------------------------------------------------------- 1 | 2 | name: mcmclib-docs 3 | 4 | channels: 5 | - conda-forge 6 | 7 | dependencies: 8 | - doxygen=1.9.3 9 | - pip: 10 | - sphinx==5.1.1 11 | - breathe==4.34.0 12 | - sphinx-rtd-theme==1.0.0 13 | - sphinxcontrib-katex==0.8.6 14 | - sphinxcontrib-contentui==0.2.5 15 | - docutils==0.17.1 -------------------------------------------------------------------------------- /docs/Doxyfile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = "mcmclib" 2 | XML_OUTPUT = xml 3 | INPUT = ../include 4 | GENERATE_LATEX = NO 5 | GENERATE_MAN = NO 6 | GENERATE_RTF = NO 7 | CASE_SENSE_NAMES = NO 8 | GENERATE_HTML = NO 9 | GENERATE_XML = YES 10 | RECURSIVE = YES 11 | QUIET = YES 12 | JAVADOC_AUTOBRIEF = YES 13 | WARN_IF_UNDOCUMENTED = NO 14 | 15 | ENABLE_PREPROCESSING = YES 16 | MACRO_EXPANSION = YES 17 | EXPAND_ONLY_PREDEF = YES 18 | PREDEFINED = "mcmclib_inline=inline" \ 19 | "MCMC_ENABLE_ARMA_WRAPPERS" \ 20 | "MCMC_ENABLE_EIGEN_WRAPPERS" 21 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | 6 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 7 | 8 | if on_rtd: 9 | subprocess.run(["doxygen", "-v"]) 10 | subprocess.call('cd ..; doxygen', shell=True) 11 | 12 | import sphinx_rtd_theme 13 | 14 | html_theme = "sphinx_rtd_theme" 15 | 16 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 17 | 18 | html_theme_options = { 19 | 'navigation_depth': 4 20 | } 21 | 22 | def setup(app): 23 | app.add_css_file("main_stylesheet.css") 24 | 25 | extensions = ['breathe','sphinxcontrib.katex','sphinxcontrib.contentui'] 26 | breathe_projects = { 'mcmclib': '../xml' } 27 | templates_path = ['_templates'] 28 | html_static_path = ['_static'] 29 | source_suffix = '.rst' 30 | master_doc = 'index' 31 | project = 'MCMCLib' 32 | copyright = '2011-2023 Keith O\'Hara' 33 | author = 'Keith O\'Hara' 34 | 35 | exclude_patterns = [] 36 | highlight_language = 'c++' 37 | pygments_style = 'sphinx' 38 | todo_include_todos = False 39 | htmlhelp_basename = 'mcmcdoc' 40 | -------------------------------------------------------------------------------- /include/mcmc/mcmc_algos.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #ifndef MCMC_MCMC_INCLUDES 22 | #define MCMC_MCMC_INCLUDES 23 | 24 | #include "rwmh.hpp" 25 | #include "mala.hpp" 26 | #include "hmc.hpp" 27 | #include "nuts.hpp" 28 | #include "rmhmc.hpp" 29 | 30 | #include "aees.hpp" 31 | #include "de.hpp" 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/misc/misc.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #ifndef MCMC_MISC_INCLUDES 22 | #define MCMC_MISC_INCLUDES 23 | 24 | // structs 25 | #include "mcmc_structs.hpp" 26 | 27 | // utility files 28 | #include "determine_bounds_type.hpp" 29 | #include "bounds_check.hpp" 30 | #include "transform_vals.hpp" 31 | #include "log_jacobian.hpp" 32 | #include "inv_jacobian_adjust.hpp" 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/mcmc.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #ifndef MCMC_INCLUDES 22 | #define MCMC_INCLUDES 23 | 24 | #include "misc/mcmc_options.hpp" 25 | 26 | namespace mcmc 27 | { 28 | // basic distribution functions 29 | #include "stats/mcmc_stats.hpp" 30 | 31 | // misc/utility files 32 | #include "misc/misc.hpp" 33 | 34 | // MCMC algorithms 35 | #include "mcmc/mcmc_algos.hpp" 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/stats/seed_values.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #ifndef MCMCLIB_STATS_SEED_VALUES 22 | #define MCMCLIB_STATS_SEED_VALUES 23 | 24 | inline 25 | size_t 26 | generate_seed_value(const int ind_inp, const int n_threads, rand_engine_t& rand_engine) 27 | { 28 | return static_cast( (bmo::stats::runif(rand_engine) + ind_inp + n_threads) * 1000 ); 29 | // return static_cast( (ind_inp + n_threads) * 1000 ); 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/stats/mcmc_stats.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * PDF of univariate and multivariate normal distributions 23 | */ 24 | 25 | #ifndef MCMC_STATS_INCLUDE 26 | #define MCMC_STATS_INCLUDE 27 | 28 | #ifndef MCMC_LOG_2PI 29 | #define MCMC_LOG_2PI 1.83787706640934548356L 30 | #endif 31 | 32 | namespace stats_mcmc 33 | { 34 | 35 | template 36 | using return_t = typename std::conditional::value,double,T>::type; 37 | 38 | template 39 | using common_t = typename std::common_type::type; 40 | 41 | template 42 | using common_return_t = return_t>; 43 | 44 | #include "dnorm.hpp" 45 | #include "dmvnorm.hpp" 46 | 47 | } 48 | 49 | #include "seed_values.hpp" 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: c++ 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - os: linux 8 | dist: trusty 9 | addons: 10 | apt: 11 | packages: 12 | - g++-5 13 | - gfortran-5 14 | sources: &sources 15 | - ubuntu-toolchain-r-test 16 | env: 17 | - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5 && FC=gfortran-5" 18 | compiler: gcc 19 | 20 | - os: linux 21 | dist: trusty 22 | addons: 23 | apt: 24 | packages: 25 | - g++-6 26 | - gfortran-6 27 | sources: &sources 28 | - ubuntu-toolchain-r-test 29 | env: 30 | - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6 && FC=gfortran-6" 31 | compiler: gcc 32 | 33 | - os: linux 34 | dist: trusty 35 | addons: 36 | apt: 37 | packages: 38 | - g++-7 39 | - gfortran-7 40 | sources: &sources 41 | - ubuntu-toolchain-r-test 42 | env: 43 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && FC=gfortran-7" 44 | compiler: gcc 45 | 46 | before_install: 47 | - export FC=gfortran 48 | - eval "${MATRIX_EVAL}" 49 | - echo $FC 50 | 51 | install: 52 | - | 53 | if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then 54 | sudo apt-get update 55 | sudo apt-get install libblas-dev liblapack-dev 56 | else 57 | # OSX 58 | brew upgrade gcc || brew install gcc 59 | fi 60 | 61 | script: 62 | - ./configure -c 63 | - make 64 | - cd ./tests 65 | - ./setup && ./test_setup/run_cov 66 | - cd .. 67 | 68 | after_success: 69 | - bash <(curl -s https://codecov.io/bash) 70 | 71 | after_failure: 72 | - ./travis-tool.sh dump_logs 73 | 74 | notifications: 75 | email: 76 | on_success: change 77 | on_failure: change 78 | -------------------------------------------------------------------------------- /include/stats/dmvnorm.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * pdf of the Multivariate Normal distribution 23 | */ 24 | 25 | #ifndef MCMC_STATS_DMVNORM 26 | #define MCMC_STATS_DMVNORM 27 | 28 | inline 29 | fp_t 30 | dmvnorm(const ColVec_t& X, const ColVec_t& mu_par, const Mat_t& Sigma_par, bool log_form) 31 | { 32 | const size_t K = BMO_MATOPS_SIZE(X); 33 | 34 | // 35 | 36 | const fp_t cons_term = - fp_t(0.5) * K * fp_t(MCMC_LOG_2PI); 37 | const ColVec_t X_cent = X - mu_par; // avoids issues like Mat vs eGlue in templates 38 | 39 | const fp_t quad_term = BMO_MATOPS_QUAD_FORM_INV(X_cent, Sigma_par); 40 | 41 | fp_t ret = cons_term - fp_t(0.5) * ( BMO_MATOPS_LOG_DET(Sigma_par) + quad_term ); 42 | 43 | if (!log_form) { 44 | ret = std::exp(ret); 45 | 46 | if (std::isinf(ret)) { 47 | ret = std::numeric_limits::max(); 48 | } 49 | } 50 | 51 | // 52 | 53 | return ret; 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/misc/bounds_check.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * check and impose sampling bounds for DE 23 | */ 24 | 25 | inline 26 | void 27 | sampling_bounds_check( 28 | const bool vals_bound, 29 | const size_t n_vals, 30 | const ColVecInt_t bounds_type, 31 | const ColVec_t& hard_lower_bounds, 32 | const ColVec_t& hard_upper_bounds, 33 | ColVec_t& sampling_lower_bounds, 34 | ColVec_t& sampling_upper_bounds 35 | ) 36 | { 37 | if (vals_bound) { 38 | for (size_t i = 0; i < n_vals; ++i) { 39 | if (bounds_type(i) == 4 || bounds_type(i) == 2) { 40 | // lower and upper bound imposed || lower bound only 41 | sampling_lower_bounds(i) = std::max( hard_lower_bounds(i), sampling_lower_bounds(i) ); 42 | } 43 | if (bounds_type(i) == 4 || bounds_type(i) == 3) { 44 | // lower and upper bound imposed || upper bound only 45 | sampling_upper_bounds(i) = std::min( hard_upper_bounds(i), sampling_upper_bounds(i) ); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | 2 | # core compiling options 3 | 4 | CXX = @CXX@ 5 | 6 | CXX_STD = @MCMC_CXX_STD@ 7 | OPT_FLAGS = @MCMC_WARN_FLAGS@ @MCMC_OPT_FLAGS@ 8 | FPN_FLAGS = -DMCMC_FPN_TYPE=@MCMC_FPN_TYPE@ 9 | 10 | MCMC_MATLIB_FLAGS = @MCMC_MATLIB_FLAGS@ 11 | MCMC_MATLIB_INCLUDE_PATH = @MCMC_MATLIB_INCLUDE_PATH@ 12 | 13 | # install location 14 | INSTALL_PATH=@MCMC_INSTALL_PATH@ 15 | 16 | # source directories 17 | SDIR = . 18 | MCMC_DIR = $(SDIR) 19 | MCMC_SRC_DIR = $(SDIR)/src 20 | MCMC_HEADER_DIR = $(SDIR)/include 21 | 22 | # shared library name and flags 23 | SHLIB = @MCMC_SHLIB_NAME@ 24 | SHLIB_FLAGS = $(CXX_STD) @MCMC_SHLIB_FLAGS@ 25 | 26 | # general flags 27 | CXXFLAGS = $(CXX_STD) $(OPT_FLAGS) $(FPN_FLAGS) $(MCMC_MATLIB_FLAGS) -I$(MCMC_MATLIB_INCLUDE_PATH) -I$(MCMC_HEADER_DIR) 28 | LIBS= @MCMC_BLAS_LAPACK@ 29 | 30 | # core MCMC files 31 | 32 | SOURCES_MCMC= $(MCMC_SRC_DIR)/aees.cpp $(MCMC_SRC_DIR)/de.cpp $(MCMC_SRC_DIR)/hmc.cpp $(MCMC_SRC_DIR)/nuts.cpp $(MCMC_SRC_DIR)/rmhmc.cpp $(MCMC_SRC_DIR)/mala.cpp $(MCMC_SRC_DIR)/rwmh.cpp 33 | OBJECTS_MCMC= $(SOURCES_MCMC:.cpp=.o) 34 | 35 | all: $(MCMC_DIR)/$(SHLIB) $(OBJECTS_MCMC) 36 | 37 | # build 38 | 39 | $(MCMC_SRC_DIR)/%.o: $(MCMC_SRC_DIR)/%.cpp 40 | $(CXX) $(CXXFLAGS) $< -c -o $@ 41 | 42 | # shared library 43 | $(MCMC_DIR)/$(SHLIB): $(OBJECTS_MCMC) 44 | $(CXX) $(SHLIB_FLAGS) -o $@ $^ $(LIBS) 45 | 46 | # cleanup and install 47 | .PHONY: clean 48 | clean: 49 | @rm -f *.so ./tests/*.test ./tests/*.o $(MCMC_SRC_DIR)/*.o $(MCMC_SRC_DIR)/*.gcov $(MCMC_SRC_DIR)/*.gcno $(MCMC_SRC_DIR)/*.gcda $(MCMC_SRC_DIR)/*.dSYM \ 50 | $(MCMC_SRC_DIR)/*/*.o $(MCMC_SRC_DIR)/*/*.gcov $(MCMC_SRC_DIR)/*/*.gcno $(MCMC_SRC_DIR)/*/*.gcda $(MCMC_SRC_DIR)/*/*.dSYM 51 | 52 | .PHONY: vclean 53 | vclean: 54 | @rm -f *.so ./tests/*.test ./tests/*.o $(MCMC_SRC_DIR)/*.o $(MCMC_SRC_DIR)/*/*.o 55 | @rm -rf ./include/armadillo* 56 | 57 | .PHONY: install 58 | install: $(SHLIB) 59 | @cp $(MCMC_DIR)/$(SHLIB) $(INSTALL_PATH)/lib/$(SHLIB) 60 | @mkdir -p $(INSTALL_PATH)/include/mcmc 61 | @cp -r $(MCMC_DIR)/include/* $(INSTALL_PATH)/include/mcmc 62 | -------------------------------------------------------------------------------- /include/misc/determine_bounds_type.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Determine the upper-lower bounds combo type 23 | */ 24 | 25 | // note: std::isfinite is not true for: NaN, -Inf, or +Inf 26 | 27 | inline 28 | ColVecInt_t 29 | determine_bounds_type( 30 | const bool vals_bound, 31 | const size_t n_vals, 32 | const ColVec_t& lower_bounds, 33 | const ColVec_t& upper_bounds) 34 | { 35 | ColVecInt_t ret_vec(n_vals); 36 | 37 | BMO_MATOPS_SET_VALUES_SCALAR(ret_vec, 1); // base case: 1 - no bounds imposed 38 | 39 | if (vals_bound) { 40 | for (size_t i = 0; i < n_vals; ++i) { 41 | if ( std::isfinite(lower_bounds(i)) && std::isfinite(upper_bounds(i)) ) { 42 | // lower and upper bound imposed 43 | ret_vec(i) = 4; 44 | } else if ( std::isfinite(lower_bounds(i)) && !std::isfinite(upper_bounds(i)) ) { 45 | // lower bound only 46 | ret_vec(i) = 2; 47 | } else if ( !std::isfinite(lower_bounds(i)) && std::isfinite(upper_bounds(i)) ) { 48 | // upper bound only 49 | ret_vec(i) = 3; 50 | } 51 | } 52 | } 53 | 54 | // 55 | 56 | return ret_vec; 57 | } 58 | -------------------------------------------------------------------------------- /include/misc/inv_jacobian_adjust.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * inverse Jacobian adjustment 23 | */ 24 | 25 | inline 26 | Mat_t 27 | inv_jacobian_adjust( 28 | const ColVec_t& vals_trans_inp, 29 | const ColVecInt_t& bounds_type, 30 | const ColVec_t& lower_bounds, 31 | const ColVec_t& upper_bounds 32 | ) 33 | { 34 | const size_t n_vals = BMO_MATOPS_SIZE(bounds_type); 35 | 36 | Mat_t ret_mat = BMO_MATOPS_EYE(n_vals); 37 | 38 | for (size_t i = 0; i < n_vals; ++i) { 39 | switch (bounds_type(i)) { 40 | case 2: // lower bound only 41 | // ret_mat(i,i) = 1 / std::exp(vals_trans_inp(i)); 42 | ret_mat(i,i) = std::exp(-vals_trans_inp(i)); 43 | break; 44 | case 3: // upper bound only 45 | // ret_mat(i,i) = 1 / std::exp(-vals_trans_inp(i)); 46 | ret_mat(i,i) = std::exp(vals_trans_inp(i)); 47 | break; 48 | case 4: // upper and lower bounds 49 | const fp_t exp_inp = std::exp(vals_trans_inp(i)); 50 | ret_mat(i,i) = ( (exp_inp + 1) * (exp_inp + 1) ) / ( exp_inp * (upper_bounds(i) - lower_bounds(i)) ); 51 | break; 52 | } 53 | } 54 | 55 | return ret_mat; 56 | } 57 | -------------------------------------------------------------------------------- /include/misc/log_jacobian.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * log Jacobian adjustment 23 | */ 24 | 25 | inline 26 | fp_t 27 | log_jacobian( 28 | const ColVec_t& vals_trans_inp, 29 | const ColVecInt_t& bounds_type, 30 | const ColVec_t& lower_bounds, 31 | const ColVec_t& upper_bounds 32 | ) 33 | { 34 | const size_t n_vals = BMO_MATOPS_SIZE(bounds_type); 35 | 36 | fp_t ret_val = 0.0; 37 | 38 | for (size_t i = 0; i < n_vals; ++i) { 39 | switch (bounds_type(i)) { 40 | case 2: // lower bound only 41 | ret_val += vals_trans_inp(i); 42 | break; 43 | case 3: // upper bound only 44 | ret_val += - vals_trans_inp(i); 45 | break; 46 | case 4: // upper and lower bounds 47 | double exp_inp = std::exp(vals_trans_inp(i)); 48 | if (std::isfinite(exp_inp)) { 49 | ret_val += std::log(upper_bounds(i) - lower_bounds(i)) + vals_trans_inp(i) - 2 * std::log(1 + exp_inp); 50 | } else { 51 | ret_val += std::log(upper_bounds(i) - lower_bounds(i)) - vals_trans_inp(i); 52 | } 53 | break; 54 | } 55 | } 56 | 57 | return ret_val; 58 | } 59 | -------------------------------------------------------------------------------- /include/mcmc/aees.ipp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Adaptive Equi-Energy Sampler 23 | */ 24 | 25 | #ifndef _mcmc_aees_IPP 26 | #define _mcmc_aees_IPP 27 | 28 | // single-step Metropolis-Hastings for tempered distributions 29 | 30 | inline 31 | ColVec_t 32 | single_step_mh( 33 | const ColVec_t& X_prev, 34 | const fp_t temper_val, 35 | const Mat_t& prop_scaling_mat, 36 | std::function target_log_kernel, 37 | void* target_data, 38 | rand_engine_t& rand_engine, 39 | fp_t* val_out 40 | ) 41 | { 42 | const size_t n_vals = BMO_MATOPS_SIZE(X_prev); 43 | 44 | const ColVec_t rand_vec = bmo::stats::rnorm_vec(n_vals, rand_engine); 45 | 46 | const ColVec_t X_new = X_prev + std::sqrt(temper_val) * prop_scaling_mat * rand_vec; 47 | 48 | // 49 | 50 | const fp_t val_new = target_log_kernel(X_new, target_data); 51 | const fp_t val_prev = target_log_kernel(X_prev, target_data); 52 | 53 | const fp_t comp_val = std::min(fp_t(0.01), (val_new - val_prev) / temper_val ); 54 | 55 | const fp_t z = bmo::stats::runif(rand_engine); 56 | 57 | if (z < std::exp(comp_val)) { 58 | if (val_out) { 59 | *val_out = val_new; 60 | } 61 | 62 | return X_new; 63 | } else { 64 | if (val_out) { 65 | *val_out = val_prev; 66 | } 67 | 68 | return X_prev; 69 | } 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /include/mcmc/mala.ipp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Metropolis-adjusted Langevin algorithm 23 | */ 24 | 25 | #ifndef _mcmc_mala_IPP 26 | #define _mcmc_mala_IPP 27 | 28 | // 29 | 30 | inline 31 | fp_t 32 | mala_prop_adjustment( 33 | const ColVec_t& prop_vals, 34 | const ColVec_t& prev_vals, 35 | const fp_t step_size, 36 | const bool vals_bound, 37 | const Mat_t& precond_mat, 38 | std::function mala_mean_fn, 39 | void* target_data 40 | ) 41 | { 42 | 43 | fp_t ret_val = 0; 44 | const fp_t step_size_sq = step_size * step_size; 45 | 46 | // 47 | 48 | if (vals_bound) 49 | { 50 | Mat_t prop_inv_jacob, prev_inv_jacob; 51 | 52 | ColVec_t prop_mean = mala_mean_fn(prop_vals, target_data, step_size, &prop_inv_jacob); 53 | ColVec_t prev_mean = mala_mean_fn(prev_vals, target_data, step_size, &prev_inv_jacob); 54 | 55 | ret_val = stats_mcmc::dmvnorm(prev_vals, prop_mean, step_size_sq*prop_inv_jacob*precond_mat, true) \ 56 | - stats_mcmc::dmvnorm(prop_vals, prev_mean, step_size_sq*prop_inv_jacob*precond_mat, true); 57 | } 58 | else 59 | { 60 | ColVec_t prop_mean = mala_mean_fn(prop_vals, target_data, step_size, nullptr); 61 | ColVec_t prev_mean = mala_mean_fn(prev_vals, target_data, step_size, nullptr); 62 | 63 | ret_val = stats_mcmc::dmvnorm(prev_vals, prop_mean, step_size_sq*precond_mat, true) \ 64 | - stats_mcmc::dmvnorm(prop_vals, prev_mean, step_size_sq*precond_mat, true); 65 | } 66 | 67 | // 68 | 69 | return ret_val; 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /docs/source/box_constraints.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | Box Constraints 8 | =============== 9 | 10 | This section provides implementation details for how MCMCLib handles box constraints. 11 | 12 | For a parameter :math:`\theta_j` defined on a bounded interval :math:`[a_j,b_j]`, where :math:`a_j < b_j`, we use the generalized logit transform: 13 | 14 | .. math:: 15 | 16 | g(\theta_j) = \phi_j := \ln \left( \frac{x_j - a_j}{b_j - x_j} \right) 17 | 18 | with corresponding inverse transform 19 | 20 | .. math:: 21 | \theta_j = g^{-1} (\phi_j) = \frac{a_j + b_j \exp(\phi_j)}{1 + \exp(\phi_j)} 22 | 23 | Note that the support of :math:`\phi_j` is :math:`\mathbb{R}`. 24 | 25 | The log posterior kernel function of the :math:`J \times 1` vector :math:`\boldsymbol{\theta}` is given by: 26 | 27 | .. math:: 28 | 29 | K(\boldsymbol{\theta} | Y) = \ln L( Y | \boldsymbol{\theta} ) + \ln \pi (\boldsymbol{\theta}) 30 | 31 | where :math:`L` is the likelihood function of the data :math:`Y` parametrized by :math:`\boldsymbol{\theta}`, and :math:`\pi` is the joint prior distribution over :math:`\boldsymbol{\theta}`. 32 | The log posterior kernel function of the transformed parameter vector :math:`\boldsymbol{\phi} = g(\boldsymbol{\theta})` is then computed as 33 | 34 | .. math:: 35 | 36 | K(\boldsymbol{\phi} | Y) = \ln L( Y | g^{-1} (\boldsymbol{\phi}) ) + \ln \pi (g^{-1} (\boldsymbol{\phi})) + \ln | J(\boldsymbol{\phi}) | 37 | 38 | where :math:`|J|` is the modulus of the Jacobian determinant matrix :math:`J`---that is, the determinant of a matrix with :math:`(i,j)` elements equal to 39 | 40 | .. math:: 41 | 42 | \frac{\partial [g^{-1} (\phi)]_i}{\partial \phi_j} = \frac{\partial \theta_i}{\partial \phi_j} 43 | 44 | If the parameters :math:`\boldsymbol{\theta}` are assumed to be *a-priori* independent, then 45 | 46 | .. math:: 47 | \pi (\boldsymbol{\theta}) = \pi_1 (\theta_1) \cdots \pi_J (\theta_J) 48 | 49 | Given our specification for :math:`g`, the Jacobian term will be a diagonal matrix with non-negative elements: the log Jacobian adjustment for parameter :math:`j` is given by 50 | 51 | .. math:: 52 | \ln J_{j,j} := \ln \left( \frac{d \theta_j}{d \phi_j} \right) = \ln(b_j - a_j) + \phi_j - 2 \ln (1 + \exp(\phi_j)) 53 | 54 | As the determinant of a diagonal matrix is the product of its diagonal elements, the final term in can be computed as a sum: 55 | 56 | .. math:: 57 | \ln | J(\boldsymbol{\phi}) | = \sum_{j=1}^J \ln | J_{j,j} | = \sum_{j=1}^J \left[ \ln(b_j - a_j) + \phi_j - 2 \ln (1 + \exp(\phi_j)) \right] 58 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | Introduction 8 | ============ 9 | 10 | MCMCLib is a lightweight C++ library of Markov Chain Monte Carlo (MCMC) methods. 11 | 12 | Features: 13 | 14 | - A C++11/14/17 library of well-known MCMC algorithms. 15 | 16 | - Parallelized samplers designed for multi-modal distributions, including: 17 | 18 | - Differential Evolution (DE); and 19 | 20 | - Adaptive Equi-Energy Sampler (AEES). 21 | 22 | - For fast and efficient matrix-based computation, MCMCib supports the following templated linear algebra libraries: 23 | 24 | - `Armadillo `_ 25 | 26 | - `Eigen `_ (version >= 3.4.0) 27 | 28 | - Automatic differentiation functionality is available through use of the `Autodiff library `_ 29 | 30 | - OpenMP-accelerated algorithms for parallel computation. 31 | 32 | - Straightforward linking with parallelized BLAS libraries, such as `OpenBLAS `_. 33 | 34 | - Available as a header-only library, or as a compiled shared library. 35 | 36 | - Released under a permissive, non-GPL license. 37 | 38 | Author: Keith O'Hara 39 | 40 | License: Apache Version 2.0 41 | 42 | ---- 43 | 44 | Installation 45 | ------------ 46 | 47 | The library can be installed on Unix-alike systems via the standard ``./configure && make`` method. 48 | 49 | See the installation page for :ref:`detailed instructions`. 50 | 51 | Algorithms 52 | ---------- 53 | 54 | A list of currently available algorithms includes: 55 | 56 | * :ref:`Adaptive Equi-Energy Sampler (AEES)` 57 | * :ref:`Differential Evolution (DE-MCMC)` 58 | * :ref:`Hamiltonian Monte Carlo (HMC)` 59 | * :ref:`Metropolis-adjusted Langevin algorithm (MALA)` 60 | * :ref:`No-U-Turn Sampler (NUTS)` 61 | * :ref:`Random Walk Metropolis-Hastings (RWMH)` 62 | * :ref:`Riemannian Manifold Hamiltonian Monte Carlo (RM-HMC)` 63 | 64 | ---- 65 | 66 | Contents 67 | -------- 68 | 69 | .. toctree:: 70 | :caption: Guide 71 | :maxdepth: 2 72 | 73 | installation 74 | examples_and_tests 75 | settings 76 | autodiff 77 | 78 | .. toctree:: 79 | :caption: Algorithms 80 | :maxdepth: 1 81 | 82 | api/aees 83 | api/de 84 | api/hmc 85 | api/mala 86 | api/nuts 87 | api/rwmh 88 | api/rmhmc 89 | 90 | .. toctree:: 91 | :caption: Appendix 92 | :maxdepth: 2 93 | 94 | box_constraints 95 | -------------------------------------------------------------------------------- /include/mcmc/rwmh.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Random Walk Metropolis-Hastings (RWMH) MCMC 23 | */ 24 | 25 | #ifndef _mcmc_rwmh_HPP 26 | #define _mcmc_rwmh_HPP 27 | 28 | /** 29 | * @brief The Random Walk Metropolis-Hastings MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c target_data additional data passed to the user-provided function. 35 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 36 | * @param target_data additional data passed to the user-provided function. 37 | * 38 | * @return a boolean value indicating successful completion of the algorithm. 39 | */ 40 | 41 | bool 42 | rwmh( 43 | const ColVec_t& initial_vals, 44 | std::function target_log_kernel, 45 | Mat_t& draws_out, 46 | void* target_data 47 | ); 48 | 49 | /** 50 | * @brief The Random Walk Metropolis-Hastings MCMC Algorithm 51 | * 52 | * @param initial_vals a column vector of initial values. 53 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 54 | * - \c vals_inp a vector of inputs; and 55 | * - \c target_data additional data passed to the user-provided function. 56 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 57 | * @param target_data additional data passed to the user-provided function. 58 | * @param settings parameters controlling the MCMC routine. 59 | * 60 | * @return a boolean value indicating successful completion of the algorithm. 61 | */ 62 | 63 | bool 64 | rwmh( 65 | const ColVec_t& initial_vals, 66 | std::function target_log_kernel, 67 | Mat_t& draws_out, 68 | void* target_data, 69 | algo_settings_t& settings 70 | ); 71 | 72 | // 73 | // internal 74 | 75 | namespace internal 76 | { 77 | 78 | bool 79 | rwmh_impl( 80 | const ColVec_t& initial_vals, 81 | std::function target_log_kernel, 82 | Mat_t& draws_out, 83 | void* target_data, 84 | algo_settings_t* settings_inp 85 | ); 86 | 87 | } 88 | 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /include/mcmc/aees.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Adaptive Equi-Energy Sampler 23 | */ 24 | 25 | #ifndef _mcmc_aees_HPP 26 | #define _mcmc_aees_HPP 27 | 28 | /** 29 | * @brief The Adaptive Equi-Energy MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c target_data additional data passed to the user-provided function. 35 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 36 | * @param target_data additional data passed to the user-provided function. 37 | * 38 | * @return a boolean value indicating successful completion of the sampling algorithm. 39 | */ 40 | 41 | bool 42 | aees( 43 | const ColVec_t& initial_vals, 44 | std::function target_log_kernel, 45 | Mat_t& draws_out, 46 | void* target_data 47 | ); 48 | 49 | /** 50 | * @brief The Adaptive Equi-Energy MCMC Algorithm 51 | * 52 | * @param initial_vals a column vector of initial values. 53 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 54 | * - \c vals_inp a vector of inputs; and 55 | * - \c target_data additional data passed to the user-provided function. 56 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 57 | * @param target_data additional data passed to the user-provided function. 58 | * @param settings parameters controlling the sampling algorithm. 59 | * 60 | * @return a boolean value indicating successful completion of the sampling algorithm. 61 | */ 62 | 63 | bool 64 | aees( 65 | const ColVec_t& initial_vals, 66 | std::function target_log_kernel, 67 | Mat_t& draws_out, 68 | void* target_data, 69 | algo_settings_t& settings 70 | ); 71 | 72 | // 73 | // internal 74 | 75 | namespace internal 76 | { 77 | 78 | bool 79 | aees_impl( 80 | const ColVec_t& initial_vals, 81 | std::function target_log_kernel, 82 | Mat_t& draws_out, 83 | void* target_data, 84 | algo_settings_t* settings_inp 85 | ); 86 | 87 | #include "aees.ipp" 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /include/mcmc/de.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Differential Evolution (DE) MCMC 23 | */ 24 | 25 | #ifndef _mcmc_de_HPP 26 | #define _mcmc_de_HPP 27 | 28 | /** 29 | * @brief The Differential Evolution MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c target_data additional data passed to the user-provided function. 35 | * @param draws_out a 3-dimensional array posterior draws, where each matrix represents one draw. 36 | * @param target_data additional data passed to the user-provided function. 37 | * 38 | * @return a boolean value indicating successful completion of the sampling algorithm. 39 | */ 40 | 41 | bool 42 | de( 43 | const ColVec_t& initial_vals, 44 | std::function target_log_kernel, 45 | Cube_t& draws_out, 46 | void* target_data 47 | ); 48 | 49 | /** 50 | * @brief The Differential Evolution MCMC Algorithm 51 | * 52 | * @param initial_vals a column vector of initial values. 53 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking two arguments: 54 | * - \c vals_inp a vector of inputs; and 55 | * - \c target_data additional data passed to the user-provided function. 56 | * @param draws_out a 3-dimensional array posterior draws, where each matrix represents one draw. 57 | * @param target_data additional data passed to the user-provided function. 58 | * @param settings parameters controlling the sampling algorithm. 59 | * 60 | * @return a boolean value indicating successful completion of the sampling algorithm. 61 | */ 62 | 63 | bool 64 | de( 65 | const ColVec_t& initial_vals, 66 | std::function target_log_kernel, 67 | Cube_t& draws_out, 68 | void* target_data, 69 | algo_settings_t& settings 70 | ); 71 | 72 | namespace internal 73 | { 74 | 75 | bool 76 | de_impl( 77 | const ColVec_t& initial_vals, 78 | std::function target_log_kernel, 79 | Cube_t& draws_out, 80 | void* target_data, 81 | algo_settings_t* settings_inp 82 | ); 83 | 84 | inline 85 | fp_t 86 | de_cooling_schedule(const size_t s, const size_t n_gen) 87 | { 88 | return fp_t(1); 89 | } 90 | 91 | } 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /include/mcmc/hmc.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Hamiltonian Monte Carlo 23 | */ 24 | 25 | #ifndef _mcmc_hmc_HPP 26 | #define _mcmc_hmc_HPP 27 | 28 | /** 29 | * @brief The Hamiltonian Monte Carlo (HMC) MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c grad_out a vector to store the gradient; and 35 | * - \c target_data additional data passed to the user-provided function. 36 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 37 | * @param target_data additional data passed to the user-provided function. 38 | * 39 | * @return a boolean value indicating successful completion of the algorithm. 40 | */ 41 | 42 | bool 43 | hmc( 44 | const ColVec_t& initial_vals, 45 | std::function target_log_kernel, 46 | Mat_t& draws_out, 47 | void* target_data 48 | ); 49 | 50 | /** 51 | * @brief The Hamiltonian Monte Carlo (HMC) MCMC Algorithm 52 | * 53 | * @param initial_vals a column vector of initial values. 54 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 55 | * - \c vals_inp a vector of inputs; and 56 | * - \c grad_out a vector to store the gradient; and 57 | * - \c target_data additional data passed to the user-provided function. 58 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 59 | * @param target_data additional data passed to the user-provided function. 60 | * @param settings parameters controlling the MCMC routine. 61 | * 62 | * @return a boolean value indicating successful completion of the algorithm. 63 | */ 64 | 65 | bool 66 | hmc( 67 | const ColVec_t& initial_vals, 68 | std::function target_log_kernel, 69 | Mat_t& draws_out, 70 | void* target_data, 71 | algo_settings_t& settings 72 | ); 73 | 74 | 75 | namespace internal 76 | { 77 | 78 | bool 79 | hmc_impl( 80 | const ColVec_t& initial_vals, 81 | std::function target_log_kernel, 82 | Mat_t& draws_out, 83 | void* target_data, 84 | algo_settings_t* settings_inp 85 | ); 86 | 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /include/mcmc/nuts.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * No-U-Turn Sampler (NUTS) (with Dual Averaging) 23 | */ 24 | 25 | #ifndef _mcmc_nuts_HPP 26 | #define _mcmc_nuts_HPP 27 | 28 | /** 29 | * @brief The No-U-Turn Sampler (NUTS) MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c grad_out a vector to store the gradient; and 35 | * - \c target_data additional data passed to the user-provided function. 36 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 37 | * @param target_data additional data passed to the user-provided function. 38 | * 39 | * @return a boolean value indicating successful completion of the algorithm. 40 | */ 41 | 42 | bool 43 | nuts( 44 | const ColVec_t& initial_vals, 45 | std::function target_log_kernel, 46 | Mat_t& draws_out, 47 | void* target_data 48 | ); 49 | 50 | /** 51 | * @brief The No-U-Turn Sampler (NUTS) MCMC Algorithm 52 | * 53 | * @param initial_vals a column vector of initial values. 54 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 55 | * - \c vals_inp a vector of inputs; and 56 | * - \c grad_out a vector to store the gradient; and 57 | * - \c target_data additional data passed to the user-provided function. 58 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 59 | * @param target_data additional data passed to the user-provided function. 60 | * @param settings parameters controlling the MCMC routine. 61 | * 62 | * @return a boolean value indicating successful completion of the algorithm. 63 | */ 64 | 65 | bool 66 | nuts( 67 | const ColVec_t& initial_vals, 68 | std::function target_log_kernel, 69 | Mat_t& draws_out, 70 | void* target_data, 71 | algo_settings_t& settings 72 | ); 73 | 74 | 75 | namespace internal 76 | { 77 | 78 | bool 79 | nuts_impl( 80 | const ColVec_t& initial_vals, 81 | std::function target_log_kernel, 82 | Mat_t& draws_out, 83 | void* target_data, 84 | algo_settings_t* settings_inp 85 | ); 86 | 87 | #include "nuts.ipp" 88 | 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /include/mcmc/mala.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Metropolis-adjusted Langevin algorithm 23 | */ 24 | 25 | #ifndef _mcmc_mala_HPP 26 | #define _mcmc_mala_HPP 27 | 28 | 29 | /** 30 | * @brief The Metropolis-adjusted Langevin Algorithm (MALA) 31 | * 32 | * @param initial_vals a column vector of initial values. 33 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 34 | * - \c vals_inp a vector of inputs; and 35 | * - \c grad_out a vector to store the gradient; and 36 | * - \c target_data additional data passed to the user-provided function. 37 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 38 | * @param target_data additional data passed to the user-provided function. 39 | * 40 | * @return a boolean value indicating successful completion of the algorithm. 41 | */ 42 | 43 | bool 44 | mala( 45 | const ColVec_t& initial_vals, 46 | std::function target_log_kernel, 47 | Mat_t& draws_out, 48 | void* target_data 49 | ); 50 | 51 | /** 52 | * @brief The Metropolis-adjusted Langevin Algorithm (MALA) 53 | * 54 | * @param initial_vals a column vector of initial values. 55 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 56 | * - \c vals_inp a vector of inputs; and 57 | * - \c grad_out a vector to store the gradient; and 58 | * - \c target_data additional data passed to the user-provided function. 59 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 60 | * @param target_data additional data passed to the user-provided function. 61 | * @param settings parameters controlling the MCMC routine. 62 | * 63 | * @return a boolean value indicating successful completion of the algorithm. 64 | */ 65 | 66 | bool 67 | mala( 68 | const ColVec_t& initial_vals, 69 | std::function target_log_kernel, 70 | Mat_t& draws_out, 71 | void* target_data, 72 | algo_settings_t& settings 73 | ); 74 | 75 | 76 | namespace internal 77 | { 78 | 79 | bool 80 | mala_impl( 81 | const ColVec_t& initial_vals, 82 | std::function target_log_kernel, 83 | Mat_t& draws_out, 84 | void* target_data, 85 | algo_settings_t* settings_inp 86 | ); 87 | 88 | #include "mala.ipp" 89 | 90 | } 91 | 92 | // 93 | 94 | 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /examples/armadillo/nuts_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using NUTS 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ nuts_normal.cpp -o nuts_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | arma::vec x; 32 | }; 33 | 34 | double ll_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 35 | { 36 | const double pi = arma::datum::pi; 37 | 38 | const double mu = vals_inp(0); 39 | const double sigma = vals_inp(1); 40 | 41 | norm_data_t* dta = reinterpret_cast(ll_data); 42 | const arma::vec x = dta->x; 43 | const int n_vals = x.n_rows; 44 | 45 | // 46 | 47 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 48 | 49 | // 50 | 51 | if (grad_out) { 52 | grad_out->set_size(2,1); 53 | 54 | // 55 | 56 | const double m_1 = arma::accu(x - mu); 57 | const double m_2 = arma::accu( arma::pow(x - mu,2) ); 58 | 59 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 60 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 61 | } 62 | 63 | // 64 | 65 | return ret; 66 | } 67 | 68 | double log_target_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 69 | { 70 | return ll_dens(vals_inp,grad_out,ll_data); 71 | } 72 | 73 | int main() 74 | { 75 | const int n_data = 1000; 76 | 77 | const double mu = 2.0; 78 | const double sigma = 2.0; 79 | 80 | norm_data_t dta; 81 | 82 | arma::vec x_dta = mu + sigma * arma::randn(n_data,1); 83 | dta.x = x_dta; 84 | 85 | arma::vec initial_val(2); 86 | initial_val(0) = mu + 1; // mu 87 | initial_val(1) = sigma + 1; // sigma 88 | 89 | // 90 | 91 | mcmc::algo_settings_t settings; 92 | 93 | settings.nuts_settings.n_burnin_draws = 2000; 94 | settings.nuts_settings.n_keep_draws = 2000; 95 | 96 | // 97 | 98 | arma::mat draws_out; 99 | mcmc::nuts(initial_val, log_target_dens, draws_out, &dta, settings); 100 | 101 | // 102 | 103 | std::cout << "nuts mean:\n" << arma::mean(draws_out) << std::endl; 104 | std::cout << "acceptance rate: " << static_cast(settings.nuts_settings.n_accept_draws) / settings.nuts_settings.n_keep_draws << std::endl; 105 | 106 | // 107 | 108 | return 0; 109 | } 110 | -------------------------------------------------------------------------------- /examples/armadillo/hmc_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using HMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ hmc_normal.cpp -o hmc_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | arma::vec x; 32 | }; 33 | 34 | double ll_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 35 | { 36 | const double pi = arma::datum::pi; 37 | 38 | const double mu = vals_inp(0); 39 | const double sigma = vals_inp(1); 40 | 41 | norm_data_t* dta = reinterpret_cast(ll_data); 42 | const arma::vec x = dta->x; 43 | const int n_vals = x.n_rows; 44 | 45 | // 46 | 47 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 48 | 49 | // 50 | 51 | if (grad_out) { 52 | grad_out->set_size(2,1); 53 | 54 | // 55 | 56 | const double m_1 = arma::accu(x - mu); 57 | const double m_2 = arma::accu( arma::pow(x - mu,2) ); 58 | 59 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 60 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 61 | } 62 | 63 | // 64 | 65 | return ret; 66 | } 67 | 68 | double log_target_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 69 | { 70 | return ll_dens(vals_inp,grad_out,ll_data); 71 | } 72 | 73 | int main() 74 | { 75 | const int n_data = 1000; 76 | 77 | const double mu = 2.0; 78 | const double sigma = 2.0; 79 | 80 | norm_data_t dta; 81 | 82 | arma::vec x_dta = mu + sigma * arma::randn(n_data,1); 83 | dta.x = x_dta; 84 | 85 | arma::vec initial_val(2); 86 | initial_val(0) = mu + 1; // mu 87 | initial_val(1) = sigma + 1; // sigma 88 | 89 | // 90 | 91 | mcmc::algo_settings_t settings; 92 | 93 | settings.hmc_settings.step_size = 0.08; 94 | settings.hmc_settings.n_burnin_draws = 2000; 95 | settings.hmc_settings.n_keep_draws = 2000; 96 | 97 | // 98 | 99 | arma::mat draws_out; 100 | mcmc::hmc(initial_val, log_target_dens, draws_out, &dta, settings); 101 | 102 | // 103 | 104 | std::cout << "hmc mean:\n" << arma::mean(draws_out) << std::endl; 105 | std::cout << "acceptance rate: " << static_cast(settings.hmc_settings.n_accept_draws) / settings.hmc_settings.n_keep_draws << std::endl; 106 | 107 | // 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/armadillo/mala_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using MALA 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ mala_normal.cpp -o mala_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | arma::vec x; 32 | }; 33 | 34 | double ll_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 35 | { 36 | const double pi = arma::datum::pi; 37 | 38 | const double mu = vals_inp(0); 39 | const double sigma = vals_inp(1); 40 | 41 | norm_data_t* dta = reinterpret_cast(ll_data); 42 | const arma::vec x = dta->x; 43 | const int n_vals = x.n_rows; 44 | 45 | // 46 | 47 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 48 | 49 | // 50 | 51 | if (grad_out) { 52 | grad_out->set_size(2,1); 53 | 54 | // 55 | 56 | const double m_1 = arma::accu(x - mu); 57 | const double m_2 = arma::accu( arma::pow(x - mu,2) ); 58 | 59 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 60 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 61 | } 62 | 63 | // 64 | 65 | return ret; 66 | } 67 | 68 | double log_target_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 69 | { 70 | return ll_dens(vals_inp,grad_out,ll_data); 71 | } 72 | 73 | int main() 74 | { 75 | const int n_data = 1000; 76 | 77 | const double mu = 2.0; 78 | const double sigma = 2.0; 79 | 80 | norm_data_t dta; 81 | 82 | arma::vec x_dta = mu + sigma * arma::randn(n_data,1); 83 | dta.x = x_dta; 84 | 85 | arma::vec initial_val(2); 86 | initial_val(0) = mu + 1; // mu 87 | initial_val(1) = sigma + 1; // sigma 88 | 89 | // 90 | 91 | mcmc::algo_settings_t settings; 92 | 93 | settings.mala_settings.step_size = 0.08; 94 | settings.mala_settings.n_burnin_draws = 2000; 95 | settings.mala_settings.n_keep_draws = 2000; 96 | 97 | // 98 | 99 | arma::mat draws_out; 100 | mcmc::mala(initial_val, log_target_dens, draws_out, &dta, settings); 101 | 102 | // 103 | 104 | std::cout << "mala mean:\n" << arma::mean(draws_out) << std::endl; 105 | std::cout << "acceptance rate: " << static_cast(settings.mala_settings.n_accept_draws) / settings.mala_settings.n_keep_draws << std::endl; 106 | 107 | // 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/armadillo/rwmh_normal_mean.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using RWMH 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ rwmh_normal_mean.cpp -o rwmh_normal_mean.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | double sigma; 32 | arma::vec x; 33 | 34 | double mu_0; 35 | double sigma_0; 36 | }; 37 | 38 | double ll_dens(const arma::vec& vals_inp, void* ll_data) 39 | { 40 | const double pi = arma::datum::pi; 41 | 42 | // 43 | 44 | const double mu = vals_inp(0); 45 | 46 | norm_data_t* dta = reinterpret_cast(ll_data); 47 | const double sigma = dta->sigma; 48 | const arma::vec x = dta->x; 49 | 50 | const int n_vals = x.n_rows; 51 | 52 | // 53 | 54 | const double ret = - ((double) n_vals) * (0.5*std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 55 | 56 | // 57 | 58 | return ret; 59 | } 60 | 61 | double log_pr_dens(const arma::vec& vals_inp, void* ll_data) 62 | { 63 | const double pi = arma::datum::pi; 64 | 65 | // 66 | 67 | norm_data_t* dta = reinterpret_cast< norm_data_t* >(ll_data); 68 | 69 | const double mu_0 = dta->mu_0; 70 | const double sigma_0 = dta->sigma_0; 71 | 72 | const double x = vals_inp(0); 73 | 74 | const double ret = - 0.5*std::log(2*pi) - std::log(sigma_0) - std::pow(x - mu_0,2) / (2*sigma_0*sigma_0); 75 | 76 | return ret; 77 | } 78 | 79 | double log_target_dens(const arma::vec& vals_inp, void* ll_data) 80 | { 81 | return ll_dens(vals_inp,ll_data) + log_pr_dens(vals_inp,ll_data); 82 | } 83 | 84 | int main() 85 | { 86 | const int n_data = 100; 87 | const double mu = 2.0; 88 | 89 | norm_data_t dta; 90 | dta.sigma = 1.0; 91 | dta.mu_0 = 1.0; 92 | dta.sigma_0 = 2.0; 93 | 94 | arma::vec x_dta = mu + arma::randn(n_data,1); 95 | dta.x = x_dta; 96 | 97 | arma::vec initial_val(1); 98 | initial_val(0) = 1.0; 99 | 100 | // 101 | 102 | mcmc::algo_settings_t settings; 103 | 104 | settings.rwmh_settings.par_scale = 0.4; 105 | settings.rwmh_settings.n_burnin_draws = 2000; 106 | settings.rwmh_settings.n_keep_draws = 2000; 107 | 108 | // 109 | 110 | arma::mat draws_out; 111 | mcmc::rwmh(initial_val, log_target_dens, draws_out, &dta, settings); 112 | 113 | // 114 | 115 | std::cout << "rwmh mean:\n" << arma::mean(draws_out) << std::endl; 116 | std::cout << "acceptance rate: " << static_cast(settings.rwmh_settings.n_accept_draws) / settings.rwmh_settings.n_keep_draws << std::endl; 117 | 118 | // 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /examples/armadillo/de_normal_mean.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using DE-MCMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ de_normal_mean.cpp -o de_normal_mean.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | double sigma; 32 | arma::vec x; 33 | 34 | double mu_0; 35 | double sigma_0; 36 | }; 37 | 38 | double ll_dens(const arma::vec& vals_inp, void* ll_data) 39 | { 40 | const double pi = arma::datum::pi; 41 | 42 | // 43 | 44 | const double mu = vals_inp(0); 45 | 46 | norm_data_t* dta = reinterpret_cast(ll_data); 47 | const double sigma = dta->sigma; 48 | const arma::vec x = dta->x; 49 | 50 | const int n_vals = x.n_rows; 51 | 52 | // 53 | 54 | const double ret = - ((double) n_vals) * (0.5*std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 55 | 56 | // 57 | 58 | return ret; 59 | } 60 | 61 | double log_pr_dens(const arma::vec& vals_inp, void* ll_data) 62 | { 63 | const double pi = arma::datum::pi; 64 | 65 | // 66 | 67 | norm_data_t* dta = reinterpret_cast< norm_data_t* >(ll_data); 68 | 69 | const double mu_0 = dta->mu_0; 70 | const double sigma_0 = dta->sigma_0; 71 | 72 | const double x = vals_inp(0); 73 | 74 | const double ret = - 0.5*std::log(2*pi) - std::log(sigma_0) - std::pow(x - mu_0,2) / (2*sigma_0*sigma_0); 75 | 76 | return ret; 77 | } 78 | 79 | double log_target_dens(const arma::vec& vals_inp, void* ll_data) 80 | { 81 | return ll_dens(vals_inp,ll_data) + log_pr_dens(vals_inp,ll_data); 82 | } 83 | 84 | int main() 85 | { 86 | const int n_data = 100; 87 | const double mu = 2.0; 88 | 89 | norm_data_t dta; 90 | dta.sigma = 1.0; 91 | dta.mu_0 = 1.0; 92 | dta.sigma_0 = 2.0; 93 | 94 | arma::vec x_dta = mu + arma::randn(n_data,1); 95 | dta.x = x_dta; 96 | 97 | arma::vec initial_val(1); 98 | initial_val(0) = 1.0; 99 | 100 | // 101 | 102 | mcmc::algo_settings_t settings; 103 | 104 | settings.de_settings.n_burnin_draws = 2000; 105 | settings.de_settings.n_keep_draws = 2000; 106 | 107 | // 108 | 109 | mcmc::Cube_t draws_out; 110 | mcmc::de(initial_val, log_target_dens, draws_out, &dta, settings); 111 | 112 | // 113 | 114 | std::cout << "de mean:\n" << arma::mean(draws_out.mat(settings.de_settings.n_keep_draws - 1)) << std::endl; 115 | std::cout << "acceptance rate: " << static_cast(settings.de_settings.n_accept_draws) / (settings.de_settings.n_keep_draws * settings.de_settings.n_pop) << std::endl; 116 | 117 | // 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /examples/eigen/nuts_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using NUTS 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ nuts_normal.cpp -o nuts_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | Eigen::VectorXd x; 42 | }; 43 | 44 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 45 | { 46 | const double pi = 3.14159265358979; 47 | 48 | const double mu = vals_inp(0); 49 | const double sigma = vals_inp(1); 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | const int n_vals = x.size(); 54 | 55 | // 56 | 57 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 58 | 59 | // 60 | 61 | if (grad_out) { 62 | grad_out->resize(2,1); 63 | 64 | // 65 | 66 | const double m_1 = (x.array() - mu).sum(); 67 | const double m_2 = (x.array() - mu).pow(2).sum(); 68 | 69 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 70 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 71 | } 72 | 73 | // 74 | 75 | return ret; 76 | } 77 | 78 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 79 | { 80 | return ll_dens(vals_inp,grad_out,ll_data); 81 | } 82 | 83 | int main() 84 | { 85 | const int n_data = 1000; 86 | 87 | const double mu = 2.0; 88 | const double sigma = 2.0; 89 | 90 | norm_data_t dta; 91 | 92 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 93 | dta.x = x_dta; 94 | 95 | Eigen::VectorXd initial_val(2); 96 | initial_val(0) = mu + 1; // mu 97 | initial_val(1) = sigma + 1; // sigma 98 | 99 | mcmc::algo_settings_t settings; 100 | 101 | settings.nuts_settings.n_burnin_draws = 2000; 102 | settings.nuts_settings.n_keep_draws = 2000; 103 | 104 | // 105 | 106 | Eigen::MatrixXd draws_out; 107 | mcmc::nuts(initial_val, log_target_dens, draws_out, &dta, settings); 108 | 109 | // 110 | 111 | std::cout << "nuts mean:\n" << draws_out.colwise().mean() << std::endl; 112 | std::cout << "acceptance rate: " << static_cast(settings.nuts_settings.n_accept_draws) / settings.nuts_settings.n_keep_draws << std::endl; 113 | 114 | // 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /examples/eigen/hmc_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using HMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ hmc_normal.cpp -o hmc_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | Eigen::VectorXd x; 42 | }; 43 | 44 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 45 | { 46 | const double pi = 3.14159265358979; 47 | 48 | const double mu = vals_inp(0); 49 | const double sigma = vals_inp(1); 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | const int n_vals = x.size(); 54 | 55 | // 56 | 57 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 58 | 59 | // 60 | 61 | if (grad_out) { 62 | grad_out->resize(2,1); 63 | 64 | // 65 | 66 | const double m_1 = (x.array() - mu).sum(); 67 | const double m_2 = (x.array() - mu).pow(2).sum(); 68 | 69 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 70 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 71 | } 72 | 73 | // 74 | 75 | return ret; 76 | } 77 | 78 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 79 | { 80 | return ll_dens(vals_inp,grad_out,ll_data); 81 | } 82 | 83 | int main() 84 | { 85 | const int n_data = 1000; 86 | 87 | const double mu = 2.0; 88 | const double sigma = 2.0; 89 | 90 | norm_data_t dta; 91 | 92 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 93 | dta.x = x_dta; 94 | 95 | Eigen::VectorXd initial_val(2); 96 | initial_val(0) = mu + 1; // mu 97 | initial_val(1) = sigma + 1; // sigma 98 | 99 | mcmc::algo_settings_t settings; 100 | 101 | settings.hmc_settings.step_size = 0.08; 102 | settings.hmc_settings.n_burnin_draws = 2000; 103 | settings.hmc_settings.n_keep_draws = 2000; 104 | 105 | // 106 | 107 | Eigen::MatrixXd draws_out; 108 | mcmc::hmc(initial_val, log_target_dens, draws_out, &dta, settings); 109 | 110 | // 111 | 112 | std::cout << "hmc mean:\n" << draws_out.colwise().mean() << std::endl; 113 | std::cout << "acceptance rate: " << static_cast(settings.hmc_settings.n_accept_draws) / settings.hmc_settings.n_keep_draws << std::endl; 114 | 115 | // 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /examples/eigen/mala_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using MALA 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ mala_normal.cpp -o mala_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | Eigen::VectorXd x; 42 | }; 43 | 44 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 45 | { 46 | const double pi = 3.14159265358979; 47 | 48 | const double mu = vals_inp(0); 49 | const double sigma = vals_inp(1); 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | const int n_vals = x.size(); 54 | 55 | // 56 | 57 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 58 | 59 | // 60 | 61 | if (grad_out) { 62 | grad_out->resize(2,1); 63 | 64 | // 65 | 66 | const double m_1 = (x.array() - mu).sum(); 67 | const double m_2 = (x.array() - mu).pow(2).sum(); 68 | 69 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 70 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 71 | } 72 | 73 | // 74 | 75 | return ret; 76 | } 77 | 78 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 79 | { 80 | return ll_dens(vals_inp,grad_out,ll_data); 81 | } 82 | 83 | int main() 84 | { 85 | const int n_data = 1000; 86 | 87 | const double mu = 2.0; 88 | const double sigma = 2.0; 89 | 90 | norm_data_t dta; 91 | 92 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 93 | dta.x = x_dta; 94 | 95 | Eigen::VectorXd initial_val(2); 96 | initial_val(0) = mu + 1; // mu 97 | initial_val(1) = sigma + 1; // sigma 98 | 99 | mcmc::algo_settings_t settings; 100 | 101 | settings.mala_settings.step_size = 0.08; 102 | settings.mala_settings.n_burnin_draws = 2000; 103 | settings.mala_settings.n_keep_draws = 2000; 104 | 105 | // 106 | 107 | Eigen::MatrixXd draws_out; 108 | mcmc::mala(initial_val, log_target_dens, draws_out, &dta, settings); 109 | 110 | // 111 | 112 | std::cout << "mala mean:\n" << draws_out.colwise().mean() << std::endl; 113 | std::cout << "acceptance rate: " << static_cast(settings.mala_settings.n_accept_draws) / settings.mala_settings.n_keep_draws << std::endl; 114 | 115 | // 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /examples/armadillo/aees_mixture.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian Mixture Distribution using the Adaptive Equi-Energy Sampler (AEES) 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ aees_mixture.cpp -o aees_mixture.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct mixture_data_t { 31 | arma::mat mu; 32 | arma::vec sig_sq; 33 | arma::vec weights; 34 | }; 35 | 36 | double 37 | gaussian_mixture(const arma::vec& X_vec_inp, const arma::vec& weights, const arma::mat& mu, const arma::vec& sig_sq) 38 | { 39 | const double pi = arma::datum::pi; 40 | 41 | const int n_vals = X_vec_inp.n_elem; 42 | const int n_mix = weights.n_elem; 43 | 44 | // 45 | 46 | double dens_val = 0; 47 | 48 | for (int i = 0; i < n_mix; ++i) { 49 | const double dist_val = arma::accu(arma::pow(X_vec_inp - mu.col(i), 2)); 50 | 51 | dens_val += weights(i) * std::exp(-0.5 * dist_val / sig_sq(i)) / std::pow(2.0 * pi * sig_sq(i), static_cast(n_vals) / 2.0); 52 | } 53 | 54 | // 55 | 56 | return std::log(dens_val); 57 | } 58 | 59 | double 60 | target_log_kernel(const arma::vec& vals_inp, void* target_data) 61 | { 62 | mixture_data_t* dta = reinterpret_cast(target_data); 63 | 64 | return gaussian_mixture(vals_inp, dta->weights, dta->mu, dta->sig_sq); 65 | } 66 | 67 | int main() 68 | { 69 | const int n_vals = 2; 70 | const int n_mix = 2; 71 | 72 | // 73 | 74 | arma::mat mu = arma::ones(n_vals, n_mix) + 1.0; 75 | mu.col(0) *= -1.0; // (-2, 2) 76 | 77 | arma::vec weights(n_mix, arma::fill::value(1.0 / n_mix)); 78 | 79 | arma::vec sig_sq = 0.1 * arma::ones(n_mix); 80 | 81 | mixture_data_t dta; 82 | dta.mu = mu; 83 | dta.sig_sq = sig_sq; 84 | dta.weights = weights; 85 | 86 | // 87 | 88 | arma::vec T_vec(2); 89 | T_vec(0) = 60.0; 90 | T_vec(1) = 9.0; 91 | 92 | // settings 93 | 94 | mcmc::algo_settings_t settings; 95 | 96 | settings.aees_settings.n_initial_draws = 1000; 97 | settings.aees_settings.n_burnin_draws = 1000; 98 | settings.aees_settings.n_keep_draws = 20000; 99 | 100 | settings.aees_settings.n_rings = 11; 101 | settings.aees_settings.ee_prob_par = 0.05; 102 | settings.aees_settings.temper_vec = T_vec; 103 | 104 | settings.aees_settings.par_scale = 1.0; 105 | settings.aees_settings.cov_mat = 0.35 * arma::eye(n_vals, n_vals); 106 | 107 | // 108 | 109 | arma::mat draws_out; 110 | 111 | mcmc::aees(mu.col(0), target_log_kernel, draws_out, &dta, settings); 112 | 113 | arma::cout << "posterior mean for > 0.1:\n" << arma::mean(draws_out.elem( arma::find(draws_out > 0.1) ), 0) << arma::endl; 114 | arma::cout << "posterior mean for < -0.1:\n" << arma::mean(draws_out.elem( arma::find(draws_out < -0.1) ), 0) << arma::endl; 115 | 116 | // 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /examples/eigen/rwmh_normal_mean.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using RWMH 23 | */ 24 | 25 | // $CXX -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 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | double sigma; 42 | Eigen::VectorXd x; 43 | 44 | double mu_0; 45 | double sigma_0; 46 | }; 47 | 48 | double ll_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 49 | { 50 | const double pi = 3.14159265358979; 51 | 52 | // 53 | 54 | const double mu = vals_inp(0); 55 | 56 | norm_data_t* dta = reinterpret_cast(ll_data); 57 | const double sigma = dta->sigma; 58 | const Eigen::VectorXd x = dta->x; 59 | 60 | const int n_vals = x.size(); 61 | 62 | // 63 | 64 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 65 | 66 | // 67 | 68 | return ret; 69 | } 70 | 71 | double log_pr_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 72 | { 73 | const double pi = 3.14159265358979; 74 | 75 | // 76 | 77 | norm_data_t* dta = reinterpret_cast< norm_data_t* >(ll_data); 78 | 79 | const double mu_0 = dta->mu_0; 80 | const double sigma_0 = dta->sigma_0; 81 | 82 | const double x = vals_inp(0); 83 | 84 | const double ret = - 0.5*std::log(2*pi) - std::log(sigma_0) - std::pow(x - mu_0,2) / (2*sigma_0*sigma_0); 85 | 86 | return ret; 87 | } 88 | 89 | double log_target_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 90 | { 91 | return ll_dens(vals_inp,ll_data) + log_pr_dens(vals_inp,ll_data); 92 | } 93 | 94 | int main() 95 | { 96 | const int n_data = 100; 97 | const double mu = 2.0; 98 | 99 | norm_data_t dta; 100 | dta.sigma = 1.0; 101 | dta.mu_0 = 1.0; 102 | dta.sigma_0 = 2.0; 103 | 104 | Eigen::VectorXd x_dta = mu + eigen_randn_colvec(n_data).array(); 105 | dta.x = x_dta; 106 | 107 | Eigen::VectorXd initial_val(1); 108 | initial_val(0) = 1.0; 109 | 110 | // 111 | 112 | mcmc::algo_settings_t settings; 113 | 114 | settings.rwmh_settings.par_scale = 0.4; 115 | settings.rwmh_settings.n_burnin_draws = 2000; 116 | settings.rwmh_settings.n_keep_draws = 2000; 117 | 118 | // 119 | 120 | Eigen::MatrixXd draws_out; 121 | mcmc::rwmh(initial_val, log_target_dens, draws_out, &dta, settings); 122 | 123 | // 124 | 125 | std::cout << "rwmh mean:\n" << draws_out.colwise().mean() << std::endl; 126 | std::cout << "acceptance rate: " << static_cast(settings.rwmh_settings.n_accept_draws) / settings.rwmh_settings.n_keep_draws << std::endl; 127 | 128 | // 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /examples/eigen/de_normal_mean.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using DE-MCMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ de_normal_mean.cpp -o de_normal_mean.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | double sigma; 42 | Eigen::VectorXd x; 43 | 44 | double mu_0; 45 | double sigma_0; 46 | }; 47 | 48 | double ll_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 49 | { 50 | const double pi = 3.14159265358979; 51 | 52 | // 53 | 54 | const double mu = vals_inp(0); 55 | 56 | norm_data_t* dta = reinterpret_cast(ll_data); 57 | const double sigma = dta->sigma; 58 | const Eigen::VectorXd x = dta->x; 59 | 60 | const int n_vals = x.size(); 61 | 62 | // 63 | 64 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 65 | 66 | // 67 | 68 | return ret; 69 | } 70 | 71 | double log_pr_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 72 | { 73 | const double pi = 3.14159265358979; 74 | 75 | // 76 | 77 | norm_data_t* dta = reinterpret_cast< norm_data_t* >(ll_data); 78 | 79 | const double mu_0 = dta->mu_0; 80 | const double sigma_0 = dta->sigma_0; 81 | 82 | const double x = vals_inp(0); 83 | 84 | const double ret = - 0.5*std::log(2*pi) - std::log(sigma_0) - std::pow(x - mu_0,2) / (2*sigma_0*sigma_0); 85 | 86 | return ret; 87 | } 88 | 89 | double log_target_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 90 | { 91 | return ll_dens(vals_inp,ll_data) + log_pr_dens(vals_inp,ll_data); 92 | } 93 | 94 | int main() 95 | { 96 | const int n_data = 100; 97 | const double mu = 2.0; 98 | 99 | norm_data_t dta; 100 | dta.sigma = 1.0; 101 | dta.mu_0 = 1.0; 102 | dta.sigma_0 = 2.0; 103 | 104 | Eigen::VectorXd x_dta = mu + eigen_randn_colvec(n_data).array(); 105 | dta.x = x_dta; 106 | 107 | Eigen::VectorXd initial_val(1); 108 | initial_val(0) = 1.0; 109 | 110 | // 111 | 112 | mcmc::algo_settings_t settings; 113 | 114 | settings.de_settings.n_burnin_draws = 2000; 115 | settings.de_settings.n_keep_draws = 2000; 116 | 117 | // 118 | 119 | mcmc::Cube_t draws_out; 120 | mcmc::de(initial_val, log_target_dens, draws_out, &dta, settings); 121 | 122 | // 123 | 124 | std::cout << "de mean:\n" << draws_out.mat(settings.de_settings.n_keep_draws - 1).colwise().mean() << std::endl; 125 | std::cout << "acceptance rate: " << static_cast(settings.de_settings.n_accept_draws) / (settings.de_settings.n_keep_draws * settings.de_settings.n_pop) << std::endl; 126 | 127 | // 128 | 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | 2 | # modified version of: 3 | # https://gist.github.com/NickNaso/0d478f1481686d5bcc868cac06620a60 4 | 5 | name: CI 6 | 7 | on: [push, pull_request, release] 8 | 9 | jobs: 10 | build: 11 | name: ${{ matrix.config.name }} 12 | runs-on: ${{ matrix.config.os }} 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | config: 18 | - { 19 | name: "ubuntu_latest_gcc_arma", 20 | os: ubuntu-latest, 21 | build_type: "Release", 22 | cc: "gcc", 23 | cxx: "g++", 24 | fc: "gfortran", 25 | linalg: "arma" 26 | } 27 | - { 28 | name: "ubuntu_latest_gcc_eigen", 29 | os: ubuntu-latest, 30 | build_type: "Release", 31 | cc: "gcc", 32 | cxx: "g++", 33 | fc: "gfortran", 34 | linalg: "eigen" 35 | } 36 | - { 37 | name: "ubuntu_latest_gcc9_arma", 38 | os: ubuntu-latest, 39 | build_type: "Release", 40 | cc: "gcc-9", 41 | cxx: "g++-9", 42 | fc: "gfortran-9", 43 | linalg: "arma" 44 | } 45 | - { 46 | name: "ubuntu_latest_gcc9_eigen", 47 | os: ubuntu-latest, 48 | build_type: "Release", 49 | cc: "gcc-9", 50 | cxx: "g++-9", 51 | fc: "gfortran-9", 52 | linalg: "eigen" 53 | } 54 | - { 55 | name: "macos_latest_clang_arma", 56 | os: macos-latest, 57 | build_type: "Release", 58 | cc: "clang", 59 | cxx: "clang++", 60 | linalg: "arma" 61 | } 62 | - { 63 | name: "macos_latest_clang_eigen", 64 | os: macos-latest, 65 | build_type: "Release", 66 | cc: "clang", 67 | cxx: "clang++", 68 | linalg: "eigen" 69 | } 70 | 71 | steps: 72 | - uses: actions/checkout@v2 73 | 74 | - name: Print env 75 | run: | 76 | echo github.event.action: ${{ github.event.action }} 77 | echo github.event_name: ${{ github.event_name }} 78 | 79 | - name: Install dependencies on ubuntu 80 | if: startsWith(matrix.config.name, 'ubuntu') 81 | run: | 82 | sudo apt-get update 83 | sudo apt-get install ${{ matrix.config.cc }} ${{ matrix.config.cxx }} ${{ matrix.config.fc }} libblas-dev liblapack-dev 84 | ${{ matrix.config.cc }} --version 85 | 86 | - name: Configure 87 | shell: bash 88 | run: | 89 | export CC=${{ matrix.config.cc }} 90 | export CXX=${{ matrix.config.cxx }} 91 | export FC=${{ matrix.config.fc }} 92 | WDIR=${PWD} 93 | if [[ "${{ matrix.config.linalg }}" == "arma" ]]; then 94 | export OPTIM_TEST_USE_ARMA="y" 95 | mkdir ${WDIR}/arma_tmp 96 | git clone --single-branch https://gitlab.com/conradsnicta/armadillo-code.git ${WDIR}/arma_tmp > /dev/null 2>&1 97 | mv ${WDIR}/arma_tmp/include/* ${WDIR}/include 98 | rm -rf ${WDIR}/arma_tmp 99 | export ARMA_INCLUDE_PATH="${WDIR}/include" 100 | elif [[ "${{ matrix.config.linalg }}" == "eigen" ]]; then 101 | export OPTIM_TEST_USE_EIGEN="y" 102 | mkdir ${WDIR}/eigen_tmp 103 | git clone --single-branch https://gitlab.com/libeigen/eigen.git ${WDIR}/eigen_tmp > /dev/null 2>&1 104 | mv ${WDIR}/eigen_tmp/* ${WDIR}/include 105 | rm -rf ${WDIR}/eigen_tmp 106 | export EIGEN_INCLUDE_PATH="${WDIR}/include" 107 | else 108 | echo -e " \x1B[31m- error: unrecognized linear algebra library.\033[0m" >&2 ; 109 | echo "" 110 | exit 1 111 | fi 112 | # 113 | git submodule update --init 114 | ./configure -c -l ${{ matrix.config.linalg }} 115 | 116 | - name: Build 117 | shell: bash 118 | run: make 119 | -------------------------------------------------------------------------------- /examples/autodiff/hmc_normal_autodiff.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using HMC and Autodiff 23 | */ 24 | 25 | // $CXX -Wall -std=c++17 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I$AUTODIFF_INCLUDE_PATH -I./../../include/ hmc_normal_autodiff.cpp -o hmc_normal_autodiff.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | #include 31 | #include 32 | 33 | inline 34 | Eigen::VectorXd 35 | eigen_randn_colvec(size_t nr) 36 | { 37 | static std::mt19937 gen{ std::random_device{}() }; 38 | static std::normal_distribution<> dist; 39 | 40 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 41 | } 42 | 43 | struct norm_data_t { 44 | Eigen::VectorXd x; 45 | }; 46 | 47 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 48 | { 49 | const double pi = 3.14159265358979; 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | 54 | // 55 | 56 | autodiff::real u; 57 | autodiff::ArrayXreal xd = vals_inp.eval(); 58 | 59 | std::function normal_dens_log_form \ 60 | = [x, pi](const autodiff::ArrayXreal& vals_inp) -> autodiff::real 61 | { 62 | autodiff::real mu = vals_inp(0); 63 | autodiff::real sigma = vals_inp(1); 64 | 65 | return - x.size() * (0.5 * std::log(2*pi) + autodiff::detail::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 66 | }; 67 | 68 | // 69 | 70 | if (grad_out) { 71 | Eigen::VectorXd grad_tmp = autodiff::gradient(normal_dens_log_form, autodiff::wrt(xd), autodiff::at(xd), u); 72 | 73 | *grad_out = grad_tmp; 74 | } else { 75 | u = normal_dens_log_form(xd); 76 | } 77 | 78 | // 79 | 80 | return u.val(); 81 | } 82 | 83 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 84 | { 85 | return ll_dens(vals_inp,grad_out,ll_data); 86 | } 87 | 88 | int main() 89 | { 90 | const int n_data = 1000; 91 | 92 | const double mu = 2.0; 93 | const double sigma = 2.0; 94 | 95 | norm_data_t dta; 96 | 97 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 98 | dta.x = x_dta; 99 | 100 | Eigen::VectorXd initial_val(2); 101 | initial_val(0) = mu + 1; // mu 102 | initial_val(1) = sigma + 1; // sigma 103 | 104 | mcmc::algo_settings_t settings; 105 | 106 | settings.hmc_settings.step_size = 0.08; 107 | settings.hmc_settings.n_burnin_draws = 2000; 108 | settings.hmc_settings.n_keep_draws = 2000; 109 | 110 | // 111 | 112 | Eigen::MatrixXd draws_out; 113 | mcmc::hmc(initial_val, log_target_dens, draws_out, &dta, settings); 114 | 115 | // 116 | 117 | std::cout << "hmc mean:\n" << draws_out.colwise().mean() << std::endl; 118 | std::cout << "acceptance rate: " << static_cast(settings.hmc_settings.n_accept_draws) / settings.hmc_settings.n_keep_draws << std::endl; 119 | 120 | // 121 | 122 | return 0; 123 | } 124 | -------------------------------------------------------------------------------- /include/misc/transform_vals.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * transform values 23 | */ 24 | 25 | template 26 | inline 27 | vT 28 | transform( 29 | const vT& vals_inp, 30 | const ColVecInt_t& bounds_type, 31 | const ColVec_t& lower_bounds, 32 | const ColVec_t& upper_bounds 33 | ) 34 | { 35 | const size_t n_vals = BMO_MATOPS_SIZE(bounds_type); 36 | 37 | vT vals_trans_out(n_vals); 38 | 39 | for (size_t i = 0; i < n_vals; ++i) { 40 | switch (bounds_type(i)) { 41 | case 1: // no bounds 42 | vals_trans_out(i) = vals_inp(i); 43 | break; 44 | case 2: // lower bound only 45 | vals_trans_out(i) = std::log(vals_inp(i) - lower_bounds(i) + eps_dbl); 46 | break; 47 | case 3: // upper bound only 48 | vals_trans_out(i) = - std::log(upper_bounds(i) - vals_inp(i) + eps_dbl); 49 | break; 50 | case 4: // upper and lower bounds 51 | vals_trans_out(i) = std::log(vals_inp(i) - lower_bounds(i) + eps_dbl) - std::log(upper_bounds(i) - vals_inp(i) + eps_dbl); 52 | break; 53 | } 54 | } 55 | 56 | // 57 | 58 | return vals_trans_out; 59 | } 60 | 61 | template 62 | inline 63 | vT 64 | inv_transform( 65 | const vT& vals_trans_inp, 66 | const ColVecInt_t& bounds_type, 67 | const ColVec_t& lower_bounds, 68 | const ColVec_t& upper_bounds 69 | ) 70 | { 71 | const size_t n_vals = BMO_MATOPS_SIZE(bounds_type); 72 | 73 | vT vals_out(n_vals); 74 | 75 | for (size_t i = 0; i < n_vals; ++i) { 76 | switch (bounds_type(i)) { 77 | case 1: // no bounds 78 | vals_out(i) = vals_trans_inp(i); 79 | break; 80 | case 2: // lower bound only 81 | if (!std::isfinite(vals_trans_inp(i))) { 82 | vals_out(i) = lower_bounds(i) + eps_dbl; 83 | } else { 84 | vals_out(i) = lower_bounds(i) + eps_dbl + std::exp(vals_trans_inp(i)); 85 | } 86 | break; 87 | case 3: // upper bound only 88 | if (!std::isfinite(vals_trans_inp(i))) { 89 | vals_out(i) = upper_bounds(i) - eps_dbl; 90 | } else { 91 | vals_out(i) = upper_bounds(i) - eps_dbl - std::exp(-vals_trans_inp(i)); 92 | } 93 | break; 94 | case 4: // upper and lower bounds 95 | if (!std::isfinite(vals_trans_inp(i))) { 96 | if (std::isnan(vals_trans_inp(i))) { 97 | vals_out(i) = (upper_bounds(i) - lower_bounds(i)) / 2; 98 | } 99 | else if (vals_trans_inp(i) < 0.0) { 100 | vals_out(i) = lower_bounds(i) + eps_dbl; 101 | } else { 102 | vals_out(i) = upper_bounds(i) - eps_dbl; 103 | } 104 | } else { 105 | vals_out(i) = ( lower_bounds(i) - eps_dbl + (upper_bounds(i) + eps_dbl)*std::exp(vals_trans_inp(i)) ) \ 106 | / ( 1.0 + std::exp(vals_trans_inp(i)) ); 107 | 108 | if (!std::isfinite(vals_out(i))) { 109 | vals_out(i) = upper_bounds(i) - eps_dbl; 110 | } 111 | } 112 | break; 113 | } 114 | } 115 | 116 | // 117 | 118 | return vals_out; 119 | } 120 | -------------------------------------------------------------------------------- /examples/armadillo/rmhmc_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using RM-HMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++11 -O3 -mcpu=native -ffp-contract=fast -I$ARMA_INCLUDE_PATH -I./../../include/ rmhmc_normal.cpp -o rmhmc_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_ARMA_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct norm_data_t { 31 | arma::vec x; 32 | }; 33 | 34 | double ll_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 35 | { 36 | const double pi = arma::datum::pi; 37 | 38 | const double mu = vals_inp(0); 39 | const double sigma = vals_inp(1); 40 | 41 | norm_data_t* dta = reinterpret_cast(ll_data); 42 | const arma::vec x = dta->x; 43 | const int n_vals = x.n_rows; 44 | 45 | // 46 | 47 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - arma::accu( arma::pow(x - mu,2) / (2*sigma*sigma) ); 48 | 49 | // 50 | 51 | if (grad_out) { 52 | grad_out->set_size(2,1); 53 | 54 | // 55 | 56 | const double m_1 = arma::accu(x - mu); 57 | const double m_2 = arma::accu( arma::pow(x - mu,2) ); 58 | 59 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 60 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 61 | } 62 | 63 | // 64 | 65 | return ret; 66 | } 67 | 68 | arma::mat tensor_fn(const arma::vec& vals_inp, mcmc::Cube_t* tensor_deriv_out, void* tensor_data) 69 | { 70 | // const double mu = vals_inp(0); 71 | const double sigma = vals_inp(1); 72 | 73 | norm_data_t* dta = reinterpret_cast(tensor_data); 74 | 75 | const int n_vals = dta->x.n_rows; 76 | 77 | // 78 | 79 | const double sigma_sq = sigma*sigma; 80 | 81 | arma::mat tensor_out = arma::zeros(2,2); 82 | 83 | tensor_out(0,0) = ((double) n_vals) / sigma_sq; 84 | tensor_out(1,1) = 2.0 * ((double) n_vals) / sigma_sq; 85 | 86 | // 87 | 88 | if (tensor_deriv_out) { 89 | tensor_deriv_out->setZero(2,2,2); 90 | 91 | // 92 | 93 | // tensor_deriv_out->mat(0).setZero(); 94 | 95 | tensor_deriv_out->mat(1) = - 2.0 * tensor_out / sigma; 96 | } 97 | 98 | // 99 | 100 | return tensor_out; 101 | } 102 | 103 | double log_target_dens(const arma::vec& vals_inp, arma::vec* grad_out, void* ll_data) 104 | { 105 | return ll_dens(vals_inp,grad_out,ll_data); 106 | } 107 | 108 | int main() 109 | { 110 | const int n_data = 1000; 111 | 112 | const double mu = 2.0; 113 | const double sigma = 2.0; 114 | 115 | norm_data_t dta; 116 | 117 | arma::vec x_dta = mu + sigma * arma::randn(n_data,1); 118 | dta.x = x_dta; 119 | 120 | arma::vec initial_val(2); 121 | initial_val(0) = mu + 1; // mu 122 | initial_val(1) = sigma + 1; // sigma 123 | 124 | // 125 | 126 | mcmc::algo_settings_t settings; 127 | 128 | settings.rmhmc_settings.step_size = 0.2; 129 | settings.rmhmc_settings.n_burnin_draws = 2000; 130 | settings.rmhmc_settings.n_keep_draws = 2000; 131 | 132 | // 133 | 134 | arma::mat draws_out; 135 | mcmc::rmhmc(initial_val, log_target_dens, tensor_fn, draws_out, &dta, &dta, settings); 136 | 137 | // 138 | 139 | std::cout << "rmhmc mean:\n" << arma::mean(draws_out) << std::endl; 140 | std::cout << "acceptance rate: " << static_cast(settings.rmhmc_settings.n_accept_draws) / settings.rmhmc_settings.n_keep_draws << std::endl; 141 | 142 | // 143 | 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | .. _installation: 8 | 9 | Installation 10 | ============ 11 | 12 | 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). Note that use of this library with Windows-based systems, with or without MSVC, **is not supported**. 13 | 14 | 15 | Requirements 16 | ------------ 17 | 18 | MCMCLib requires either the Armadillo or Eigen C++ linear algebra libraries. (Note that Eigen version 3.4.0 requires a C++14-compatible compiler.) 19 | 20 | The following options should be declared **before** including the MCMCLib header files. 21 | 22 | - OpenMP functionality is enabled by default if the ``_OPENMP`` macro is detected (e.g., by invoking ``-fopenmp`` with GCC or Clang). 23 | 24 | - To explicitly enable OpenMP features, use: 25 | 26 | .. code:: cpp 27 | 28 | #define MCMC_USE_OPENMP 29 | 30 | - To explicitly disable OpenMP functionality, use: 31 | 32 | .. code:: cpp 33 | 34 | #define MCMC_DONT_USE_OPENMP 35 | 36 | - To use MCMCLib with Armadillo or Eigen: 37 | 38 | .. code:: cpp 39 | 40 | #define MCMC_ENABLE_ARMA_WRAPPERS 41 | #define MCMC_ENABLE_EIGEN_WRAPPERS 42 | 43 | Example: 44 | 45 | .. code:: cpp 46 | 47 | #define MCMC_ENABLE_EIGEN_WRAPPERS 48 | #include "mcmc.hpp" 49 | 50 | - To use MCMCLib with RcppArmadillo: 51 | 52 | .. code:: cpp 53 | 54 | #define MCMC_USE_RCPP_ARMADILLO 55 | 56 | Example: 57 | 58 | .. code:: cpp 59 | 60 | #define MCMC_USE_RCPP_ARMADILLO 61 | #include "mcmc.hpp" 62 | 63 | 64 | ---- 65 | 66 | Installation Method 1: Shared Library 67 | ------------------------------------- 68 | 69 | The library can be installed on Unix-alike systems via the standard ``./configure && make`` method. 70 | 71 | The primary configuration options can be displayed by calling ``./configure -h``, which results in: 72 | 73 | .. code:: bash 74 | 75 | $ ./configure -h 76 | 77 | MCMCLib Configuration 78 | 79 | Main options: 80 | -c Code coverage build 81 | (default: disabled) 82 | -d Developmental build 83 | (default: disabled) 84 | -f Floating-point number type 85 | (default: double) 86 | -g Debugging build (optimization flags set to -O0 -g) 87 | (default: disabled) 88 | -h Print help 89 | -i Install path (default: current directory) 90 | Example: /usr/local 91 | -l Choice of linear algebra library 92 | Examples: -l arma or -l eigen 93 | -m Specify the BLAS and Lapack libraries to link against 94 | Examples: -m "-lopenblas" or -m "-framework Accelerate" 95 | -o Compiler optimization options 96 | (default: -O3 -march=native -ffp-contract=fast -flto -DARMA_NO_DEBUG) 97 | -p Enable OpenMP parallelization features 98 | (default: disabled) 99 | 100 | Special options: 101 | --header-only-version Generate a header-only version of MCMCLib 102 | 103 | If choosing a shared library build, set (one) of the following environment variables *before* running `configure`: 104 | 105 | .. code:: bash 106 | 107 | export ARMA_INCLUDE_PATH=/path/to/armadillo 108 | export EIGEN_INCLUDE_PATH=/path/to/eigen 109 | 110 | Then, to set the install path to ``/usr/local``, use Armadillo as the linear algebra library, and enable OpenMP features, we would run: 111 | 112 | .. code:: bash 113 | 114 | ./configure -i "/usr/local" -l arma -p 115 | 116 | Following this with the standard ``make && make install`` would build the library and install into ``/usr/local``. 117 | 118 | ---- 119 | 120 | Installation Method 2: Header-only Library 121 | ------------------------------------------ 122 | 123 | MCMCLib is also available as a header-only library (i.e., without the need to compile a shared library). Simply run ``configure`` with the ``--header-only-version`` option: 124 | 125 | .. code:: bash 126 | 127 | ./configure --header-only-version 128 | 129 | This will create a new directory, ``header_only_version``, containing a copy of MCMCLib, modified to work on an inline basis. 130 | With this header-only version, simply include the header files (``#include "mcmc.hpp``) and set the include path to the ``head_only_version`` directory (e.g.,``-I/path/to/mcmclib/header_only_version``). 131 | -------------------------------------------------------------------------------- /docs/source/autodiff.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | Automatic Differentiation 8 | ========================= 9 | 10 | Gradient-based MCMC methods in MCMCLib (such as HMC and MALA) require a user-defined function that returns a gradient vector at each function evaluation. While this is best achieved by knowing the gradient in closed form, MCMCLib also provides **experimental support** for automatic differentiation with Eigen-based builds via the `autodiff library `_. 11 | 12 | Requirements: an Eigen-based build of MCMCLib, a copy of the ``autodiff`` header files, and a C++17 compatible compiler. 13 | 14 | ---- 15 | 16 | Example 17 | ------- 18 | 19 | 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. 20 | 21 | .. code:: cpp 22 | 23 | /* 24 | * Sampling from a Gaussian distribution using HMC and Autodiff 25 | */ 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | #include 31 | #include 32 | 33 | inline 34 | Eigen::VectorXd 35 | eigen_randn_colvec(size_t nr) 36 | { 37 | static std::mt19937 gen{ std::random_device{}() }; 38 | static std::normal_distribution<> dist; 39 | 40 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 41 | } 42 | 43 | struct norm_data_t { 44 | Eigen::VectorXd x; 45 | }; 46 | 47 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 48 | { 49 | const double pi = 3.14159265358979; 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | 54 | // 55 | 56 | autodiff::real u; 57 | autodiff::ArrayXreal xd = vals_inp.eval(); 58 | 59 | std::function normal_dens_log_form \ 60 | = [x, pi](const autodiff::ArrayXreal& vals_inp) -> autodiff::real 61 | { 62 | autodiff::real mu = vals_inp(0); 63 | autodiff::real sigma = vals_inp(1); 64 | 65 | return - x.size() * (0.5 * std::log(2*pi) + autodiff::detail::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 66 | }; 67 | 68 | // 69 | 70 | if (grad_out) { 71 | Eigen::VectorXd grad_tmp = autodiff::gradient(normal_dens_log_form, autodiff::wrt(xd), autodiff::at(xd), u); 72 | 73 | *grad_out = grad_tmp; 74 | } else { 75 | u = normal_dens_log_form(xd); 76 | } 77 | 78 | // 79 | 80 | return u.val(); 81 | } 82 | 83 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 84 | { 85 | return ll_dens(vals_inp,grad_out,ll_data); 86 | } 87 | 88 | int main() 89 | { 90 | const int n_data = 1000; 91 | 92 | const double mu = 2.0; 93 | const double sigma = 2.0; 94 | 95 | norm_data_t dta; 96 | 97 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 98 | dta.x = x_dta; 99 | 100 | Eigen::VectorXd initial_val(2); 101 | initial_val(0) = mu + 1; // mu 102 | initial_val(1) = sigma + 1; // sigma 103 | 104 | mcmc::algo_settings_t settings; 105 | 106 | settings.hmc_settings.step_size = 0.08; 107 | settings.hmc_settings.n_burnin_draws = 2000; 108 | settings.hmc_settings.n_keep_draws = 2000; 109 | 110 | // 111 | 112 | Eigen::MatrixXd draws_out; 113 | mcmc::hmc(initial_val, log_target_dens, draws_out, &dta, settings); 114 | 115 | // 116 | 117 | std::cout << "hmc mean:\n" << draws_out.colwise().mean() << std::endl; 118 | std::cout << "acceptance rate: " << static_cast(settings.hmc_settings.n_accept_draws) / settings.hmc_settings.n_keep_draws << std::endl; 119 | 120 | // 121 | 122 | return 0; 123 | } 124 | 125 | 126 | This example can be compiled using: 127 | 128 | .. code:: bash 129 | 130 | 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 131 | 132 | 133 | ---- 134 | -------------------------------------------------------------------------------- /include/mcmc/rmhmc.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Riemannian Manifold Hamiltonian Monte Carlo (RM-HMC) 23 | */ 24 | 25 | #ifndef _mcmc_rmhmc_HPP 26 | #define _mcmc_rmhmc_HPP 27 | 28 | /** 29 | * @brief The Riemannian Manifold Hamiltonian Monte Carlo (RM-HMC) MCMC Algorithm 30 | * 31 | * @param initial_vals a column vector of initial values. 32 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 33 | * - \c vals_inp a vector of inputs; and 34 | * - \c grad_out a vector to store the gradient; and 35 | * - \c target_data additional data passed to the user-provided function. 36 | * @param tensor_fn the manifold tensor function, taking three arguments: 37 | * - \c vals_inp a vector of inputs; and 38 | * - \c tensor_deriv_out a 3-dimensional array to store the tensor derivatives; and 39 | * - \c tensor_data additional data passed to the user-provided function. 40 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 41 | * @param target_data additional data passed to the user-provided log kernel function. 42 | * @param tensor_data additional data passed to the user-provided tensor function. 43 | * 44 | * @return a boolean value indicating successful completion of the algorithm. 45 | */ 46 | 47 | bool 48 | rmhmc( 49 | const ColVec_t& initial_vals, 50 | std::function target_log_kernel, 51 | std::function tensor_fn, 52 | Mat_t& draws_out, 53 | void* target_data, 54 | void* tensor_data 55 | ); 56 | 57 | /** 58 | * @brief The Riemannian Manifold Hamiltonian Monte Carlo (RM-HMC) MCMC Algorithm 59 | * 60 | * @param initial_vals a column vector of initial values. 61 | * @param target_log_kernel the log posterior kernel function of the target distribution, taking three arguments: 62 | * - \c vals_inp a vector of inputs; and 63 | * - \c grad_out a vector to store the gradient; and 64 | * - \c target_data additional data passed to the user-provided function. 65 | * @param tensor_fn the manifold tensor function, taking three arguments: 66 | * - \c vals_inp a vector of inputs; and 67 | * - \c tensor_deriv_out a 3-dimensional array to store the tensor derivatives; and 68 | * - \c tensor_data additional data passed to the user-provided function. 69 | * @param draws_out a matrix of posterior draws, where each row represents one draw. 70 | * @param target_data additional data passed to the user-provided log kernel function. 71 | * @param tensor_data additional data passed to the user-provided tensor function. 72 | * @param settings parameters controlling the MCMC routine. 73 | * 74 | * @return a boolean value indicating successful completion of the algorithm. 75 | */ 76 | 77 | bool 78 | rmhmc( 79 | const ColVec_t& initial_vals, 80 | std::function target_log_kernel, 81 | std::function tensor_fn, 82 | Mat_t& draws_out, 83 | void* target_data, 84 | void* tensor_data, 85 | algo_settings_t& settings 86 | ); 87 | 88 | 89 | namespace internal 90 | { 91 | 92 | bool 93 | rmhmc_impl( 94 | const ColVec_t& initial_vals, 95 | std::function target_log_kernel, 96 | std::function tensor_fn, 97 | Mat_t& draws_out, 98 | void* target_data, 99 | void* tensor_data, 100 | algo_settings_t* settings_inp 101 | ); 102 | 103 | } 104 | 105 | // 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /examples/eigen/rmhmc_normal.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian distribution using HMC 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ rmhmc_normal.cpp -o rmhmc_normal.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | inline 31 | Eigen::VectorXd 32 | eigen_randn_colvec(size_t nr) 33 | { 34 | static std::mt19937 gen{ std::random_device{}() }; 35 | static std::normal_distribution<> dist; 36 | 37 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 38 | } 39 | 40 | struct norm_data_t { 41 | Eigen::VectorXd x; 42 | }; 43 | 44 | double ll_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 45 | { 46 | const double pi = 3.14159265358979; 47 | 48 | const double mu = vals_inp(0); 49 | const double sigma = vals_inp(1); 50 | 51 | norm_data_t* dta = reinterpret_cast(ll_data); 52 | const Eigen::VectorXd x = dta->x; 53 | const int n_vals = x.size(); 54 | 55 | // 56 | 57 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 58 | 59 | // 60 | 61 | if (grad_out) { 62 | grad_out->resize(2,1); 63 | 64 | // 65 | 66 | const double m_1 = (x.array() - mu).sum(); 67 | const double m_2 = (x.array() - mu).pow(2).sum(); 68 | 69 | (*grad_out)(0,0) = m_1 / (sigma*sigma); 70 | (*grad_out)(1,0) = (m_2 / (sigma*sigma*sigma)) - ((double) n_vals) / sigma; 71 | } 72 | 73 | // 74 | 75 | return ret; 76 | } 77 | 78 | Eigen::MatrixXd tensor_fn(const Eigen::VectorXd& vals_inp, mcmc::Cube_t* tensor_deriv_out, void* tensor_data) 79 | { 80 | // const double mu = vals_inp(0); 81 | const double sigma = vals_inp(1); 82 | 83 | norm_data_t* dta = reinterpret_cast(tensor_data); 84 | 85 | const int n_vals = dta->x.size(); 86 | 87 | // 88 | 89 | const double sigma_sq = sigma*sigma; 90 | 91 | Eigen::MatrixXd tensor_out = Eigen::MatrixXd::Zero(2,2); 92 | 93 | tensor_out(0,0) = ((double) n_vals) / sigma_sq; 94 | tensor_out(1,1) = 2.0 * ((double) n_vals) / sigma_sq; 95 | 96 | // 97 | 98 | if (tensor_deriv_out) { 99 | tensor_deriv_out->setZero(2,2,2); 100 | 101 | // 102 | 103 | // tensor_deriv_out->mat(0).setZero(); 104 | 105 | tensor_deriv_out->mat(1) = - 2.0 * tensor_out / sigma; 106 | } 107 | 108 | // 109 | 110 | return tensor_out; 111 | } 112 | 113 | double log_target_dens(const Eigen::VectorXd& vals_inp, Eigen::VectorXd* grad_out, void* ll_data) 114 | { 115 | return ll_dens(vals_inp,grad_out,ll_data); 116 | } 117 | 118 | int main() 119 | { 120 | const int n_data = 1000; 121 | 122 | const double mu = 2.0; 123 | const double sigma = 2.0; 124 | 125 | norm_data_t dta; 126 | 127 | Eigen::VectorXd x_dta = mu + sigma * eigen_randn_colvec(n_data).array(); 128 | dta.x = x_dta; 129 | 130 | Eigen::VectorXd initial_val(2); 131 | initial_val(0) = mu + 1; // mu 132 | initial_val(1) = sigma + 1; // sigma 133 | 134 | mcmc::algo_settings_t settings; 135 | 136 | settings.rmhmc_settings.step_size = 0.2; 137 | settings.rmhmc_settings.n_burnin_draws = 2000; 138 | settings.rmhmc_settings.n_keep_draws = 2000; 139 | 140 | // 141 | 142 | Eigen::MatrixXd draws_out; 143 | mcmc::rmhmc(initial_val, log_target_dens, tensor_fn, draws_out, &dta, &dta, settings); 144 | 145 | // 146 | 147 | std::cout << "rmhmc mean:\n" << draws_out.colwise().mean() << std::endl; 148 | std::cout << "acceptance rate: " << static_cast(settings.rmhmc_settings.n_accept_draws) / settings.rmhmc_settings.n_keep_draws << std::endl; 149 | 150 | // 151 | 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /docs/source/settings.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | MCMC Settings 8 | ============= 9 | 10 | .. contents:: :local: 11 | 12 | ---- 13 | 14 | Main 15 | ---- 16 | 17 | An object of type ``algo_settings_t`` can be used to control the behavior of the MCMC routines. Each algorithm page details the relevant parameters for that methods, but we list the full settings here for completeness. 18 | 19 | .. code:: cpp 20 | 21 | struct algo_settings_t 22 | { 23 | // RNG seeding 24 | 25 | size_t rng_seed_value = std::random_device{}(); 26 | 27 | // bounds 28 | 29 | bool vals_bound = false; 30 | 31 | ColVec_t lower_bounds; 32 | ColVec_t upper_bounds; 33 | 34 | // AEES 35 | aees_settings_t aees_settings; 36 | 37 | // DE 38 | de_settings_t de_settings; 39 | 40 | // HMC 41 | hmc_settings_t hmc_settings; 42 | 43 | // RM-HMC 44 | rmhmc_settings_t rmhmc_settings; 45 | 46 | // MALA 47 | mala_settings_t mala_settings; 48 | 49 | // RWMH 50 | rwmh_settings_t rwmh_settings; 51 | }; 52 | 53 | 54 | Description: 55 | 56 | - ``rng_seed_value`` seed value used for random number generators. 57 | 58 | - ``vals_bound`` whether the search space of the algorithm is bounded. 59 | 60 | - ``lower_bounds`` defines the lower bounds of the search space. 61 | 62 | - ``upper_bounds`` defines the upper bounds of the search space. 63 | 64 | Algorithm-specific data structures are listed in the next section. 65 | 66 | ---- 67 | 68 | By Algorithm 69 | ------------ 70 | 71 | AEES 72 | ~~~~ 73 | 74 | .. code:: cpp 75 | 76 | struct aees_settings_t 77 | { 78 | size_t n_initial_draws = 1E03; 79 | size_t n_burnin_draws = 1E03; 80 | size_t n_keep_draws = 1E03; 81 | 82 | int omp_n_threads = -1; // numbers of threads to use 83 | 84 | fp_t par_scale = 1.0; 85 | Mat_t cov_mat; 86 | 87 | size_t n_rings = 5; // number of energy rings 88 | fp_t ee_prob_par = 0.10; // equi-energy probability parameter 89 | ColVec_t temper_vec; // temperature vector 90 | }; 91 | 92 | DE 93 | ~~ 94 | 95 | .. code:: cpp 96 | 97 | struct de_settings_t 98 | { 99 | bool jumps = false; 100 | 101 | size_t n_pop = 100; 102 | size_t n_burnin_draws = 1E03; 103 | size_t n_keep_draws = 1E03; 104 | 105 | int omp_n_threads = -1; // numbers of threads to use 106 | 107 | fp_t par_b = 1E-04; 108 | fp_t par_gamma = 1.0; 109 | fp_t par_gamma_jump = 2.0; 110 | 111 | ColVec_t initial_lb; // this will default to -0.5 112 | ColVec_t initial_ub; // this will default to 0.5 113 | 114 | size_t n_accept_draws; // will be returned by the algorithm 115 | }; 116 | 117 | 118 | HMC 119 | ~~~ 120 | 121 | .. code:: cpp 122 | 123 | struct hmc_settings_t 124 | { 125 | size_t n_burnin_draws = 1E03; 126 | size_t n_keep_draws = 1E03; 127 | 128 | int omp_n_threads = -1; // numbers of threads to use 129 | 130 | size_t n_leap_steps = 1; // number of leap frog steps 131 | fp_t step_size = 1.0; 132 | Mat_t precond_mat; 133 | 134 | size_t n_accept_draws; // will be returned by the function 135 | }; 136 | 137 | 138 | RM-HMC 139 | ~~~~~~ 140 | 141 | .. code:: cpp 142 | 143 | struct rmhmc_settings_t 144 | { 145 | size_t n_burnin_draws = 1E03; 146 | size_t n_keep_draws = 1E03; 147 | 148 | int omp_n_threads = -1; // numbers of threads to use 149 | 150 | size_t n_leap_steps = 1; // number of leap frog steps 151 | fp_t step_size = 1.0; 152 | Mat_t precond_mat; 153 | 154 | size_t n_fp_steps = 5; // number of fixed point iteration steps 155 | 156 | size_t n_accept_draws; // will be returned by the function 157 | }; 158 | 159 | 160 | MALA 161 | ~~~~ 162 | 163 | .. code:: cpp 164 | 165 | struct mala_settings_t 166 | { 167 | size_t n_burnin_draws = 1E03; 168 | size_t n_keep_draws = 1E03; 169 | 170 | int omp_n_threads = -1; // numbers of threads to use 171 | 172 | fp_t step_size = 1.0; 173 | Mat_t precond_mat; 174 | 175 | size_t n_accept_draws; // will be returned by the function 176 | }; 177 | 178 | RWMH 179 | ~~~~ 180 | 181 | .. code:: cpp 182 | 183 | struct rwmh_settings_t 184 | { 185 | size_t n_burnin_draws = 1E03; 186 | size_t n_keep_draws = 1E03; 187 | 188 | int omp_n_threads = -1; // numbers of threads to use 189 | 190 | fp_t par_scale = 1.0; 191 | Mat_t cov_mat; 192 | 193 | size_t n_accept_draws; // will be returned by the function 194 | }; 195 | 196 | ---- 197 | -------------------------------------------------------------------------------- /include/misc/mcmc_structs.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #ifndef mcmc_structs_HPP 22 | #define mcmc_structs_HPP 23 | 24 | // AEES 25 | 26 | struct aees_settings_t 27 | { 28 | size_t n_initial_draws = 1E03; 29 | size_t n_burnin_draws = 1E03; 30 | size_t n_keep_draws = 1E03; 31 | 32 | int omp_n_threads = -1; // numbers of threads to use 33 | 34 | fp_t par_scale = 1.0; 35 | Mat_t cov_mat; 36 | 37 | size_t n_rings = 5; // number of energy rings 38 | fp_t ee_prob_par = 0.10; // equi-energy probability parameter 39 | ColVec_t temper_vec; // temperature vector 40 | }; 41 | 42 | // DE 43 | 44 | struct de_settings_t 45 | { 46 | bool jumps = false; 47 | 48 | size_t n_pop = 100; 49 | size_t n_burnin_draws = 1E03; 50 | size_t n_keep_draws = 1E03; 51 | 52 | int omp_n_threads = -1; // numbers of threads to use 53 | 54 | fp_t par_b = 1E-04; 55 | fp_t par_gamma = 1.0; 56 | fp_t par_gamma_jump = 2.0; 57 | 58 | ColVec_t initial_lb; // this will default to -0.5 59 | ColVec_t initial_ub; // this will default to 0.5 60 | 61 | size_t n_accept_draws; // will be returned by the algorithm 62 | }; 63 | 64 | // HMC 65 | 66 | struct hmc_settings_t 67 | { 68 | size_t n_burnin_draws = 1E03; 69 | size_t n_keep_draws = 1E03; 70 | 71 | int omp_n_threads = -1; // numbers of threads to use 72 | 73 | size_t n_leap_steps = 1; // number of leap frog steps 74 | fp_t step_size = 1.0; 75 | Mat_t precond_mat; 76 | 77 | size_t n_accept_draws; // will be returned by the function 78 | }; 79 | 80 | // NUTS 81 | 82 | struct nuts_settings_t 83 | { 84 | size_t n_burnin_draws = 1E03; 85 | size_t n_keep_draws = 1E03; 86 | 87 | int omp_n_threads = -1; // numbers of threads to use 88 | 89 | size_t n_adapt_draws = 1E03; 90 | fp_t target_accept_rate = 0.55; 91 | 92 | size_t max_tree_depth = size_t(10); 93 | 94 | fp_t step_size = 1.0; // \bar{\epsilon}_0 95 | fp_t gamma_val = 0.05; 96 | fp_t t0_val = 10; 97 | fp_t kappa_val = 0.75; 98 | Mat_t precond_mat; 99 | 100 | size_t n_accept_draws; // will be returned by the function 101 | }; 102 | 103 | // RM-HMC 104 | 105 | struct rmhmc_settings_t 106 | { 107 | size_t n_burnin_draws = 1E03; 108 | size_t n_keep_draws = 1E03; 109 | 110 | int omp_n_threads = -1; // numbers of threads to use 111 | 112 | size_t n_leap_steps = 1; // number of leap frog steps 113 | fp_t step_size = 1.0; 114 | Mat_t precond_mat; 115 | 116 | size_t n_fp_steps = 5; // number of fixed point iteration steps 117 | 118 | size_t n_accept_draws; // will be returned by the function 119 | }; 120 | 121 | // MALA 122 | 123 | struct mala_settings_t 124 | { 125 | size_t n_burnin_draws = 1E03; 126 | size_t n_keep_draws = 1E03; 127 | 128 | int omp_n_threads = -1; // numbers of threads to use 129 | 130 | fp_t step_size = 1.0; 131 | Mat_t precond_mat; 132 | 133 | size_t n_accept_draws; // will be returned by the function 134 | }; 135 | 136 | // RWMH 137 | 138 | struct rwmh_settings_t 139 | { 140 | size_t n_burnin_draws = 1E03; 141 | size_t n_keep_draws = 1E03; 142 | 143 | int omp_n_threads = -1; // numbers of threads to use 144 | 145 | fp_t par_scale = 1.0; 146 | Mat_t cov_mat; 147 | 148 | size_t n_accept_draws; // will be returned by the function 149 | }; 150 | 151 | struct algo_settings_t 152 | { 153 | // RNG seeding 154 | 155 | size_t rng_seed_value = std::random_device{}(); 156 | 157 | // bounds 158 | 159 | bool vals_bound = false; 160 | 161 | ColVec_t lower_bounds; 162 | ColVec_t upper_bounds; 163 | 164 | // AEES 165 | aees_settings_t aees_settings; 166 | 167 | // DE 168 | de_settings_t de_settings; 169 | 170 | // HMC 171 | hmc_settings_t hmc_settings; 172 | 173 | // NUTS 174 | nuts_settings_t nuts_settings; 175 | 176 | // RM-HMC 177 | rmhmc_settings_t rmhmc_settings; 178 | 179 | // MALA 180 | mala_settings_t mala_settings; 181 | 182 | // RWMH 183 | rwmh_settings_t rwmh_settings; 184 | }; 185 | 186 | #endif -------------------------------------------------------------------------------- /examples/eigen/aees_mixture.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Sampling from a Gaussian Mixture Distribution using the Adaptive Equi-Energy Sampler (AEES) 23 | */ 24 | 25 | // $CXX -Wall -std=c++14 -O3 -mcpu=native -ffp-contract=fast -I$EIGEN_INCLUDE_PATH -I./../../include/ aees_mixture.cpp -o aees_mixture.out -L./../.. -lmcmc 26 | 27 | #define MCMC_ENABLE_EIGEN_WRAPPERS 28 | #include "mcmc.hpp" 29 | 30 | struct mixture_data_t { 31 | Eigen::MatrixXd mu; 32 | Eigen::VectorXd sig_sq; 33 | Eigen::VectorXd weights; 34 | }; 35 | 36 | double 37 | gaussian_mixture(const Eigen::VectorXd& X_vec_inp, const Eigen::VectorXd& weights, const Eigen::MatrixXd& mu, const Eigen::VectorXd& sig_sq) 38 | { 39 | const double pi = 3.14159265358979; 40 | 41 | const int n_vals = X_vec_inp.size(); 42 | const int n_mix = weights.size(); 43 | 44 | // 45 | 46 | double dens_val = 0; 47 | 48 | for (int i = 0; i < n_mix; ++i) { 49 | const double dist_val = (X_vec_inp - mu.col(i)).array().pow(2).sum(); 50 | 51 | dens_val += weights(i) * std::exp(-0.5 * dist_val / sig_sq(i)) / std::pow(2.0 * pi * sig_sq(i), static_cast(n_vals) / 2.0); 52 | } 53 | 54 | // 55 | 56 | return std::log(dens_val); 57 | } 58 | 59 | double 60 | target_log_kernel(const Eigen::VectorXd& vals_inp, void* target_data) 61 | { 62 | mixture_data_t* dta = reinterpret_cast(target_data); 63 | 64 | return gaussian_mixture(vals_inp, dta->weights, dta->mu, dta->sig_sq); 65 | } 66 | 67 | int main() 68 | { 69 | const int n_vals = 2; 70 | const int n_mix = 2; 71 | 72 | // 73 | 74 | Eigen::MatrixXd mu = Eigen::MatrixXd::Ones(n_vals, n_mix).array() + 1.0; 75 | mu.col(0) *= -1.0; // (-2, 2) 76 | 77 | Eigen::VectorXd weights = Eigen::VectorXd::Constant(n_mix, 1.0 / n_mix); 78 | 79 | Eigen::VectorXd sig_sq = 0.1 * Eigen::VectorXd::Ones(n_mix); 80 | 81 | mixture_data_t dta; 82 | dta.mu = mu; 83 | dta.sig_sq = sig_sq; 84 | dta.weights = weights; 85 | 86 | // 87 | 88 | Eigen::VectorXd T_vec(2); 89 | T_vec(0) = 60.0; 90 | T_vec(1) = 9.0; 91 | 92 | // settings 93 | 94 | mcmc::algo_settings_t settings; 95 | 96 | settings.aees_settings.n_initial_draws = 1000; 97 | settings.aees_settings.n_burnin_draws = 1000; 98 | settings.aees_settings.n_keep_draws = 20000; 99 | 100 | settings.aees_settings.n_rings = 11; 101 | settings.aees_settings.ee_prob_par = 0.05; 102 | settings.aees_settings.temper_vec = T_vec; 103 | 104 | settings.aees_settings.par_scale = 1.0; 105 | settings.aees_settings.cov_mat = 0.35 * Eigen::MatrixXd::Identity(n_vals, n_vals); 106 | 107 | // 108 | 109 | Eigen::MatrixXd draws_out; 110 | 111 | mcmc::aees(mu.col(0), target_log_kernel, draws_out, &dta, settings); 112 | 113 | // 114 | 115 | Eigen::Matrix pos_inds = (draws_out.array() > 0.1); 116 | 117 | Eigen::VectorXd mean_vec = Eigen::VectorXd::Zero(2); 118 | 119 | for (int i = 0; i < n_vals; ++i) { 120 | for (size_t draw_ind = 0; draw_ind < settings.aees_settings.n_keep_draws; ++draw_ind) { 121 | if (pos_inds(draw_ind, i)) { 122 | mean_vec(i) += draws_out(draw_ind, i); 123 | } 124 | } 125 | mean_vec(i) /= pos_inds.col(i).count(); 126 | } 127 | 128 | std::cout << "posterior mean for > 0.1:\n" << mean_vec << std::endl; 129 | 130 | // 131 | 132 | Eigen::Matrix neg_inds = (draws_out.array() < - 0.1); 133 | 134 | mean_vec = Eigen::VectorXd::Zero(2); 135 | 136 | for (int i = 0; i < n_vals; ++i) { 137 | for (size_t draw_ind = 0; draw_ind < settings.aees_settings.n_keep_draws; ++draw_ind) { 138 | if (neg_inds(draw_ind, i)) { 139 | mean_vec(i) += draws_out(draw_ind, i); 140 | } 141 | } 142 | mean_vec(i) /= neg_inds.col(i).count(); 143 | } 144 | 145 | std::cout << "posterior mean for < - 0.1:\n" << mean_vec << std::endl; 146 | 147 | // 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /docs/source/examples_and_tests.rst: -------------------------------------------------------------------------------- 1 | .. Copyright (c) 2011-2023 Keith O'Hara 2 | 3 | Distributed under the terms of the Apache License, Version 2.0. 4 | 5 | The full license is in the file LICENSE, distributed with this software. 6 | 7 | Examples and Tests 8 | ================== 9 | 10 | .. contents:: :local: 11 | 12 | ---- 13 | 14 | API 15 | --- 16 | 17 | The MCMCLib API follows a relatively simple convention, with most algorithms called using the following syntax: 18 | 19 | .. code:: 20 | 21 | algorithm_id(, , , ); 22 | 23 | The inputs, in order, are: 24 | 25 | * A vector of initial values used to define the starting point of the algorithm. 26 | * A user-specified function that returns the log posterior kernel value of the target distribution. 27 | * An array to store the posterior draws. 28 | * The final input is optional: it is any object that contains additional data necessary to evaluate the log posterior kernel function. 29 | 30 | For example, the RWMH algorithm is called using 31 | 32 | .. code:: cpp 33 | 34 | rwmh(const ColVec_t& initial_vals, std::function target_log_kernel, Mat_t& draws_out, void* target_data); 35 | 36 | 37 | ---- 38 | 39 | Example 40 | ------- 41 | 42 | The code below uses the RWMH algorithm generate draws of the mean of a Gaussian likelihood function. 43 | 44 | .. code:: cpp 45 | 46 | #define MCMC_ENABLE_EIGEN_WRAPPERS 47 | #include "mcmc.hpp" 48 | 49 | inline 50 | Eigen::VectorXd 51 | eigen_randn_colvec(size_t nr) 52 | { 53 | static std::mt19937 gen{ std::random_device{}() }; 54 | static std::normal_distribution<> dist; 55 | 56 | return Eigen::VectorXd{ nr }.unaryExpr([&](double x) { (void)(x); return dist(gen); }); 57 | } 58 | 59 | struct norm_data_t { 60 | double sigma; 61 | Eigen::VectorXd x; 62 | 63 | double mu_0; 64 | double sigma_0; 65 | }; 66 | 67 | double ll_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 68 | { 69 | const double pi = 3.14159265358979; 70 | 71 | // 72 | 73 | const double mu = vals_inp(0); 74 | 75 | norm_data_t* dta = reinterpret_cast(ll_data); 76 | const double sigma = dta->sigma; 77 | const Eigen::VectorXd x = dta->x; 78 | 79 | const int n_vals = x.size(); 80 | 81 | // 82 | 83 | const double ret = - n_vals * (0.5 * std::log(2*pi) + std::log(sigma)) - (x.array() - mu).pow(2).sum() / (2*sigma*sigma); 84 | 85 | // 86 | 87 | return ret; 88 | } 89 | 90 | double log_pr_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 91 | { 92 | const double pi = 3.14159265358979; 93 | 94 | // 95 | 96 | norm_data_t* dta = reinterpret_cast< norm_data_t* >(ll_data); 97 | 98 | const double mu_0 = dta->mu_0; 99 | const double sigma_0 = dta->sigma_0; 100 | 101 | const double x = vals_inp(0); 102 | 103 | const double ret = - 0.5*std::log(2*pi) - std::log(sigma_0) - std::pow(x - mu_0,2) / (2*sigma_0*sigma_0); 104 | 105 | return ret; 106 | } 107 | 108 | double log_target_dens(const Eigen::VectorXd& vals_inp, void* ll_data) 109 | { 110 | return ll_dens(vals_inp,ll_data) + log_pr_dens(vals_inp,ll_data); 111 | } 112 | 113 | int main() 114 | { 115 | const int n_data = 100; 116 | const double mu = 2.0; 117 | 118 | norm_data_t dta; 119 | dta.sigma = 1.0; 120 | dta.mu_0 = 1.0; 121 | dta.sigma_0 = 2.0; 122 | 123 | Eigen::VectorXd x_dta = mu + eigen_randn_colvec(n_data).array(); 124 | dta.x = x_dta; 125 | 126 | Eigen::VectorXd initial_val(1); 127 | initial_val(0) = 1.0; 128 | 129 | // 130 | 131 | mcmc::algo_settings_t settings; 132 | 133 | settings.rwmh_settings.par_scale = 0.4; 134 | settings.rwmh_settings.n_burnin_draws = 2000; 135 | settings.rwmh_settings.n_keep_draws = 2000; 136 | 137 | // 138 | 139 | Eigen::MatrixXd draws_out; 140 | mcmc::rwmh(initial_val, log_target_dens, draws_out, &dta, settings); 141 | 142 | // 143 | 144 | std::cout << "de mean:\n" << draws_out.colwise().mean() << std::endl; 145 | std::cout << "acceptance rate: " << static_cast(settings.rwmh_settings.n_accept_draws) / settings.rwmh_settings.n_keep_draws << std::endl; 146 | 147 | // 148 | 149 | return 0; 150 | } 151 | 152 | On x86-based computers, this example can be compiled using: 153 | 154 | .. code:: bash 155 | 156 | 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 157 | 158 | 159 | ---- 160 | 161 | Test suite 162 | ---------- 163 | 164 | You can build the test suite as follows: 165 | 166 | .. code:: bash 167 | 168 | # compile tests 169 | cd ./tests 170 | ./setup 171 | cd ./examples 172 | ./configure -l eigen 173 | make 174 | ./rwmh.test 175 | -------------------------------------------------------------------------------- /include/stats/dnorm.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * pdf of the univariate normal distribution 23 | */ 24 | 25 | #ifndef _statsmcmc_dnorm_HPP 26 | #define _statsmcmc_dnorm_HPP 27 | 28 | // 29 | // scalar input 30 | 31 | namespace internal 32 | { 33 | 34 | template 35 | constexpr 36 | bool 37 | is_posinf(const T x) 38 | noexcept 39 | { 40 | return x == std::numeric_limits::infinity(); 41 | } 42 | 43 | template 44 | constexpr 45 | bool 46 | is_neginf(const T x) 47 | noexcept 48 | { 49 | return x == - std::numeric_limits::infinity(); 50 | } 51 | 52 | template 53 | constexpr 54 | bool 55 | all_posinf(const T1 x, const T2 y) 56 | noexcept 57 | { 58 | return( is_posinf(x) && is_posinf(y) ); 59 | } 60 | 61 | template 62 | constexpr 63 | bool 64 | all_neginf(const T1 x, const T2 y) 65 | noexcept 66 | { 67 | return( is_neginf(x) && is_neginf(y) ); 68 | } 69 | 70 | template 71 | constexpr 72 | bool 73 | is_inf(const T x) 74 | noexcept 75 | { 76 | return( is_neginf(x) || is_posinf(x) ); 77 | } 78 | 79 | template 80 | constexpr 81 | bool 82 | any_inf(const T1 x, const T2 y) 83 | noexcept 84 | { 85 | return( is_inf(x) || is_inf(y) ); 86 | } 87 | 88 | // 89 | 90 | template 91 | constexpr 92 | T 93 | dnorm_log_compute(const T z, const T sigma_par) 94 | noexcept 95 | { 96 | return( - T(0.5)*T(MCMC_LOG_2PI) - std::log(sigma_par) - z*z/T(2) ); 97 | } 98 | 99 | template 100 | constexpr 101 | T 102 | dnorm_limit_vals(const T x, const T mu_par, const T sigma_par) 103 | noexcept 104 | { 105 | return( // sigma == Inf 106 | is_posinf(sigma_par) ? \ 107 | T(0) : 108 | // sigma finite; x == mu == Inf or -Inf 109 | all_posinf(x,mu_par) || all_neginf(x,mu_par) ? \ 110 | std::numeric_limits::quiet_NaN() : 111 | // sigma == 0 and x-mu == 0 112 | sigma_par == T(0) && x == mu_par ? \ 113 | std::numeric_limits::infinity() : 114 | // 115 | T(0) ); 116 | } 117 | 118 | template 119 | constexpr 120 | T 121 | dnorm_vals_check(const T x, const T mu_par, const T sigma_par, const bool log_form) 122 | noexcept 123 | { 124 | return( !norm_sanity_check(x,mu_par,sigma_par) ? \ 125 | std::numeric_limits::quiet_NaN() : 126 | // 127 | any_inf(x,mu_par,sigma_par) || sigma_par == T(0) ? \ 128 | log_if(dnorm_limit_vals(x,mu_par,sigma_par),log_form) : 129 | // 130 | exp_if(dnorm_log_compute((x-mu_par)/sigma_par,sigma_par), !log_form) ); 131 | } 132 | 133 | template> 134 | constexpr 135 | TC 136 | dnorm_type_check(const T1 x, const T2 mu_par, const T3 sigma_par, const bool log_form) 137 | noexcept 138 | { 139 | return dnorm_vals_check(static_cast(x),static_cast(mu_par), 140 | static_cast(sigma_par),log_form); 141 | } 142 | 143 | } 144 | 145 | template 146 | constexpr 147 | common_return_t 148 | dnorm(const T1 x, const T2 mu_par, const T3 sigma_par, const bool log_form) 149 | noexcept 150 | { 151 | return internal::dnorm_type_check(x,mu_par,sigma_par,log_form); 152 | } 153 | 154 | template 155 | constexpr 156 | return_t 157 | dnorm(const T x, const bool log_form) 158 | noexcept 159 | { 160 | return dnorm(x,T(0),T(1),log_form); 161 | } 162 | 163 | // 164 | // matrix/vector input 165 | 166 | namespace internal 167 | { 168 | 169 | inline 170 | Mat_t 171 | dnorm_compute(const Mat_t& x, const fp_t sigma_par, const bool log_form) 172 | { 173 | const fp_t norm_term = - fp_t(0.5) * fp_t(MCMC_LOG_2PI) - std::log(sigma_par); 174 | Mat_t ret = BMO_MATOPS_ARRAY_ADD_SCALAR(- BMO_MATOPS_ARRAY_DIV_SCALAR( BMO_MATOPS_HADAMARD_PROD(x,x), fp_t(2) ), norm_term); 175 | 176 | if (!log_form) { 177 | return BMO_MATOPS_EXP(ret); 178 | } 179 | 180 | // 181 | 182 | return ret; 183 | } 184 | 185 | inline 186 | Mat_t 187 | dnorm_int(const Mat_t& x, const fp_t mu_par, const fp_t sigma_par, const bool log_form) 188 | { 189 | return dnorm_compute( BMO_MATOPS_ARRAY_ADD_DIV_SCALARS(x,-mu_par,sigma_par), sigma_par, log_form ); 190 | } 191 | 192 | } 193 | 194 | inline 195 | Mat_t 196 | dnorm(const Mat_t& x, const bool log_form = false) 197 | { 198 | return internal::dnorm_int(x,fp_t(0),fp_t(1),log_form); 199 | } 200 | 201 | inline 202 | Mat_t 203 | dnorm(const Mat_t& x, const fp_t mu_par, const fp_t sigma_par, const bool log_form) 204 | { 205 | return internal::dnorm_int(x,mu_par,sigma_par,log_form); 206 | } 207 | 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /include/misc/mcmc_options.hpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // version 29 | 30 | #ifndef MCMC_VERSION_MAJOR 31 | #define MCMC_VERSION_MAJOR 2 32 | #endif 33 | 34 | #ifndef MCMC_VERSION_MINOR 35 | #define MCMC_VERSION_MINOR 1 36 | #endif 37 | 38 | #ifndef MCMC_VERSION_PATCH 39 | #define MCMC_VERSION_PATCH 0 40 | #endif 41 | 42 | // 43 | 44 | #ifdef _MSC_VER 45 | #error MCMCLib: MSVC is not supported 46 | #endif 47 | 48 | // 49 | 50 | #if defined(_OPENMP) && !defined(MCMC_DONT_USE_OPENMP) 51 | #undef MCMC_USE_OPENMP 52 | #define MCMC_USE_OPENMP 53 | #endif 54 | 55 | #if !defined(_OPENMP) && defined(MCMC_USE_OPENMP) 56 | #undef MCMC_USE_OPENMP 57 | 58 | #undef MCMC_DONE_USE_OPENMP 59 | #define MCMC_DONE_USE_OPENMP 60 | #endif 61 | 62 | // #ifdef MCMC_USE_OPENMP 63 | // #include "omp.h" // OpenMP 64 | // #endif 65 | 66 | #ifdef MCMC_DONT_USE_OPENMP 67 | #ifdef MCMC_USE_OPENMP 68 | #undef MCMC_USE_OPENMP 69 | #endif 70 | #endif 71 | 72 | // 73 | 74 | #ifndef mcmclib_inline 75 | #define mcmclib_inline 76 | #endif 77 | 78 | // floating point number type 79 | 80 | #ifndef MCMC_FPN_TYPE 81 | #define MCMC_FPN_TYPE double 82 | #endif 83 | 84 | #if MCMC_FPN_TYPE == float 85 | #undef MCMC_FPN_SMALL_NUMBER 86 | #define MCMC_FPN_SMALL_NUMBER fp_t(1e-05) 87 | #elif MCMC_FPN_TYPE == double 88 | #undef MCMC_FPN_SMALL_NUMBER 89 | #define MCMC_FPN_SMALL_NUMBER fp_t(1e-08) 90 | #else 91 | #error floating-point number type must be 'float' or 'double' 92 | #endif 93 | 94 | // 95 | 96 | namespace mcmc 97 | { 98 | using uint_t = unsigned int; 99 | using fp_t = MCMC_FPN_TYPE; 100 | 101 | using rand_engine_t = std::mt19937_64; 102 | 103 | static const double eps_dbl = std::numeric_limits::epsilon(); 104 | static const double posinf = std::numeric_limits::infinity(); 105 | static const double neginf = - std::numeric_limits::infinity(); 106 | } 107 | 108 | // 109 | 110 | #ifdef MCMC_ENABLE_ARMA_WRAPPERS 111 | #ifdef USE_RCPP_ARMADILLO 112 | #include 113 | #else 114 | #ifndef ARMA_DONT_USE_WRAPPER 115 | #define ARMA_DONT_USE_WRAPPER 116 | #endif 117 | 118 | #include "armadillo" 119 | #endif 120 | 121 | #ifdef MCMC_USE_OPENMP 122 | #ifndef ARMA_USE_OPENMP 123 | #define ARMA_USE_OPENMP 124 | #endif 125 | #endif 126 | 127 | #ifdef MCMC_DONT_USE_OPENMP 128 | #ifndef ARMA_DONT_USE_OPENMP 129 | #define ARMA_DONT_USE_OPENMP 130 | #endif 131 | #endif 132 | 133 | #ifndef BMO_ENABLE_ARMA_WRAPPERS 134 | #define BMO_ENABLE_ARMA_WRAPPERS 135 | #endif 136 | 137 | namespace mcmc 138 | { 139 | using ColVec_t = arma::Col; 140 | using RowVec_t = arma::Row; 141 | using ColVecInt_t = arma::Col; 142 | using RowVecInt_t = arma::Row; 143 | using ColVecUInt_t = arma::Col; 144 | 145 | using Mat_t = arma::Mat; 146 | } 147 | #elif defined MCMC_ENABLE_EIGEN_WRAPPERS 148 | #include 149 | #include 150 | #include 151 | 152 | #ifndef BMO_ENABLE_EIGEN_WRAPPERS 153 | #define BMO_ENABLE_EIGEN_WRAPPERS 154 | #endif 155 | 156 | // template 157 | // using EigenMat = Eigen::Matrix; 158 | 159 | namespace mcmc 160 | { 161 | using ColVec_t = Eigen::Matrix; 162 | using RowVec_t = Eigen::Matrix; 163 | using ColVecInt_t = Eigen::Matrix; 164 | using RowVecInt_t = Eigen::Matrix; 165 | using ColVecUInt_t = Eigen::Matrix; 166 | 167 | using Mat_t = Eigen::Matrix; 168 | } 169 | #else 170 | #error MCMCLib: you must enable the Armadillo OR Eigen wrappers 171 | #endif 172 | 173 | // 174 | 175 | #ifndef BMO_ENABLE_EXTRA_FEATURES 176 | #define BMO_ENABLE_EXTRA_FEATURES 177 | #endif 178 | 179 | #ifndef BMO_ENABLE_EXTRA_EXPERIMENTAL 180 | #define BMO_ENABLE_EXTRA_EXPERIMENTAL 181 | #endif 182 | 183 | #ifndef BMO_ENABLE_STATS_FEATURES 184 | #define BMO_ENABLE_STATS_FEATURES 185 | #endif 186 | 187 | #ifndef BMO_RNG_ENGINE_TYPE 188 | #define BMO_RNG_ENGINE_TYPE mcmc::rand_engine_t 189 | #endif 190 | 191 | #ifndef BMO_CORE_TYPES 192 | #define BMO_CORE_TYPES 193 | 194 | namespace bmo 195 | { 196 | using fp_t = MCMC_FPN_TYPE; 197 | 198 | using ColVec_t = mcmc::ColVec_t; 199 | using RowVec_t = mcmc::RowVec_t; 200 | using ColVecInt_t = mcmc::ColVecInt_t; 201 | using RowVecInt_t = mcmc::RowVecInt_t; 202 | using ColVecUInt_t = mcmc::ColVecUInt_t; 203 | 204 | using Mat_t = mcmc::Mat_t; 205 | } 206 | #endif 207 | 208 | #include "BaseMatrixOps/include/BaseMatrixOps.hpp" 209 | 210 | namespace mcmc 211 | { 212 | using Cube_t = bmo::Cube_t; 213 | } 214 | 215 | -------------------------------------------------------------------------------- /src/rwmh.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Random Walk Metropolis-Hastings (RWMH) MCMC 23 | */ 24 | 25 | #include "mcmc.hpp" 26 | 27 | // [MCMC_BEGIN] 28 | mcmclib_inline 29 | bool 30 | mcmc::internal::rwmh_impl( 31 | const ColVec_t& initial_vals, 32 | std::function target_log_kernel, 33 | Mat_t& draws_out, 34 | void* target_data, 35 | algo_settings_t* settings_inp 36 | ) 37 | { 38 | bool success = false; 39 | 40 | const size_t n_vals = BMO_MATOPS_SIZE(initial_vals); 41 | 42 | // 43 | // RWMH settings 44 | 45 | algo_settings_t settings; 46 | 47 | if (settings_inp) { 48 | settings = *settings_inp; 49 | } 50 | 51 | const size_t n_keep_draws = settings.rwmh_settings.n_keep_draws; 52 | const size_t n_burnin_draws = settings.rwmh_settings.n_burnin_draws; 53 | const size_t n_total_draws = n_burnin_draws + n_keep_draws; 54 | 55 | const fp_t par_scale = settings.rwmh_settings.par_scale; 56 | 57 | const Mat_t cov_mcmc = (BMO_MATOPS_SIZE(settings.rwmh_settings.cov_mat) == n_vals*n_vals) ? settings.rwmh_settings.cov_mat : BMO_MATOPS_EYE(n_vals); 58 | 59 | const bool vals_bound = settings.vals_bound; 60 | 61 | const ColVec_t lower_bounds = settings.lower_bounds; 62 | const ColVec_t upper_bounds = settings.upper_bounds; 63 | 64 | const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds); 65 | 66 | rand_engine_t rand_engine(settings.rng_seed_value); 67 | 68 | // parallelization setup 69 | 70 | #ifdef MCMC_USE_OPENMP 71 | int omp_n_threads = 1; 72 | 73 | if (settings.rwmh_settings.omp_n_threads > 0) { 74 | omp_n_threads = settings.rwmh_settings.omp_n_threads; 75 | } else { 76 | omp_n_threads = std::max(1, static_cast(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores 77 | } 78 | #endif 79 | 80 | // lambda function for box constraints 81 | 82 | std::function box_log_kernel \ 83 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, void* target_data) \ 84 | -> fp_t 85 | { 86 | if (vals_bound) { 87 | ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds); 88 | 89 | return target_log_kernel(vals_inv_trans, target_data) + log_jacobian(vals_inp, bounds_type, lower_bounds, upper_bounds); 90 | } else { 91 | return target_log_kernel(vals_inp, target_data); 92 | } 93 | }; 94 | 95 | // 96 | // setup 97 | 98 | ColVec_t rand_vec(n_vals); 99 | ColVec_t first_draw = initial_vals; 100 | 101 | if (vals_bound) { // should we transform the parameters? 102 | first_draw = transform(initial_vals, bounds_type, lower_bounds, upper_bounds); 103 | } 104 | 105 | BMO_MATOPS_SET_SIZE(draws_out, n_keep_draws, n_vals); 106 | 107 | fp_t prev_LP = box_log_kernel(first_draw, target_data); 108 | fp_t prop_LP = prev_LP; 109 | 110 | ColVec_t prev_draw = first_draw; 111 | ColVec_t new_draw = first_draw; 112 | 113 | Mat_t cov_mcmc_chol = par_scale * BMO_MATOPS_CHOL_LOWER(cov_mcmc); 114 | 115 | // 116 | 117 | size_t n_accept_draws = 0; 118 | 119 | for (size_t draw_ind = 0; draw_ind < n_total_draws; ++draw_ind) { 120 | 121 | // new_draw = prev_draw + cov_mcmc_chol * bmo::stats::rnorm_vec(n_vals, rand_engine); 122 | bmo::stats::internal::rnorm_vec_inplace(n_vals, rand_engine, rand_vec); 123 | new_draw = prev_draw + cov_mcmc_chol * rand_vec; 124 | 125 | prop_LP = box_log_kernel(new_draw, target_data); 126 | 127 | if (!std::isfinite(prop_LP)) { 128 | prop_LP = neginf; 129 | } 130 | 131 | // 132 | 133 | fp_t comp_val = std::min(fp_t(0.0), prop_LP - prev_LP); 134 | fp_t z = bmo::stats::runif(rand_engine); 135 | 136 | if (z < std::exp(comp_val)) { 137 | prev_draw = new_draw; 138 | prev_LP = prop_LP; 139 | 140 | if (draw_ind >= n_burnin_draws) { 141 | ++n_accept_draws; 142 | } 143 | } 144 | 145 | // 146 | 147 | if (draw_ind >= n_burnin_draws) { 148 | draws_out.row(draw_ind - n_burnin_draws) = BMO_MATOPS_TRANSPOSE(prev_draw); 149 | } 150 | } 151 | 152 | success = true; 153 | 154 | // 155 | 156 | if (vals_bound) { 157 | #ifdef MCMC_USE_OPENMP 158 | #pragma omp parallel for num_threads(omp_n_threads) 159 | #endif 160 | for (size_t draw_ind = 0; draw_ind < n_keep_draws; draw_ind++) { 161 | draws_out.row(draw_ind) = inv_transform(draws_out.row(draw_ind), bounds_type, lower_bounds, upper_bounds); 162 | } 163 | } 164 | 165 | if (settings_inp) { 166 | settings_inp->rwmh_settings.n_accept_draws = n_accept_draws; 167 | } 168 | 169 | // 170 | 171 | return success; 172 | } 173 | 174 | // wrappers 175 | 176 | mcmclib_inline 177 | bool 178 | mcmc::rwmh( 179 | const ColVec_t& initial_vals, 180 | std::function target_log_kernel, 181 | Mat_t& draws_out, 182 | void* target_data 183 | ) 184 | { 185 | return internal::rwmh_impl(initial_vals,target_log_kernel,draws_out,target_data,nullptr); 186 | } 187 | 188 | mcmclib_inline 189 | bool 190 | mcmc::rwmh( 191 | const ColVec_t& initial_vals, 192 | std::function target_log_kernel, 193 | Mat_t& draws_out, 194 | void* target_data, 195 | algo_settings_t& settings 196 | ) 197 | { 198 | return internal::rwmh_impl(initial_vals,target_log_kernel,draws_out,target_data,&settings); 199 | } 200 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # You can set these variables from the command line. 2 | SPHINXOPTS = 3 | SPHINXBUILD = sphinx-build 4 | PAPER = 5 | BUILDDIR = build 6 | 7 | # User-friendly check for sphinx-build 8 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 9 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 10 | endif 11 | 12 | # Internal variables. 13 | PAPEROPT_a4 = -D latex_paper_size=a4 14 | PAPEROPT_letter = -D latex_paper_size=letter 15 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | # the i18n builder cannot share the environment and doctrees with the others 17 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 18 | 19 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext api 20 | 21 | default: html 22 | 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | clean: 51 | rm -rf $(BUILDDIR)/* 52 | 53 | html: 54 | doxygen 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | doxygen 61 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 62 | @echo 63 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 64 | 65 | singlehtml: 66 | doxygen 67 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 68 | @echo 69 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 70 | 71 | pickle: 72 | doxygen 73 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 74 | @echo 75 | @echo "Build finished; now you can process the pickle files." 76 | 77 | json: 78 | doxygen 79 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 80 | @echo 81 | @echo "Build finished; now you can process the JSON files." 82 | 83 | htmlhelp: 84 | doxygen 85 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 86 | @echo 87 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 88 | ".hhp project file in $(BUILDDIR)/htmlhelp." 89 | 90 | epub: 91 | doxygen 92 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 93 | @echo 94 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 95 | 96 | latex: 97 | doxygen 98 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 99 | @echo 100 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 101 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 102 | "(use \`make latexpdf' here to do that automatically)." 103 | 104 | latexpdf: 105 | doxygen 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | latexpdfja: 112 | doxygen 113 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 114 | @echo "Running LaTeX files through platex and dvipdfmx..." 115 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 116 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 117 | 118 | text: 119 | doxygen 120 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 121 | @echo 122 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 123 | 124 | man: 125 | doxygen 126 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 127 | @echo 128 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 129 | 130 | texinfo: 131 | doxygen 132 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 133 | @echo 134 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 135 | @echo "Run \`make' in that directory to run these through makeinfo" \ 136 | "(use \`make info' here to do that automatically)." 137 | 138 | info: 139 | doxygen 140 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 141 | @echo "Running Texinfo files through makeinfo..." 142 | make -C $(BUILDDIR)/texinfo info 143 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 144 | 145 | gettext: 146 | doxygen 147 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 148 | @echo 149 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 150 | 151 | changes: 152 | doxygen 153 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 154 | @echo 155 | @echo "The overview file is in $(BUILDDIR)/changes." 156 | 157 | linkcheck: 158 | doxygen 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | doxygen 166 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 167 | @echo "Testing of doctests in the sources finished, look at the " \ 168 | "results in $(BUILDDIR)/doctest/output.txt." 169 | 170 | coverage: 171 | doxygen 172 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 173 | @echo "Testing of coverage in the sources finished, look at the " \ 174 | "results in $(BUILDDIR)/coverage/python.txt." 175 | 176 | xml: 177 | doxygen 178 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 179 | @echo 180 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 181 | 182 | pseudoxml: 183 | doxygen 184 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 185 | @echo 186 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 187 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 1>NUL 2>NUL 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | doxygen 77 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 78 | if errorlevel 1 exit /b 1 79 | echo. 80 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 81 | goto end 82 | ) 83 | 84 | if "%1" == "dirhtml" ( 85 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 86 | if errorlevel 1 exit /b 1 87 | echo. 88 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 89 | goto end 90 | ) 91 | 92 | if "%1" == "singlehtml" ( 93 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 97 | goto end 98 | ) 99 | 100 | if "%1" == "pickle" ( 101 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 102 | if errorlevel 1 exit /b 1 103 | echo. 104 | echo.Build finished; now you can process the pickle files. 105 | goto end 106 | ) 107 | 108 | if "%1" == "json" ( 109 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished; now you can process the JSON files. 113 | goto end 114 | ) 115 | 116 | if "%1" == "htmlhelp" ( 117 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished; now you can run HTML Help Workshop with the ^ 121 | .hhp project file in %BUILDDIR%/htmlhelp. 122 | goto end 123 | ) 124 | 125 | if "%1" == "qthelp" ( 126 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 127 | if errorlevel 1 exit /b 1 128 | echo. 129 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 130 | .qhcp project file in %BUILDDIR%/qthelp, like this: 131 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\packagename.qhcp 132 | echo.To view the help file: 133 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\packagename.ghc 134 | goto end 135 | ) 136 | 137 | if "%1" == "devhelp" ( 138 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 139 | if errorlevel 1 exit /b 1 140 | echo. 141 | echo.Build finished. 142 | goto end 143 | ) 144 | 145 | if "%1" == "epub" ( 146 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 147 | if errorlevel 1 exit /b 1 148 | echo. 149 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 150 | goto end 151 | ) 152 | 153 | if "%1" == "latex" ( 154 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 155 | if errorlevel 1 exit /b 1 156 | echo. 157 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 158 | goto end 159 | ) 160 | 161 | if "%1" == "latexpdf" ( 162 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 163 | cd %BUILDDIR%/latex 164 | make all-pdf 165 | cd %~dp0 166 | echo. 167 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 168 | goto end 169 | ) 170 | 171 | if "%1" == "latexpdfja" ( 172 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 173 | cd %BUILDDIR%/latex 174 | make all-pdf-ja 175 | cd %~dp0 176 | echo. 177 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 178 | goto end 179 | ) 180 | 181 | if "%1" == "text" ( 182 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Build finished. The text files are in %BUILDDIR%/text. 186 | goto end 187 | ) 188 | 189 | if "%1" == "man" ( 190 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 191 | if errorlevel 1 exit /b 1 192 | echo. 193 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 194 | goto end 195 | ) 196 | 197 | if "%1" == "texinfo" ( 198 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 199 | if errorlevel 1 exit /b 1 200 | echo. 201 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 202 | goto end 203 | ) 204 | 205 | if "%1" == "gettext" ( 206 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 207 | if errorlevel 1 exit /b 1 208 | echo. 209 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 210 | goto end 211 | ) 212 | 213 | if "%1" == "changes" ( 214 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 215 | if errorlevel 1 exit /b 1 216 | echo. 217 | echo.The overview file is in %BUILDDIR%/changes. 218 | goto end 219 | ) 220 | 221 | if "%1" == "linkcheck" ( 222 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 223 | if errorlevel 1 exit /b 1 224 | echo. 225 | echo.Link check complete; look for any errors in the above output ^ 226 | or in %BUILDDIR%/linkcheck/output.txt. 227 | goto end 228 | ) 229 | 230 | if "%1" == "doctest" ( 231 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 232 | if errorlevel 1 exit /b 1 233 | echo. 234 | echo.Testing of doctests in the sources finished, look at the ^ 235 | results in %BUILDDIR%/doctest/output.txt. 236 | goto end 237 | ) 238 | 239 | if "%1" == "coverage" ( 240 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 241 | if errorlevel 1 exit /b 1 242 | echo. 243 | echo.Testing of coverage in the sources finished, look at the ^ 244 | results in %BUILDDIR%/coverage/python.txt. 245 | goto end 246 | ) 247 | 248 | if "%1" == "xml" ( 249 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 250 | if errorlevel 1 exit /b 1 251 | echo. 252 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 253 | goto end 254 | ) 255 | 256 | if "%1" == "pseudoxml" ( 257 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 258 | if errorlevel 1 exit /b 1 259 | echo. 260 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 261 | goto end 262 | ) 263 | 264 | :end 265 | -------------------------------------------------------------------------------- /src/mala.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Metropolis-adjusted Langevin algorithm (MALA) 23 | */ 24 | 25 | #include "mcmc.hpp" 26 | 27 | // [MCMC_BEGIN] 28 | mcmclib_inline 29 | bool 30 | mcmc::internal::mala_impl( 31 | const ColVec_t& initial_vals, 32 | std::function target_log_kernel, 33 | Mat_t& draws_out, 34 | void* target_data, 35 | algo_settings_t* settings_inp 36 | ) 37 | { 38 | bool success = false; 39 | 40 | const size_t n_vals = BMO_MATOPS_SIZE(initial_vals); 41 | 42 | // 43 | // MALA settings 44 | 45 | algo_settings_t settings; 46 | 47 | if (settings_inp) { 48 | settings = *settings_inp; 49 | } 50 | 51 | const size_t n_burnin_draws = settings.mala_settings.n_burnin_draws; 52 | const size_t n_keep_draws = settings.mala_settings.n_keep_draws; 53 | const size_t n_total_draws = n_burnin_draws + n_keep_draws; 54 | 55 | const fp_t step_size = settings.mala_settings.step_size; 56 | 57 | const Mat_t precond_matrix = (BMO_MATOPS_SIZE(settings.mala_settings.precond_mat) == n_vals*n_vals) ? settings.mala_settings.precond_mat : BMO_MATOPS_EYE(n_vals); 58 | const Mat_t sqrt_precond_matrix = BMO_MATOPS_CHOL_LOWER(precond_matrix); 59 | 60 | const bool vals_bound = settings.vals_bound; 61 | 62 | const ColVec_t lower_bounds = settings.lower_bounds; 63 | const ColVec_t upper_bounds = settings.upper_bounds; 64 | 65 | const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds); 66 | 67 | rand_engine_t rand_engine(settings.rng_seed_value); 68 | 69 | // parallelization setup 70 | 71 | #ifdef MCMC_USE_OPENMP 72 | int omp_n_threads = 1; 73 | 74 | if (settings.mala_settings.omp_n_threads > 0) { 75 | omp_n_threads = settings.mala_settings.omp_n_threads; 76 | } else { 77 | omp_n_threads = std::max(1, static_cast(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores 78 | } 79 | #endif 80 | 81 | // 82 | // lambda functions for box constraints 83 | 84 | std::function box_log_kernel \ 85 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, ColVec_t* grad_out, void* target_data) \ 86 | -> fp_t 87 | { 88 | if (vals_bound) { 89 | ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds); 90 | 91 | return target_log_kernel(vals_inv_trans, nullptr, target_data) + log_jacobian(vals_inp, bounds_type, lower_bounds, upper_bounds); 92 | } else { 93 | return target_log_kernel(vals_inp, nullptr, target_data); 94 | } 95 | }; 96 | 97 | std::function mala_mean_fn \ 98 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds, precond_matrix] (const ColVec_t& vals_inp, void* target_data, const fp_t step_size, Mat_t* jacob_matrix_out) \ 99 | -> ColVec_t 100 | { 101 | const size_t n_vals = BMO_MATOPS_SIZE(vals_inp); 102 | ColVec_t grad_obj(n_vals); 103 | 104 | if (vals_bound) { 105 | ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds); 106 | 107 | target_log_kernel(vals_inv_trans,&grad_obj,target_data); 108 | 109 | // 110 | 111 | Mat_t jacob_matrix = inv_jacobian_adjust(vals_inp, bounds_type, lower_bounds, upper_bounds); 112 | 113 | if (jacob_matrix_out) { 114 | *jacob_matrix_out = jacob_matrix; 115 | } 116 | 117 | // 118 | 119 | return vals_inp + step_size * step_size * jacob_matrix * precond_matrix * grad_obj / fp_t(2); 120 | } else { 121 | target_log_kernel(vals_inp, &grad_obj, target_data); 122 | 123 | return vals_inp + step_size * step_size * precond_matrix * grad_obj / fp_t(2); 124 | } 125 | }; 126 | 127 | // 128 | // setup 129 | 130 | ColVec_t first_draw = initial_vals; 131 | 132 | if (vals_bound) { // should we transform the parameters? 133 | first_draw = transform(initial_vals, bounds_type, lower_bounds, upper_bounds); 134 | } 135 | 136 | BMO_MATOPS_SET_SIZE(draws_out, n_keep_draws, n_vals); 137 | 138 | fp_t prev_LP = box_log_kernel(first_draw, nullptr, target_data); 139 | fp_t prop_LP = prev_LP; 140 | 141 | ColVec_t prev_draw = first_draw; 142 | ColVec_t new_draw = first_draw; 143 | 144 | // 145 | 146 | size_t n_accept = 0; 147 | ColVec_t rand_vec(n_vals); 148 | 149 | for (size_t draw_ind = 0; draw_ind < n_total_draws; ++draw_ind) { 150 | bmo::stats::internal::rnorm_vec_inplace(n_vals, rand_engine, rand_vec); 151 | 152 | if (vals_bound) { 153 | Mat_t jacob_matrix; 154 | 155 | ColVec_t mean_vec = mala_mean_fn(prev_draw, target_data, step_size, &jacob_matrix); 156 | 157 | new_draw = mean_vec + step_size * BMO_MATOPS_CHOL_LOWER(jacob_matrix) * sqrt_precond_matrix * rand_vec; 158 | } else { 159 | new_draw = mala_mean_fn(prev_draw, target_data, step_size, nullptr) + step_size * sqrt_precond_matrix * rand_vec; 160 | } 161 | 162 | prop_LP = box_log_kernel(new_draw, nullptr, target_data); 163 | 164 | if (!std::isfinite(prop_LP)) { 165 | prop_LP = neginf; 166 | } 167 | 168 | // 169 | 170 | const fp_t comp_val = std::min(fp_t(0.01), prop_LP - prev_LP + mala_prop_adjustment(new_draw, prev_draw, step_size, vals_bound, precond_matrix, mala_mean_fn, target_data)); 171 | const fp_t z = bmo::stats::runif(rand_engine); 172 | 173 | if (z < std::exp(comp_val)) { 174 | prev_draw = new_draw; 175 | prev_LP = prop_LP; 176 | 177 | if (draw_ind >= n_burnin_draws) { 178 | draws_out.row(draw_ind - n_burnin_draws) = BMO_MATOPS_TRANSPOSE(new_draw); 179 | n_accept++; 180 | } 181 | } else { 182 | if (draw_ind >= n_burnin_draws) { 183 | draws_out.row(draw_ind - n_burnin_draws) = BMO_MATOPS_TRANSPOSE(prev_draw); 184 | } 185 | } 186 | } 187 | 188 | success = true; 189 | 190 | // 191 | 192 | if (vals_bound) { 193 | #ifdef MCMC_USE_OPENMP 194 | #pragma omp parallel for num_threads(omp_n_threads) 195 | #endif 196 | for (size_t draw_ind = 0; draw_ind < n_keep_draws; ++draw_ind) { 197 | draws_out.row(draw_ind) = inv_transform(draws_out.row(draw_ind), bounds_type, lower_bounds, upper_bounds); 198 | } 199 | } 200 | 201 | if (settings_inp) { 202 | settings_inp->mala_settings.n_accept_draws = n_accept; 203 | } 204 | 205 | // 206 | 207 | return success; 208 | } 209 | 210 | // wrappers 211 | 212 | mcmclib_inline 213 | bool 214 | mcmc::mala( 215 | const ColVec_t& initial_vals, 216 | std::function target_log_kernel, 217 | Mat_t& draws_out, 218 | void* target_data 219 | ) 220 | { 221 | return internal::mala_impl(initial_vals,target_log_kernel,draws_out,target_data,nullptr); 222 | } 223 | 224 | mcmclib_inline 225 | bool 226 | mcmc::mala( 227 | const ColVec_t& initial_vals, 228 | std::function target_log_kernel, 229 | Mat_t& draws_out, 230 | void* target_data, 231 | algo_settings_t& settings 232 | ) 233 | { 234 | return internal::mala_impl(initial_vals,target_log_kernel,draws_out,target_data,&settings); 235 | } 236 | -------------------------------------------------------------------------------- /src/hmc.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Hamiltonian Monte Carlo (HMC) 23 | */ 24 | 25 | #include "mcmc.hpp" 26 | 27 | // [MCMC_BEGIN] 28 | mcmclib_inline 29 | bool 30 | mcmc::internal::hmc_impl( 31 | const ColVec_t& initial_vals, 32 | std::function target_log_kernel, 33 | Mat_t& draws_out, 34 | void* target_data, 35 | algo_settings_t* settings_inp 36 | ) 37 | { 38 | bool success = false; 39 | 40 | const size_t n_vals = BMO_MATOPS_SIZE(initial_vals); 41 | 42 | // settings 43 | 44 | algo_settings_t settings; 45 | 46 | if (settings_inp) { 47 | settings = *settings_inp; 48 | } 49 | 50 | const size_t n_burnin_draws = settings.hmc_settings.n_burnin_draws; 51 | const size_t n_keep_draws = settings.hmc_settings.n_keep_draws; 52 | const size_t n_total_draws = n_burnin_draws + n_keep_draws; 53 | 54 | const fp_t step_size = settings.hmc_settings.step_size; 55 | const uint_t n_leap_steps = settings.hmc_settings.n_leap_steps; 56 | 57 | const Mat_t precond_matrix = (BMO_MATOPS_SIZE(settings.hmc_settings.precond_mat) == n_vals*n_vals) ? settings.hmc_settings.precond_mat : BMO_MATOPS_EYE(n_vals); 58 | const Mat_t inv_precond_matrix = BMO_MATOPS_INV(precond_matrix); 59 | const Mat_t sqrt_precond_matrix = BMO_MATOPS_CHOL_LOWER(precond_matrix); 60 | 61 | const bool vals_bound = settings.vals_bound; 62 | 63 | const ColVec_t lower_bounds = settings.lower_bounds; 64 | const ColVec_t upper_bounds = settings.upper_bounds; 65 | 66 | const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds); 67 | 68 | rand_engine_t rand_engine(settings.rng_seed_value); 69 | 70 | // parallelization setup 71 | 72 | #ifdef MCMC_USE_OPENMP 73 | int omp_n_threads = 1; 74 | 75 | if (settings.hmc_settings.omp_n_threads > 0) { 76 | omp_n_threads = settings.hmc_settings.omp_n_threads; 77 | } else { 78 | omp_n_threads = std::max(1, static_cast(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores 79 | } 80 | #endif 81 | 82 | // lambda function for box constraints 83 | 84 | std::function box_log_kernel \ 85 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, ColVec_t* grad_out, void* target_data) \ 86 | -> fp_t 87 | { 88 | if (vals_bound) { 89 | ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds); 90 | 91 | return target_log_kernel(vals_inv_trans, nullptr, target_data) + log_jacobian(vals_inp, bounds_type, lower_bounds, upper_bounds); 92 | } else { 93 | return target_log_kernel(vals_inp, nullptr, target_data); 94 | } 95 | }; 96 | 97 | // momentum update function 98 | 99 | std::function mntm_update_fn \ 100 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& pos_inp, const ColVec_t& mntm_inp, void* target_data, const fp_t step_size, Mat_t* jacob_matrix_out) \ 101 | -> ColVec_t 102 | { 103 | const size_t n_vals = BMO_MATOPS_SIZE(pos_inp); 104 | 105 | ColVec_t grad_obj(n_vals); 106 | 107 | if (vals_bound) { 108 | ColVec_t pos_inv_trans = inv_transform(pos_inp, bounds_type, lower_bounds, upper_bounds); 109 | 110 | target_log_kernel(pos_inv_trans, &grad_obj, target_data); 111 | 112 | // 113 | 114 | Mat_t jacob_matrix = inv_jacobian_adjust(pos_inp, bounds_type, lower_bounds, upper_bounds); 115 | 116 | if (jacob_matrix_out) { 117 | *jacob_matrix_out = jacob_matrix; 118 | } 119 | 120 | // 121 | 122 | return mntm_inp + step_size * jacob_matrix * grad_obj / fp_t(2); 123 | } else { 124 | target_log_kernel(pos_inp,&grad_obj,target_data); 125 | 126 | return mntm_inp + step_size * grad_obj / fp_t(2); 127 | } 128 | }; 129 | 130 | // setup 131 | 132 | ColVec_t first_draw = initial_vals; 133 | 134 | if (vals_bound) { // should we transform the parameters? 135 | first_draw = transform(initial_vals, bounds_type, lower_bounds, upper_bounds); 136 | } 137 | 138 | BMO_MATOPS_SET_SIZE(draws_out, n_keep_draws, n_vals); 139 | 140 | fp_t prev_U = - box_log_kernel(first_draw, nullptr, target_data); 141 | fp_t prop_U = prev_U; 142 | 143 | fp_t prop_K, prev_K; 144 | 145 | ColVec_t prev_draw = first_draw; 146 | ColVec_t new_draw = first_draw; 147 | 148 | ColVec_t new_mntm(n_vals); 149 | 150 | // 151 | 152 | size_t n_accept = 0; 153 | ColVec_t rand_vec(n_vals); 154 | 155 | for (size_t draw_ind = 0; draw_ind < n_total_draws; ++draw_ind) { 156 | bmo::stats::internal::rnorm_vec_inplace(n_vals, rand_engine, rand_vec); 157 | 158 | new_mntm = sqrt_precond_matrix * rand_vec; 159 | 160 | prev_K = BMO_MATOPS_DOT_PROD(new_mntm, inv_precond_matrix * new_mntm) / fp_t(2); 161 | 162 | new_draw = prev_draw; 163 | 164 | for (size_t k = 0; k < n_leap_steps; ++k) { 165 | // begin leap-frog steps 166 | 167 | new_mntm = mntm_update_fn(new_draw, new_mntm, target_data, step_size, nullptr); // first half-step 168 | 169 | // 170 | 171 | new_draw += step_size * inv_precond_matrix * new_mntm; 172 | 173 | // 174 | 175 | new_mntm = mntm_update_fn(new_draw, new_mntm, target_data, step_size, nullptr); // second half-step 176 | } 177 | 178 | prop_U = - box_log_kernel(new_draw, nullptr, target_data); 179 | 180 | if (!std::isfinite(prop_U)) { 181 | prop_U = posinf; 182 | } 183 | 184 | prop_K = BMO_MATOPS_DOT_PROD(new_mntm, inv_precond_matrix * new_mntm) / fp_t(2); 185 | 186 | // 187 | 188 | const fp_t comp_val = std::min(fp_t(0.01), - (prop_U + prop_K) + (prev_U + prev_K)); 189 | const fp_t z = bmo::stats::runif(rand_engine); 190 | 191 | if (z < std::exp(comp_val)) { 192 | prev_draw = new_draw; 193 | prev_U = prop_U; 194 | prev_K = prop_K; 195 | 196 | if (draw_ind >= n_burnin_draws) { 197 | draws_out.row(draw_ind - n_burnin_draws) = BMO_MATOPS_TRANSPOSE(new_draw); 198 | n_accept++; 199 | } 200 | } else { 201 | if (draw_ind >= n_burnin_draws) { 202 | draws_out.row(draw_ind - n_burnin_draws) = BMO_MATOPS_TRANSPOSE(prev_draw); 203 | } 204 | } 205 | } 206 | 207 | success = true; 208 | 209 | // 210 | 211 | if (vals_bound) { 212 | #ifdef MCMC_USE_OPENMP 213 | #pragma omp parallel for num_threads(omp_n_threads) 214 | #endif 215 | for (size_t draw_ind = 0; draw_ind < n_keep_draws; ++draw_ind) { 216 | draws_out.row(draw_ind) = inv_transform(draws_out.row(draw_ind), bounds_type, lower_bounds, upper_bounds); 217 | } 218 | } 219 | 220 | if (settings_inp) { 221 | settings_inp->hmc_settings.n_accept_draws = n_accept; 222 | } 223 | 224 | // 225 | 226 | return success; 227 | } 228 | 229 | // wrappers 230 | 231 | mcmclib_inline 232 | bool 233 | mcmc::hmc( 234 | const ColVec_t& initial_vals, 235 | std::function target_log_kernel, 236 | Mat_t& draws_out, 237 | void* target_data 238 | ) 239 | { 240 | return internal::hmc_impl(initial_vals,target_log_kernel,draws_out,target_data,nullptr); 241 | } 242 | 243 | mcmclib_inline 244 | bool 245 | mcmc::hmc( 246 | const ColVec_t& initial_vals, 247 | std::function target_log_kernel, 248 | Mat_t& draws_out, 249 | void* target_data, 250 | algo_settings_t& settings 251 | ) 252 | { 253 | return internal::hmc_impl(initial_vals,target_log_kernel,draws_out,target_data,&settings); 254 | } 255 | -------------------------------------------------------------------------------- /include/mcmc/nuts.ipp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * No-U-Turn Sampler (NUTS) (with Dual Averaging) 23 | */ 24 | 25 | #ifndef _mcmc_nuts_IPP 26 | #define _mcmc_nuts_IPP 27 | 28 | // 29 | 30 | inline 31 | fp_t 32 | nuts_find_initial_step_size( 33 | const ColVec_t& draw_vec, 34 | const ColVec_t& mntm_vec, 35 | const Mat_t& inv_precond_matrix, 36 | std::function box_log_kernel_fn, 37 | std::function leap_frog_fn, 38 | void* target_data 39 | ) 40 | { 41 | fp_t step_size = fp_t(1); // initial value 42 | 43 | // 44 | 45 | fp_t prev_U = - box_log_kernel_fn(draw_vec, nullptr, target_data); 46 | 47 | if (!std::isfinite(prev_U)) { 48 | prev_U = posinf; 49 | } 50 | 51 | fp_t prev_K = BMO_MATOPS_DOT_PROD(mntm_vec, inv_precond_matrix * mntm_vec) / fp_t(2); 52 | 53 | // 54 | 55 | ColVec_t new_draw = draw_vec; 56 | ColVec_t new_mntm = mntm_vec; 57 | 58 | leap_frog_fn(step_size, 1, new_draw, new_mntm, target_data); 59 | 60 | fp_t prop_U = - box_log_kernel_fn(new_draw, nullptr, target_data); 61 | 62 | if (!std::isfinite(prop_U)) { 63 | prop_U = posinf; 64 | } 65 | 66 | fp_t prop_K = BMO_MATOPS_DOT_PROD(new_mntm, inv_precond_matrix * new_mntm) / fp_t(2); 67 | 68 | // 69 | 70 | int a_val = 2 * ( - (prop_U + prop_K) + (prev_U + prev_K) > std::log(0.5) ) - 1; 71 | bool check_cond = (- (prop_U + prop_K) + (prev_U + prev_K)) > - std::log(2); 72 | 73 | while (check_cond) { 74 | step_size *= std::pow(2, a_val); 75 | 76 | leap_frog_fn(step_size, 1, new_draw, new_mntm, target_data); 77 | 78 | prop_U = - box_log_kernel_fn(new_draw, nullptr, target_data); 79 | 80 | if (!std::isfinite(prop_U)) { 81 | prop_U = posinf; 82 | } 83 | 84 | prop_K = BMO_MATOPS_DOT_PROD(new_mntm, inv_precond_matrix * new_mntm) / fp_t(2); 85 | 86 | // 87 | 88 | a_val = 2 * ( (- (prop_U + prop_K) + (prev_U + prev_K)) > std::log(0.5)) - 1; // not obvious from the paper, but I think we should update a... 89 | check_cond = (- (prop_U + prop_K) + (prev_U + prev_K)) > - std::log(2); 90 | } 91 | 92 | return step_size; 93 | } 94 | 95 | // 96 | 97 | inline 98 | void 99 | nuts_build_tree( 100 | const int direction_val, // v 101 | const fp_t step_size, // epsilon 102 | const fp_t log_rand_val, // u 103 | const fp_t prev_U, // - L(\theta^0) 104 | const fp_t prev_K, // 0.5 * [r^0]^T M^{-1} [r^0] 105 | const ColVec_t& draw_vec, // \theta 106 | const ColVec_t& mntm_vec, // r 107 | const Mat_t& inv_precond_matrix, // not in the original paper 108 | std::function box_log_kernel_fn, 109 | std::function leap_frog_fn, 110 | const size_t tree_depth, // j 111 | ColVec_t& new_draw, // \theta' 112 | ColVec_t& new_draw_pos, // \theta^+ 113 | ColVec_t& new_draw_neg, // \theta^- 114 | ColVec_t& new_mntm_pos, // r^+ 115 | ColVec_t& new_mntm_neg, // r^- 116 | size_t& n_val, // n and n' 117 | size_t& s_val, // s and s' 118 | fp_t& alpha_val, // \alpha and \alpha' 119 | size_t& n_alpha_val, // n_\alpha and n_\alpha' 120 | rand_engine_t& rand_engine, 121 | void* target_data 122 | ) 123 | { 124 | const fp_t max_tuning_par = 1000; // from the paper: We recommend setting \delta_max to a large value like 1000 so that it does not interfere with the algorithm so long as the simulation is even moderately accurate. 125 | 126 | if (tree_depth == size_t(0)) { 127 | new_draw = draw_vec; 128 | ColVec_t new_mntm = mntm_vec; 129 | 130 | // 131 | 132 | leap_frog_fn(direction_val * step_size, 1, new_draw, new_mntm, target_data); 133 | 134 | fp_t prop_U = - box_log_kernel_fn(new_draw, nullptr, target_data); 135 | 136 | if (!std::isfinite(prop_U)) { 137 | prop_U = posinf; 138 | } 139 | 140 | fp_t prop_K = BMO_MATOPS_DOT_PROD(new_mntm, inv_precond_matrix * new_mntm) / fp_t(2); 141 | 142 | // 143 | 144 | // fp_t log_log_rand_val = std::log(rand_val); 145 | 146 | n_val = (log_rand_val <= - prop_U - prop_K); 147 | s_val = (log_rand_val < max_tuning_par - prop_U - prop_K); 148 | 149 | // 150 | 151 | new_draw_pos = new_draw; 152 | new_draw_neg = new_draw; 153 | 154 | new_mntm_pos = new_mntm; 155 | new_mntm_neg = new_mntm; 156 | 157 | alpha_val = std::exp( std::min( fp_t(0), - (prop_U + prop_K) + (prev_U + prev_K)) ); 158 | n_alpha_val = 1; 159 | } else { 160 | size_t n_p_val; 161 | size_t s_p_val; 162 | fp_t alpha_p_val; 163 | size_t n_alpha_p_val; 164 | ColVec_t new_draw_p; 165 | 166 | nuts_build_tree( 167 | direction_val, step_size, log_rand_val, prev_U, prev_K, 168 | draw_vec, mntm_vec, inv_precond_matrix, 169 | box_log_kernel_fn, leap_frog_fn, tree_depth - 1, 170 | new_draw_p, new_draw_pos, new_draw_neg, new_mntm_pos, new_mntm_neg, 171 | n_p_val, s_p_val, alpha_p_val, n_alpha_p_val, rand_engine, target_data); 172 | 173 | // 174 | 175 | if (s_p_val == size_t(1)) { 176 | size_t n_pp_val; 177 | size_t s_pp_val; 178 | fp_t alpha_pp_val; 179 | size_t n_alpha_pp_val; 180 | 181 | ColVec_t new_draw_pp; 182 | 183 | // 184 | 185 | if (direction_val == -1) { 186 | ColVec_t dummy_draw = new_draw_pos; 187 | ColVec_t dummy_mntm = new_mntm_pos; 188 | ColVec_t draw_neg = new_draw_neg; 189 | ColVec_t mntm_neg = new_mntm_neg; 190 | 191 | nuts_build_tree( 192 | direction_val, step_size, log_rand_val, prev_U, prev_K, 193 | draw_neg, mntm_neg, inv_precond_matrix, 194 | box_log_kernel_fn, leap_frog_fn, tree_depth - 1, 195 | new_draw_pp, new_draw_neg, dummy_draw, new_mntm_neg, dummy_mntm, 196 | n_pp_val, s_pp_val, alpha_pp_val, n_alpha_pp_val, rand_engine, target_data); 197 | } else { 198 | ColVec_t dummy_draw = new_draw_neg; 199 | ColVec_t dummy_mntm = new_mntm_neg; 200 | ColVec_t draw_pos = new_draw_pos; 201 | ColVec_t mntm_pos = new_mntm_pos; 202 | 203 | nuts_build_tree( 204 | direction_val, step_size, log_rand_val, prev_U, prev_K, 205 | draw_pos, mntm_pos, inv_precond_matrix, 206 | box_log_kernel_fn, leap_frog_fn, tree_depth - 1, 207 | new_draw_pp, dummy_draw, new_draw_pos, dummy_mntm, new_mntm_pos, 208 | n_pp_val, s_pp_val, alpha_pp_val, n_alpha_pp_val, rand_engine, target_data); 209 | } 210 | 211 | // 212 | 213 | const fp_t prob_val = fp_t(n_pp_val) / fp_t( n_p_val + n_pp_val ); 214 | const fp_t z = bmo::stats::runif(rand_engine); 215 | 216 | if (z < prob_val) { 217 | new_draw_p = new_draw_pp; 218 | } 219 | 220 | n_p_val += n_pp_val; 221 | alpha_p_val += alpha_pp_val; 222 | n_alpha_p_val += n_alpha_pp_val; 223 | 224 | // 225 | 226 | int check_val_1 = BMO_MATOPS_DOT_PROD(new_draw_pos - new_draw_neg, new_mntm_neg) >= fp_t(0); 227 | int check_val_2 = BMO_MATOPS_DOT_PROD(new_draw_pos - new_draw_neg, new_mntm_pos) >= fp_t(0); 228 | 229 | s_p_val = s_pp_val * check_val_1 * check_val_2; 230 | } 231 | 232 | // 233 | 234 | n_val = n_p_val; 235 | s_val = s_p_val; 236 | alpha_val = alpha_p_val; 237 | n_alpha_val = n_alpha_p_val; 238 | 239 | new_draw = new_draw_p; 240 | } 241 | } 242 | 243 | #endif 244 | -------------------------------------------------------------------------------- /src/de.cpp: -------------------------------------------------------------------------------- 1 | /*################################################################################ 2 | ## 3 | ## Copyright (C) 2011-2023 Keith O'Hara 4 | ## 5 | ## This file is part of the MCMC C++ library. 6 | ## 7 | ## Licensed under the Apache License, Version 2.0 (the "License"); 8 | ## you may not use this file except in compliance with the License. 9 | ## You may obtain a copy of the License at 10 | ## 11 | ## http://www.apache.org/licenses/LICENSE-2.0 12 | ## 13 | ## Unless required by applicable law or agreed to in writing, software 14 | ## distributed under the License is distributed on an "AS IS" BASIS, 15 | ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ## See the License for the specific language governing permissions and 17 | ## limitations under the License. 18 | ## 19 | ################################################################################*/ 20 | 21 | /* 22 | * Differential Evolution (DE) MCMC 23 | */ 24 | 25 | #include "mcmc.hpp" 26 | 27 | // [MCMC_BEGIN] 28 | mcmclib_inline 29 | bool 30 | mcmc::internal::de_impl( 31 | const ColVec_t& initial_vals, 32 | std::function target_log_kernel, 33 | Cube_t& draws_out, 34 | void* target_data, 35 | algo_settings_t* settings_inp 36 | ) 37 | { 38 | bool success = false; 39 | 40 | const size_t n_vals = BMO_MATOPS_SIZE(initial_vals); 41 | 42 | // 43 | // DE settings 44 | 45 | algo_settings_t settings; 46 | 47 | if (settings_inp) { 48 | settings = *settings_inp; 49 | } 50 | 51 | const size_t n_pop = settings.de_settings.n_pop; 52 | const size_t n_keep_draws = settings.de_settings.n_keep_draws; 53 | const size_t n_burnin_draws = settings.de_settings.n_burnin_draws; 54 | 55 | const size_t n_total_draws = n_keep_draws + n_burnin_draws; 56 | 57 | const bool jumps = settings.de_settings.jumps; 58 | const fp_t par_b = settings.de_settings.par_b; 59 | // const fp_t par_gamma = settings.de_par_gamma; 60 | const fp_t par_gamma = 2.38 / std::sqrt(fp_t(2) * n_vals); 61 | const fp_t par_gamma_jump = settings.de_settings.par_gamma_jump; 62 | 63 | const bool vals_bound = settings.vals_bound; 64 | 65 | const ColVec_t lower_bounds = settings.lower_bounds; 66 | const ColVec_t upper_bounds = settings.upper_bounds; 67 | 68 | const ColVecInt_t bounds_type = determine_bounds_type(vals_bound, n_vals, lower_bounds, upper_bounds); 69 | 70 | ColVec_t par_initial_lb = ( BMO_MATOPS_SIZE(settings.de_settings.initial_lb) == n_vals ) ? settings.de_settings.initial_lb : BMO_MATOPS_ARRAY_ADD_SCALAR(initial_vals, -0.5); 71 | ColVec_t par_initial_ub = ( BMO_MATOPS_SIZE(settings.de_settings.initial_ub) == n_vals ) ? settings.de_settings.initial_ub : BMO_MATOPS_ARRAY_ADD_SCALAR(initial_vals, 0.5); 72 | 73 | sampling_bounds_check(vals_bound, n_vals, bounds_type, lower_bounds, upper_bounds, par_initial_lb, par_initial_ub); 74 | 75 | // parallelization setup 76 | 77 | int omp_n_threads = 1; 78 | 79 | #ifdef MCMC_USE_OPENMP 80 | if (settings.de_settings.omp_n_threads > 0) { 81 | omp_n_threads = settings.de_settings.omp_n_threads; 82 | } else { 83 | omp_n_threads = std::max(1, static_cast(omp_get_max_threads()) / 2); // OpenMP often detects the number of virtual/logical cores, not physical cores 84 | } 85 | #endif 86 | 87 | // random sampling setup 88 | 89 | rand_engine_t rand_engine(settings.rng_seed_value); 90 | std::vector rand_engines_vec; 91 | 92 | for (int i = 0; i < omp_n_threads; ++i) { 93 | size_t seed_val = generate_seed_value(i, omp_n_threads, rand_engine); 94 | rand_engines_vec.push_back(rand_engine_t(seed_val)); 95 | } 96 | 97 | // lambda function for box constraints 98 | 99 | std::function box_log_kernel \ 100 | = [target_log_kernel, vals_bound, bounds_type, lower_bounds, upper_bounds] (const ColVec_t& vals_inp, void* target_data) \ 101 | -> fp_t 102 | { 103 | if (vals_bound) { 104 | ColVec_t vals_inv_trans = inv_transform(vals_inp, bounds_type, lower_bounds, upper_bounds); 105 | 106 | return target_log_kernel(vals_inv_trans, target_data) + log_jacobian(vals_inp, bounds_type, lower_bounds, upper_bounds); 107 | } else { 108 | return target_log_kernel(vals_inp, target_data); 109 | } 110 | }; 111 | 112 | // setup 113 | 114 | ColVec_t rand_vec(n_vals); 115 | ColVec_t target_vals(n_pop); 116 | Mat_t X(n_pop, n_vals); 117 | 118 | #ifdef MCMC_USE_OPENMP 119 | #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec) 120 | #endif 121 | for (size_t i = 0; i < n_pop; ++i) { 122 | size_t thread_num = 0; 123 | 124 | #ifdef MCMC_USE_OPENMP 125 | thread_num = omp_get_thread_num(); 126 | #endif 127 | 128 | bmo::stats::internal::runif_vec_inplace(n_vals, rand_engines_vec[thread_num], rand_vec); 129 | 130 | X.row(i) = BMO_MATOPS_TRANSPOSE( par_initial_lb + BMO_MATOPS_HADAMARD_PROD( (par_initial_ub - par_initial_lb), rand_vec ) ); 131 | 132 | fp_t prop_kernel_val = box_log_kernel(BMO_MATOPS_TRANSPOSE(X.row(i)), target_data); 133 | 134 | if (!std::isfinite(prop_kernel_val)) { 135 | prop_kernel_val = neginf; 136 | } 137 | 138 | target_vals(i) = prop_kernel_val; 139 | } 140 | 141 | // begin MCMC run 142 | 143 | size_t n_accept = 0; 144 | 145 | fp_t par_gamma_run = par_gamma; 146 | draws_out.setZero(n_pop, n_vals, n_keep_draws); 147 | 148 | for (size_t draw_ind = 0; draw_ind < n_total_draws; draw_ind++) { 149 | fp_t temperature_j = de_cooling_schedule(draw_ind,n_keep_draws); 150 | 151 | if (jumps && ((draw_ind+1) % 10 == 0)) { 152 | par_gamma_run = par_gamma_jump; 153 | } 154 | 155 | // 156 | 157 | ColVecUInt_t n_accept_vec(omp_n_threads); 158 | BMO_MATOPS_SET_ZERO(n_accept_vec); 159 | 160 | #ifdef MCMC_USE_OPENMP 161 | #pragma omp parallel for num_threads(omp_n_threads) firstprivate(rand_vec) 162 | #endif 163 | for (size_t i = 0; i < n_pop; ++i) { 164 | size_t thread_num = 0; 165 | 166 | #ifdef MCMC_USE_OPENMP 167 | thread_num = omp_get_thread_num(); 168 | #endif 169 | uint_t c_1, c_2; 170 | 171 | do { 172 | c_1 = bmo::stats::rind(0, n_pop - 1, rand_engines_vec[thread_num]); 173 | } while (c_1 == i); 174 | 175 | do { 176 | c_2 = bmo::stats::rind(0, n_pop - 1, rand_engines_vec[thread_num]); 177 | } while (c_2 == i || c_2 == c_1); 178 | 179 | // 180 | 181 | // arma::rowvec prop_rand = arma::randu(1,n_vals)*(2*par_b) - par_b; // generate a vector of U[-b,b] RVs 182 | bmo::stats::internal::runif_vec_inplace(n_vals, -par_b, par_b, rand_engines_vec[thread_num], rand_vec); 183 | 184 | RowVec_t X_prop = X.row(i) + BMO_MATOPS_ARRAY_MULT_SCALAR(X.row(c_1) - X.row(c_2), par_gamma_run) + BMO_MATOPS_TRANSPOSE(rand_vec); 185 | 186 | fp_t prop_kernel_val = box_log_kernel(BMO_MATOPS_TRANSPOSE(X_prop),target_data); 187 | 188 | if (!std::isfinite(prop_kernel_val)) { 189 | prop_kernel_val = neginf; 190 | } 191 | 192 | // 193 | 194 | fp_t comp_val = prop_kernel_val - target_vals(i); 195 | fp_t z = bmo::stats::runif(rand_engines_vec[thread_num]); 196 | 197 | if (comp_val > temperature_j * std::log(z)) { 198 | X.row(i) = X_prop; 199 | 200 | target_vals(i) = prop_kernel_val; 201 | 202 | if (draw_ind >= n_burnin_draws) { 203 | // n_accept++; // this would lead to a race condition if parallel samlping were used 204 | n_accept_vec(thread_num) += 1; 205 | } 206 | } 207 | } 208 | 209 | // 210 | 211 | n_accept += BMO_MATOPS_SUM(n_accept_vec); 212 | 213 | // 214 | 215 | if (draw_ind >= n_burnin_draws) { 216 | draws_out.mat(draw_ind - n_burnin_draws) = X; 217 | } 218 | 219 | if (jumps && ((draw_ind + 1) % 10 == 0)) { 220 | par_gamma_run = par_gamma; 221 | } 222 | } 223 | 224 | success = true; 225 | 226 | // 227 | 228 | if (vals_bound) { 229 | #ifdef MCMC_USE_OPENMP 230 | #pragma omp parallel for num_threads(omp_n_threads) 231 | #endif 232 | for (size_t draw_ind = 0; draw_ind < n_keep_draws; draw_ind++) { 233 | for (size_t jj = 0; jj < n_pop; jj++) { 234 | draws_out.mat(draw_ind).row(jj) = inv_transform(draws_out.mat(draw_ind).row(jj), bounds_type, lower_bounds, upper_bounds); 235 | } 236 | } 237 | } 238 | 239 | if (settings_inp) { 240 | settings_inp->de_settings.n_accept_draws = n_accept; 241 | } 242 | 243 | // 244 | 245 | return success; 246 | } 247 | 248 | // wrappers 249 | 250 | mcmclib_inline 251 | bool 252 | mcmc::de( 253 | const ColVec_t& initial_vals, 254 | std::function target_log_kernel, 255 | Cube_t& draws_out, 256 | void* target_data 257 | ) 258 | { 259 | return internal::de_impl(initial_vals,target_log_kernel,draws_out,target_data,nullptr); 260 | } 261 | 262 | mcmclib_inline 263 | bool 264 | mcmc::de( 265 | const ColVec_t& initial_vals, 266 | std::function target_log_kernel, 267 | Cube_t& draws_out, 268 | void* target_data, 269 | algo_settings_t& settings 270 | ) 271 | { 272 | return internal::de_impl(initial_vals,target_log_kernel,draws_out,target_data,&settings); 273 | } 274 | --------------------------------------------------------------------------------