Estimates effective sample size.
128 | Note 1: Lag is trunctaed at when autocorrelation < 0.05,
129 | if -ve autocorrelation occurs it is possible to get ess > N.
130 | Note 2: ESS estimates will be too optimistic for chains that haven’t mixed.
3 |
4 | [](https://github.com/mrc-ide/drjacoby/actions)
5 | [](https://github.com/mrc-ide/drjacoby/actions)
6 | [](https://codecov.io/github/mrc-ide/drjacoby?branch=master)
7 |
8 |
9 |
10 | *drjacoby* is a package for running flexible Markov chain Monte Carlo (MCMC) with minimal fiddling required by the user. The likelihood and the priors that go into the model can be written as either R or C++ functions, with the latter typically being much faster to run. Outputs are produced in a standardised format, and can be explored using a range of built in diagnostic plots and statistics.
11 |
12 |
13 |
*drjacoby* uses parallel tempering to "melt" the target distribution, thereby allowing information to pass between well-separated peaks.
14 |
15 | There are many MCMC programs out there, including more far-reaching programs such as WinBUGS, JAGS, greta, STAN, and many more. These programs contain a wide variety of options for specifying complex models, and often run different flavours of MCMC such as Hamiltonian Monte Carlo (HMC). In contrast, *drjacoby* is tailored to a specific type of MCMC problem: those that **mix poorly due to highly correlated and/or multi-modal posteriors**, which can occur in both simple and complex models. If the posterior is particularly peaky then even methods like HMC may fail, meaning (unfortunately) no amount of STAN iterations will get us to the right answer. There are techniques available for dealing with this sort of problem, but they tend to be fairly advanced and tricky to implement. The aim of *drjacoby* is to bring these methods within reach of a non-specialist MCMC user, so that reliable results can be obtained without the need to code a custom MCMC program from scratch.
16 |
17 | After [installing](https://mrc-ide.github.io/drjacoby/articles/installation.html) *drjacoby*, please take at look at the first [example application](https://mrc-ide.github.io/drjacoby/articles/example.html) to get up and running.
18 |
--------------------------------------------------------------------------------
/inst/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/inst/.DS_Store
--------------------------------------------------------------------------------
/inst/extdata/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/inst/extdata/.DS_Store
--------------------------------------------------------------------------------
/inst/extdata/blocks_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 |
7 | // unpack parameters
8 | int pi = 0;
9 | std::vector mu(6);
10 | for (int i = 0; i < 6; ++i) {
11 | mu[i] = params[pi++];
12 | }
13 | double sigma = params["sigma"];
14 | double phi = params["phi"];
15 | double tau = params["tau"];
16 |
17 | // get current update block
18 | int block = misc["block"];
19 |
20 | // distinct method for first 6 blocks, vs 7th
21 | double ret = 0.0;
22 | if (block == 7) { // likelihood for the global parameters
23 |
24 | // calculate likelihood component
25 | for (int i = 0; i < 6; ++i) {
26 | ret += R::dnorm(mu[i], phi, tau, true);
27 | }
28 |
29 | } else { // likelihood for each of the 6 data groups
30 |
31 | // get data for this group
32 | NumericVector x = data[block - 1];
33 |
34 | // calculate likelihood component
35 | for (int i = 0; i < x.size(); ++i) {
36 | ret += R::dnorm(x[i], mu[block - 1], sigma, true);
37 | }
38 |
39 | }
40 |
41 | return Rcpp::wrap(ret);
42 | }
43 |
44 | // [[Rcpp::export]]
45 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
46 |
47 | // unpack parameters
48 | double sigma = params["sigma"];
49 | double phi = params["phi"];
50 | double tau = params["tau"];
51 |
52 | // apply priors
53 | double ret = R::dgamma(sigma, 0.01, 100.0, true) +
54 | R::dnorm(phi, 0, 1000, true) +
55 | R::dgamma(tau, 0.01, 100.0, true);
56 |
57 | return Rcpp::wrap(ret);
58 | }
59 |
60 | // [[Rcpp::export]]
61 | SEXP create_xptr(std::string function_name) {
62 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
63 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
64 |
65 | if (function_name == "loglike"){
66 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
67 | }
68 | if (function_name == "logprior"){
69 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
70 | }
71 |
72 | stop("cpp function %i not found", function_name);
73 | }
74 |
--------------------------------------------------------------------------------
/inst/extdata/checks/doublewell_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 | double mu = params["mu"];
7 | double gamma = params["gamma"];
8 | double ret = -gamma*(mu*mu - 1.0)*(mu*mu - 1.0);
9 |
10 | return Rcpp::wrap(ret);
11 | }
12 |
13 | // [[Rcpp::export]]
14 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
15 | return Rcpp::wrap(0.0);
16 | }
17 |
18 | // [[Rcpp::export]]
19 | SEXP create_xptr(std::string function_name) {
20 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
21 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
22 |
23 | if (function_name == "loglike"){
24 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
25 | }
26 | if (function_name == "logprior"){
27 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
28 | }
29 |
30 | stop("cpp function %i not found", function_name);
31 | }
32 |
--------------------------------------------------------------------------------
/inst/extdata/checks/multilevel_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 |
7 | int pi = 0;
8 | std::vector mu(5);
9 | for (int i = 0; i < 5; ++i) {
10 | mu[i] = params[pi++];
11 | }
12 |
13 | int block = misc["block"];
14 |
15 | double ret = 0.0;
16 | if (block == 6) { // likelihood for the global parameters
17 | for (int i = 0; i < 5; ++i) {
18 | ret += R::dnorm(mu[i], 0, 1.0, true);
19 | }
20 | } else { // likelihood for each of the 6 data groups
21 | NumericVector x = data[block - 1];
22 | for (int i = 0; i < x.size(); ++i) {
23 | ret += R::dnorm(x[i], mu[block - 1], 1.0, true);
24 | }
25 | }
26 |
27 | return Rcpp::wrap(ret);
28 | }
29 |
30 | // [[Rcpp::export]]
31 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
32 | return Rcpp::wrap(0.0);
33 | }
34 |
35 | // [[Rcpp::export]]
36 | SEXP create_xptr(std::string function_name) {
37 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
38 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
39 |
40 | if (function_name == "loglike"){
41 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
42 | }
43 | if (function_name == "logprior"){
44 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
45 | }
46 |
47 | stop("cpp function %i not found", function_name);
48 | }
49 |
--------------------------------------------------------------------------------
/inst/extdata/checks/normal_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 |
7 | std::vector x = Rcpp::as< std::vector >(data["x"]);
8 |
9 | double ret = 0.0;
10 | for (unsigned int i = 0; i < x.size(); ++i) {
11 | ret += R::dnorm(x[i], params["mu"], 1.0, true);
12 | }
13 |
14 | return Rcpp::wrap(ret);
15 | }
16 |
17 | // [[Rcpp::export]]
18 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
19 | return Rcpp::wrap(0.0);
20 | }
21 |
22 | // [[Rcpp::export]]
23 | SEXP create_xptr(std::string function_name) {
24 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
25 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
26 |
27 | if (function_name == "loglike"){
28 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
29 | }
30 |
31 | if (function_name == "logprior"){
32 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
33 | }
34 |
35 | stop("cpp function %i not found", function_name);
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------
/inst/extdata/checks/returnprior_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 | return Rcpp::wrap(0.0);
7 | }
8 |
9 | // [[Rcpp::export]]
10 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
11 |
12 | double ret = R::dnorm(params["real_line"], 0.0, 1.0, true) +
13 | R::dgamma(-params["neg_line"], 5.0, 5.0, true) +
14 | R::dgamma(params["pos_line"], 5.0, 5.0, true) +
15 | R::dbeta(params["unit_interval"], 3.0, 3.0, true);
16 |
17 | return Rcpp::wrap(ret);
18 | }
19 |
20 | // [[Rcpp::export]]
21 | SEXP create_xptr(std::string function_name) {
22 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
23 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
24 |
25 | if (function_name == "loglike"){
26 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
27 | }
28 | if (function_name == "logprior"){
29 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
30 | }
31 |
32 | stop("cpp function %i not found", function_name);
33 | }
34 |
--------------------------------------------------------------------------------
/inst/extdata/coupling_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 |
7 | // unpack data
8 | std::vector x = Rcpp::as< std::vector >(data["x"]);
9 |
10 | // unpack parameters
11 | double alpha = params["alpha"];
12 | double beta = params["beta"];
13 | double epsilon = params["epsilon"];
14 |
15 | // sum log-likelihood over all data
16 | double mean = alpha*alpha*beta + epsilon;
17 | double ret = 0.0;
18 | for (unsigned int i = 0; i < x.size(); ++i) {
19 | ret += R::dnorm(x[i], mean, 1.0, true);
20 | }
21 |
22 | // return as SEXP
23 | return Rcpp::wrap(ret);
24 | }
25 |
26 | // [[Rcpp::export]]
27 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
28 |
29 | // unpack parameters
30 | double epsilon = params["epsilon"];
31 |
32 | // calculate logprior
33 | double ret = -log(20.0) - log(10.0) + R::dnorm(epsilon, 0.0, 1.0, true);
34 |
35 | // return as SEXP
36 | return Rcpp::wrap(ret);
37 | }
38 |
39 | // [[Rcpp::export]]
40 | SEXP create_xptr(std::string function_name) {
41 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
42 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
43 |
44 | if (function_name == "loglike"){
45 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
46 | }
47 | if (function_name == "logprior"){
48 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
49 | }
50 |
51 | stop("cpp function %i not found", function_name);
52 | }
53 |
--------------------------------------------------------------------------------
/inst/extdata/example_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 |
7 | // extract parameters
8 | double mu = params["mu"];
9 | double sigma = params["sigma"];
10 |
11 | // unpack data
12 | std::vector x = Rcpp::as< std::vector >(data["x"]);
13 |
14 | // sum log-likelihood over all data
15 | double ret = 0.0;
16 | for (unsigned int i = 0; i < x.size(); ++i) {
17 | ret += R::dnorm(x[i], mu, sigma, true);
18 | }
19 |
20 | // return as SEXP
21 | return Rcpp::wrap(ret);
22 | }
23 |
24 | // [[Rcpp::export]]
25 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
26 |
27 | // extract parameters
28 | double sigma = params["sigma"];
29 |
30 | // calculate logprior
31 | double ret = -log(20.0) + R::dlnorm(sigma, 0.0, 1.0, true);
32 |
33 | // return as SEXP
34 | return Rcpp::wrap(ret);
35 | }
36 |
37 | // [[Rcpp::export]]
38 | SEXP create_xptr(std::string function_name) {
39 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
40 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
41 |
42 | if (function_name == "loglike"){
43 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
44 | }
45 | if (function_name == "logprior"){
46 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
47 | }
48 |
49 | stop("cpp function %i not found", function_name);
50 | }
51 |
--------------------------------------------------------------------------------
/inst/extdata/getting_model_fits_data.rds:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/inst/extdata/getting_model_fits_data.rds
--------------------------------------------------------------------------------
/inst/templates/cpp_template.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // [[Rcpp::export]]
5 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
6 | // Insert loglikelihood code here:
7 |
8 | double loglikelihood = 0.0;
9 | return Rcpp::wrap(loglikelihood);
10 | }
11 |
12 | // [[Rcpp::export]]
13 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
14 | // Insert loglikelihood code here:
15 |
16 | double logprior = 0.0;
17 | return Rcpp::wrap(logprior);
18 | }
19 |
20 |
21 | // NOTE: Do not edit this function name
22 | // [[Rcpp::export]]
23 | SEXP create_xptr(std::string function_name) {
24 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
25 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
26 |
27 | // NOTE: If your loglikelihood function is not called "loglike" please edit:
28 | if (function_name == "loglike"){
29 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
30 | }
31 | // NOTE: If your logprior function is not called "logprior" please edit:
32 | if (function_name == "logprior"){
33 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
34 | }
35 |
36 | stop("cpp function %i not found", function_name);
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/man/acf_data.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/diagnostic.R
3 | \name{acf_data}
4 | \alias{acf_data}
5 | \title{Estimate autocorrelation}
6 | \usage{
7 | acf_data(x, lag)
8 | }
9 | \arguments{
10 | \item{x}{Single chain.}
11 |
12 | \item{lag}{maximum lag. Must be an integer between 1 and 500.}
13 | }
14 | \description{
15 | Estimate autocorrelation
16 | }
17 |
--------------------------------------------------------------------------------
/man/check_drjacoby_loaded.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/main.R
3 | \name{check_drjacoby_loaded}
4 | \alias{check_drjacoby_loaded}
5 | \title{Check that drjacoby package has loaded successfully}
6 | \usage{
7 | check_drjacoby_loaded()
8 | }
9 | \description{
10 | Simple function to check that drjacoby package has loaded
11 | successfully. Prints "drjacoby loaded successfully!" if so.
12 | }
13 |
--------------------------------------------------------------------------------
/man/cpp_template.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/cpp_template.R
3 | \name{cpp_template}
4 | \alias{cpp_template}
5 | \title{Create template for cpp}
6 | \usage{
7 | cpp_template(save_as)
8 | }
9 | \arguments{
10 | \item{save_as}{Path of (.cpp) file to create, relative to root of active project.}
11 | }
12 | \description{
13 | Create template for cpp
14 | }
15 |
--------------------------------------------------------------------------------
/man/define_params.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/main.R
3 | \name{define_params}
4 | \alias{define_params}
5 | \title{Define parameters dataframe}
6 | \usage{
7 | define_params(...)
8 | }
9 | \arguments{
10 | \item{...}{a series of named input arguments.}
11 | }
12 | \description{
13 | Provides a convenient way of defining parameters in the format
14 | required by \code{run_mcmc()}. Each parameter must have the following three
15 | elements, defined in order:
16 | \itemize{
17 | \item \code{name} - the parameter name.
18 | \item \code{min} - the minimum value of the parameter. \code{-Inf} is
19 | allowed.
20 | \item \code{max} - the maximum value of the parameter. \code{Inf} is
21 | allowed.
22 | }
23 | There following arguments are also optional:
24 | \itemize{
25 | \item \code{init} - the initial value of the parameter. If running
26 | multiple chains a vector of initial values can be used to specify distinct
27 | values for each chain.
28 | \item \code{block} - which likelihood block(s) this parameter belongs to.
29 | See vignettes for instructions on using likelihood blocks.
30 | }
31 | }
32 | \examples{
33 | define_params(name = "mu", min = -10, max = 10, init = 0,
34 | name = "sigma", min = 0, max = 5, init = c(1, 2))
35 |
36 | define_params(name = "mu1", min = -10, max = 10, init = 0, block = 1,
37 | name = "mu2", min = -10, max = 10, init = 0, block = 2,
38 | name = "sigma", min = 0, max = 5, init = 1, block = c(1, 2))
39 | }
40 |
--------------------------------------------------------------------------------
/man/drjacoby.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/drjacoby.R
3 | \name{drjacoby}
4 | \alias{drjacoby}
5 | \title{Flexible Markov Chain Monte Carlo via Reparameterization}
6 | \description{
7 | Flexible Markov chain monte carlo via reparameterization using
8 | the Jacobean matrix.
9 |
10 | _PACKAGE
11 | }
12 |
--------------------------------------------------------------------------------
/man/gelman_rubin.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/diagnostic.R
3 | \name{gelman_rubin}
4 | \alias{gelman_rubin}
5 | \title{Gelman-Rubin statistic}
6 | \usage{
7 | gelman_rubin(par_matrix, chains, samples)
8 | }
9 | \arguments{
10 | \item{par_matrix}{Matrix (interations x chains)}
11 |
12 | \item{chains}{number of chains}
13 |
14 | \item{samples}{number of samples}
15 | }
16 | \value{
17 | Gelman-Rubin statistic
18 | }
19 | \description{
20 | Estimate sthe Gelman-Rubin (rhat) convergence statistic for a single parameter
21 | across multiple chains. Basic method, assuming all chains are of equal length
22 | }
23 | \references{
24 | Gelman, A., and D. B. Rubin. 1992.
25 | Inference from Iterative Simulation Using Multiple Sequences.
26 | Statistical Science 7: 457–511.
27 |
28 | \url{https://astrostatistics.psu.edu/RLectures/diagnosticsMCMC.pdf}
29 | }
30 |
--------------------------------------------------------------------------------
/man/plot_autocorrelation.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_autocorrelation}
4 | \alias{plot_autocorrelation}
5 | \title{Plot autocorrelation}
6 | \usage{
7 | plot_autocorrelation(x, lag = 20, par = NULL, chain = 1, phase = "sampling")
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{lag}{maximum lag. Must be an integer between 1 and 500.}
13 |
14 | \item{par}{vector of parameter names. If \code{NULL} all parameters are
15 | plotted.}
16 |
17 | \item{chain}{which chain to plot.}
18 |
19 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
20 | }
21 | \description{
22 | Plot autocorrelation for specified parameters
23 | }
24 |
--------------------------------------------------------------------------------
/man/plot_cor_mat.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_cor_mat}
4 | \alias{plot_cor_mat}
5 | \title{Plot posterior correlation matrix}
6 | \usage{
7 | plot_cor_mat(x, show = NULL, phase = "sampling", param_names = NULL)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{show}{Vector of parameter names to plot.}
13 |
14 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
15 |
16 | \item{param_names}{Optional vector of names to replace the default parameter names.}
17 | }
18 | \description{
19 | Produces a matrix showing the correlation between all parameters
20 | from posterior draws.
21 | }
22 |
--------------------------------------------------------------------------------
/man/plot_credible.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_credible}
4 | \alias{plot_credible}
5 | \title{Plot 95\% credible intervals}
6 | \usage{
7 | plot_credible(x, show = NULL, phase = "sampling", param_names = NULL)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{show}{vector of parameter names to plot.}
13 |
14 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
15 |
16 | \item{param_names}{optional vector of names to replace the default parameter names.}
17 | }
18 | \description{
19 | Plots posterior 95\% credible intervals over specified set of
20 | parameters (defauls to all parameters).
21 | }
22 |
--------------------------------------------------------------------------------
/man/plot_density.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_density}
4 | \alias{plot_density}
5 | \title{Produce density plots}
6 | \usage{
7 | plot_density(x, show = NULL, hide = NULL)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{show}{optional vector of parameter names to plot. Parameters matching
13 | show will be included.}
14 |
15 | \item{hide}{optional vector of parameter names to filter out. Parameters
16 | matching hide will be hidden.}
17 | }
18 | \description{
19 | Density plots of all parameters. Use \code{show} and \code{hide}
20 | to be more specific about which parameters to plot.
21 | }
22 |
--------------------------------------------------------------------------------
/man/plot_mc_acceptance.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_mc_acceptance}
4 | \alias{plot_mc_acceptance}
5 | \title{Plot Metropolis coupling acceptance rates}
6 | \usage{
7 | plot_mc_acceptance(x, chain = NULL, phase = "sampling", x_axis_type = 1)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{chain}{which chain to plot. If \code{NULL} then plot all chains.}
13 |
14 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
15 |
16 | \item{x_axis_type}{how to format the x-axis. 1 = integer rungs, 2 = values of
17 | the thermodynamic power.}
18 | }
19 | \description{
20 | Plot Metropolis coupling acceptance rates between all rungs.
21 | }
22 |
--------------------------------------------------------------------------------
/man/plot_pairs.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_pairs}
4 | \alias{plot_pairs}
5 | \title{Produce scatterplots between multiple parameters}
6 | \usage{
7 | plot_pairs(x, show = NULL, hide = NULL)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}}
11 |
12 | \item{show}{optional vector of parameter names to plot. Parameters matching
13 | show will be included.}
14 |
15 | \item{hide}{optional vector of parameter names to filter out. Parameters
16 | matching hide will be hidden.}
17 | }
18 | \description{
19 | Uses \code{ggpairs} function from the \code{GGally} package to
20 | produce scatterplots between all named parameters.
21 | }
22 |
--------------------------------------------------------------------------------
/man/plot_rung_loglike.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_rung_loglike}
4 | \alias{plot_rung_loglike}
5 | \title{Plot loglikelihood 95\% credible intervals}
6 | \usage{
7 | plot_rung_loglike(
8 | x,
9 | chain = 1,
10 | phase = "sampling",
11 | x_axis_type = 1,
12 | y_axis_type = 1
13 | )
14 | }
15 | \arguments{
16 | \item{x}{an object of class \code{drjacoby_output}}
17 |
18 | \item{chain}{which chain to plot.}
19 |
20 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
21 |
22 | \item{x_axis_type}{how to format the x-axis. 1 = integer rungs, 2 = values of
23 | the thermodynamic power.}
24 |
25 | \item{y_axis_type}{how to format the y-axis. 1 = raw values, 2 = truncated at
26 | auto-chosen lower limit. 3 = double-log scale.}
27 | }
28 | \description{
29 | Plot loglikelihood 95\% credible intervals.
30 | }
31 |
--------------------------------------------------------------------------------
/man/plot_scatter.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_scatter}
4 | \alias{plot_scatter}
5 | \title{Produce bivariate scatterplot}
6 | \usage{
7 | plot_scatter(
8 | x,
9 | parameter1,
10 | parameter2,
11 | downsample = TRUE,
12 | phase = "sampling",
13 | chain = NULL
14 | )
15 | }
16 | \arguments{
17 | \item{x}{an object of class \code{drjacoby_output}}
18 |
19 | \item{parameter1}{name of parameter first parameter.}
20 |
21 | \item{parameter2}{name of parameter second parameter.}
22 |
23 | \item{downsample}{whether to downsample output to 200 values max to speed up
24 | plotting.}
25 |
26 | \item{phase}{which phase to plot. Must be either "burnin" or "sampling".}
27 |
28 | \item{chain}{which chain to plot.}
29 | }
30 | \description{
31 | Produces scatterplot between two named parameters.
32 | }
33 |
--------------------------------------------------------------------------------
/man/plot_trace.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot.R
3 | \name{plot_trace}
4 | \alias{plot_trace}
5 | \title{Plot parameter trace}
6 | \usage{
7 | plot_trace(
8 | x,
9 | show = NULL,
10 | hide = NULL,
11 | lag = 20,
12 | downsample = TRUE,
13 | phase = "sampling",
14 | chain = NULL,
15 | display = TRUE
16 | )
17 | }
18 | \arguments{
19 | \item{x}{an object of class \code{drjacoby_output}}
20 |
21 | \item{show}{optional vector of parameter names to plot. Parameters matching
22 | show will be included.}
23 |
24 | \item{hide}{optional vector of parameter names to filter out. Parameters
25 | matching hide will be hidden.}
26 |
27 | \item{lag}{maximum lag. Must be an integer between 1 and 500.}
28 |
29 | \item{downsample}{boolean. Whether to downsample chain to make plotting more
30 | efficient.}
31 |
32 | \item{phase}{which phase to plot. Must be either "burnin", "sampling" or "both".}
33 |
34 | \item{chain}{which chain to plot.}
35 |
36 | \item{display}{boolean. Whether to show plots, if \code{FALSE} then plotting
37 | objects are returned without displaying.}
38 | }
39 | \description{
40 | Produce a series of plots corresponding to each parameter,
41 | including the raw trace, the posterior histogram and an autocorrelation
42 | plot. Plotting objects can be cycled through interactively, or can be
43 | returned as an object allowing them to be viewed/edited by the user.
44 | }
45 |
--------------------------------------------------------------------------------
/man/population_data.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{population_data}
5 | \alias{population_data}
6 | \title{Population data.}
7 | \format{
8 | A data frame with 20 rows and 2 variables:
9 | \describe{
10 | \item{pop}{population size}
11 | \item{time}{time}
12 | ...
13 | }
14 | }
15 | \usage{
16 | population_data
17 | }
18 | \description{
19 | Example population growth data.
20 | }
21 | \keyword{datasets}
22 |
--------------------------------------------------------------------------------
/man/run_mcmc.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/main.R
3 | \name{run_mcmc}
4 | \alias{run_mcmc}
5 | \title{Run drjacoby MCMC}
6 | \usage{
7 | run_mcmc(
8 | data,
9 | df_params,
10 | misc = list(),
11 | loglike,
12 | logprior,
13 | burnin = 1000,
14 | samples = 10000,
15 | rungs = 1,
16 | chains = 5,
17 | beta_manual = NULL,
18 | alpha = 1,
19 | target_acceptance = 0.44,
20 | cluster = NULL,
21 | coupling_on = TRUE,
22 | pb_markdown = FALSE,
23 | save_data = TRUE,
24 | save_hot_draws = FALSE,
25 | silent = FALSE
26 | )
27 | }
28 | \arguments{
29 | \item{data}{a named list or data frame or data values.}
30 |
31 | \item{df_params}{a data.frame of parameters (see \code{?define_params}).}
32 |
33 | \item{misc}{optional list object passed to likelihood and prior. This can be
34 | useful for passing values that are not strictly data, for example passing a
35 | lookup table to the prior function.}
36 |
37 | \item{loglike, logprior}{the log-likelihood and log-prior functions used in
38 | the MCMC. Can either be passed in as R functions (not in quotes), or as
39 | character strings naming compiled C++ functions (in quotes).}
40 |
41 | \item{burnin}{the number of burn-in iterations. Automatic tuning of proposal
42 | standard deviations is only active during the burn-in period.}
43 |
44 | \item{samples}{the number of sampling iterations.}
45 |
46 | \item{rungs}{the number of temperature rungs used in the parallel tempering
47 | method. By default, \eqn{\beta} values are equally spaced between 0 and 1,
48 | i.e. \eqn{\beta[i]=}\code{(i-1)/(rungs-1)} for \code{i} in \code{1:rungs}.
49 | The likelihood for the \out{ith} heated chain is raised to the
50 | power \eqn{\beta[i]^\alpha}, meaning we can use the \eqn{\alpha} parameter
51 | to concentrate rungs towards the start or the end of the interval (see the
52 | \code{alpha} argument).}
53 |
54 | \item{chains}{the number of independent replicates of the MCMC to run. If a
55 | \code{cluster} object is defined then these chains are run in parallel,
56 | otherwise they are run in serial.}
57 |
58 | \item{beta_manual}{vector of manually defined \eqn{\beta} values used in the
59 | parallel tempering approach. If defined, this overrides the spacing defined
60 | by \code{rungs}. Note that even manually defined \eqn{\beta} values are
61 | raised to the power \eqn{\alpha} internally, hence you should set
62 | \code{alpha = 1} if you want to fix \eqn{\beta} values exactly.}
63 |
64 | \item{alpha}{the likelihood for the \out{ith} heated chain is
65 | raised to the power \eqn{\beta[i]^\alpha}, meaning we can use the
66 | \eqn{\alpha} parameter to concentrate rungs towards the start or the end of
67 | the temperature scale.}
68 |
69 | \item{target_acceptance}{Target acceptance rate. Should be between 0 and 1.
70 | Default of 0.44, set as optimum for unvariate proposal distributions.}
71 |
72 | \item{cluster}{option to pass in a cluster environment, allowing chains to be
73 | run in parallel (see package "parallel").}
74 |
75 | \item{coupling_on}{whether to implement Metropolis-coupling over temperature
76 | rungs. The option of deactivating coupling has been retained for general
77 | interest and debugging purposes only. If this parameter is \code{FALSE}
78 | then parallel tempering will have no impact on MCMC mixing.}
79 |
80 | \item{pb_markdown}{whether to run progress bars in markdown mode, meaning
81 | they are only updated when they reach 100\% to avoid large amounts of output
82 | being printed to markdown files.}
83 |
84 | \item{save_data}{if \code{TRUE} (the default) the raw input data is stored
85 | for reference in the project output. This allows complete reproducibility
86 | from a project, but may be undesirable when datasets are very large.}
87 |
88 | \item{save_hot_draws}{if \code{TRUE} the parameter draws relating to the hot
89 | chains are also stored inside the \code{pt} element of the project output.
90 | If \code{FALSE} (the default) only log-likelihoods and log-priors are
91 | stored from heated chains.}
92 |
93 | \item{silent}{whether to suppress all console output.}
94 | }
95 | \description{
96 | Run MCMC either with or without parallel tempering turned on.
97 | Minimum inputs include a data object, a data.frame of parameters, a
98 | log-likelihood function and a log-prior function. Produces an object of
99 | class \code{drjacoby_output}, which contains all MCMC output along with
100 | some diagnostics and a record of inputs.
101 | }
102 | \details{
103 | Note that both \code{data} and \code{misc} are passed into
104 | log-likelihood/log-prior functions *by reference*. This means if you modify
105 | these objects inside the functions then any changes will persist.
106 | }
107 |
--------------------------------------------------------------------------------
/man/sample_chains.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/samples.R
3 | \name{sample_chains}
4 | \alias{sample_chains}
5 | \title{Sample posterior draws from all available chains}
6 | \usage{
7 | sample_chains(x, sample_n, keep_chain_index = FALSE)
8 | }
9 | \arguments{
10 | \item{x}{an object of class \code{drjacoby_output}.}
11 |
12 | \item{sample_n}{An integer number of samples.}
13 |
14 | \item{keep_chain_index}{if \code{TRUE} then the column giving the chain is
15 | retained.}
16 | }
17 | \value{
18 | A data.frame of posterior samples
19 | }
20 | \description{
21 | Sample posterior draws from all available chains
22 | }
23 |
--------------------------------------------------------------------------------
/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/src/.DS_Store
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | *.dll
4 |
--------------------------------------------------------------------------------
/src/Particle.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "Particle.h"
3 |
4 | using namespace std;
5 |
6 | //------------------------------------------------
7 | // initialise/reset particle
8 | void Particle::init(System &s, double beta_raised) {
9 |
10 | // pointer to system object
11 | this->s_ptr = &s;
12 |
13 | // local copies of some parameters for convenience
14 | d = s_ptr->d;
15 |
16 | // thermodynamic power
17 | this->beta_raised = beta_raised;
18 |
19 | // theta is the parameter vector in natural space
20 | theta = Rcpp::clone(s_ptr->theta_vector);
21 | theta_prop = Rcpp::clone(theta);
22 |
23 | // phi is a vector of transformed parameters
24 | phi = Rcpp::NumericVector(d);
25 | theta_to_phi();
26 | phi_prop = Rcpp::NumericVector(d);
27 |
28 | // proposal parameters
29 | bw = vector(d, 1.0);
30 | bw_index = vector(d, 1);
31 | bw_stepsize = 1.0;
32 |
33 | // likelihoods and priors
34 | loglike_block = vector(s_ptr->n_block);
35 | loglike_prop_block = vector(s_ptr->n_block);
36 | loglike = 0;
37 | loglike_prop = 0;
38 | logprior = 0;
39 | logprior_prop = 0;
40 |
41 | // acceptance rates
42 | accept_count = 0;
43 |
44 | }
45 |
46 | //------------------------------------------------
47 | // propose new value of phi[i] from univariate normal distribution
48 | void Particle::propose_phi(int i) {
49 | phi_prop[i] = R::rnorm(phi[i], bw[i]);
50 | }
51 |
52 | //------------------------------------------------
53 | // transform phi_prop to theta_prop. See main.R for a key to transformation
54 | // types
55 | void Particle::phi_prop_to_theta_prop(int i) {
56 |
57 | switch(s_ptr->trans_type[i]) {
58 | case 0:
59 | theta_prop[i] = phi_prop[i];
60 | break;
61 | case 1:
62 | theta_prop[i] = s_ptr->theta_max[i] - exp(phi_prop[i]);
63 | break;
64 | case 2:
65 | theta_prop[i] = exp(phi_prop[i]) + s_ptr->theta_min[i];
66 | break;
67 | case 3:
68 | theta_prop[i] = (s_ptr->theta_max[i]*exp(phi_prop[i]) + s_ptr->theta_min[i]) / (1 + exp(phi_prop[i]));
69 | break;
70 | default:
71 | Rcpp::stop("trans_type invalid");
72 | }
73 |
74 | }
75 |
76 | //------------------------------------------------
77 | // transform theta to phi. See main.R for a key to transformation types
78 | void Particle::theta_to_phi() {
79 |
80 | for (int i = 0; i < d; ++i) {
81 | switch(s_ptr->trans_type[i]) {
82 | case 0:
83 | phi[i] = theta[i];
84 | break;
85 | case 1:
86 | phi[i] = log(s_ptr->theta_max[i] - theta[i]);
87 | break;
88 | case 2:
89 | phi[i] = log(theta[i] - s_ptr->theta_min[i]);
90 | break;
91 | case 3:
92 | phi[i] = log(theta[i] - s_ptr->theta_min[i]) - log(s_ptr->theta_max[i] - theta[i]);
93 | break;
94 | default:
95 | Rcpp::stop("trans_type invalid");
96 | }
97 | }
98 |
99 | }
100 |
101 | //------------------------------------------------
102 | // get adjustment factor to account for reparameterisation
103 | double Particle::get_adjustment(int i) {
104 |
105 | double ret = 0;
106 | switch(s_ptr->trans_type[i]) {
107 | case 0:
108 | // (no adjustment needed)
109 | break;
110 | case 1:
111 | ret = log(s_ptr->theta_max[i] - theta_prop[i]) - log(s_ptr->theta_max[i] - theta[i]);
112 | break;
113 | case 2:
114 | ret = log(theta_prop[i] - s_ptr->theta_min[i]) - log(theta[i] - s_ptr->theta_min[i]);
115 | break;
116 | case 3:
117 | ret = log(s_ptr->theta_max[i] - theta_prop[i]) + log(theta_prop[i] - s_ptr->theta_min[i]) - log(s_ptr->theta_max[i] - theta[i]) - log(theta[i] - s_ptr->theta_min[i]);
118 | break;
119 | default:
120 | Rcpp::stop("trans_type invalid");
121 | }
122 | return ret;
123 |
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/src/Particle.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "System.h"
5 | #include "misc.h"
6 |
7 | #include
8 |
9 | //------------------------------------------------
10 | // class defining MCMC particle
11 | class Particle {
12 |
13 | public:
14 | // PUBLIC OBJECTS
15 |
16 | // pointer to system object
17 | System * s_ptr;
18 |
19 | // local copies of some parameters for convenience
20 | int d;
21 |
22 | // thermodynamic power
23 | double beta_raised;
24 |
25 | // theta is the parameter vector in natural space
26 | Rcpp::NumericVector theta;
27 | Rcpp::NumericVector theta_prop;
28 |
29 | // phi is a vector of transformed parameters
30 | Rcpp::NumericVector phi;
31 | Rcpp::NumericVector phi_prop;
32 |
33 | // proposal parameters
34 | std::vector bw;
35 | std::vector bw_index;
36 | double bw_stepsize;
37 |
38 | // likelihoods and priors
39 | std::vector loglike_block;
40 | std::vector loglike_prop_block;
41 | double loglike;
42 | double loglike_prop;
43 | double logprior;
44 | double logprior_prop;
45 |
46 | // store acceptance rates
47 | int accept_count;
48 |
49 |
50 | // PUBLIC FUNCTIONS
51 |
52 | // constructors
53 | Particle() {};
54 |
55 | // initialise everything except for likelihood and prior values
56 | void init(System &s, double beta_raised);
57 |
58 | // initialise likelihood and prior values
59 | template
60 | void init_like(TYPE1 get_loglike, TYPE2 get_logprior) {
61 | PutRNGstate();
62 | for (int i = 0; i < d; ++i) {
63 | for (unsigned int j = 0; j < s_ptr->block[i].size(); ++j) {
64 | int this_block = s_ptr->block[i][j];
65 | s_ptr->misc["block"] = this_block;
66 | loglike_block[this_block - 1] = Rcpp::as(get_loglike(theta, s_ptr->x, s_ptr->misc));
67 | }
68 | }
69 | loglike = sum(loglike_block);
70 |
71 | logprior = Rcpp::as(get_logprior(theta, s_ptr->misc));
72 | GetRNGstate();
73 |
74 | // Catch for -Inf in likelihood or prior given init theta
75 | if(loglike == R_NegInf || logprior == R_NegInf){
76 | Rcpp::Rcerr << "\n Current theta " << theta << std::endl;
77 | Rcpp::stop("Starting values result in -Inf in likelihood or prior. Consider setting inital values in the parameters data.frame.");
78 | }
79 | }
80 |
81 | // update theta[i] via univariate Metropolis-Hastings
82 | template
83 | void update(TYPE1 get_loglike, TYPE2 get_logprior) {
84 |
85 | // set theta_prop and phi_prop to current values of theta and phi
86 | theta_prop = Rcpp::clone(theta);
87 | phi_prop = Rcpp::clone(phi);
88 |
89 | // loop through parameters
90 | for (int i = 0; i < d; ++i) {
91 | if (s_ptr->skip_param[i]) {
92 | continue;
93 | }
94 |
95 | // generate new phi_prop[i]
96 | propose_phi(i);
97 |
98 | // transform phi_prop[i] to theta_prop[i]
99 | phi_prop_to_theta_prop(i);
100 |
101 | // calculate adjustment factor, taking into account forwards and backwards
102 | // moves
103 | double adj = get_adjustment(i);
104 |
105 | // calculate loglikelihood in each block
106 | PutRNGstate();
107 | loglike_prop_block = loglike_block;
108 | for (unsigned int j = 0; j < s_ptr->block[i].size(); ++j) {
109 | int this_block = s_ptr->block[i][j];
110 | s_ptr->misc["block"] = this_block;
111 | loglike_prop_block[this_block - 1] = Rcpp::as(get_loglike(theta_prop, s_ptr->x, s_ptr->misc));
112 | }
113 |
114 | // calculate overall likelihood and prior of proposed theta
115 | loglike_prop = sum(loglike_prop_block);
116 |
117 | logprior_prop = Rcpp::as(get_logprior(theta_prop, s_ptr->misc));
118 | GetRNGstate();
119 |
120 | // Check for NA/NaN/Inf in likelihood or prior
121 | if(R_IsNaN(loglike_prop) || loglike_prop == R_PosInf || R_IsNA(loglike_prop)){
122 | Rcpp::Rcerr << "\n Current theta " << theta_prop << std::endl;
123 | Rcpp::stop("NA, NaN or Inf in likelihood");
124 | }
125 | if(R_IsNaN(logprior_prop) || logprior_prop == R_PosInf || R_IsNA(logprior_prop)){
126 | Rcpp::Rcerr << "\n Current theta " << theta_prop << std::endl;
127 | Rcpp::stop("NA, NaN or Inf in prior");
128 | }
129 |
130 | // calculate Metropolis-Hastings ratio
131 | double MH;
132 | if(beta_raised == 0.0){
133 | MH = (logprior_prop - logprior) + adj;
134 | } else {
135 | MH = beta_raised*(loglike_prop - loglike) + (logprior_prop - logprior) + adj;
136 | }
137 |
138 | // accept or reject move
139 | bool MH_accept = (log(R::runif(0,1)) < MH);
140 |
141 | // implement changes
142 | if (MH_accept) {
143 | // update theta and phi
144 | theta[i] = theta_prop[i];
145 | phi[i] = phi_prop[i];
146 |
147 | // update likelihoods
148 | loglike_block = loglike_prop_block;
149 | loglike = loglike_prop;
150 | logprior = logprior_prop;
151 |
152 | // Robbins-Monro positive update (on the log scale)
153 | bw[i] = exp(log(bw[i]) + bw_stepsize*(1 - s_ptr->target_acceptance) / sqrt(bw_index[i]));
154 | bw_index[i]++;
155 |
156 | // add to acceptance rate count
157 | accept_count++;
158 |
159 | } else {
160 | // reset theta_prop and phi_prop
161 | theta_prop[i] = theta[i];
162 | phi_prop[i] = phi[i];
163 |
164 | // Robbins-Monro negative update (on the log scale)
165 | bw[i] = exp(log(bw[i]) - bw_stepsize*s_ptr->target_acceptance / sqrt(bw_index[i]));
166 | bw_index[i]++;
167 |
168 | } // end MH step
169 | } // end loop over parameters
170 |
171 | } // end update_univar function
172 |
173 | // other public methods
174 | void propose_phi(int i);
175 | void phi_prop_to_theta_prop(int i);
176 | void theta_to_phi();
177 | double get_adjustment(int i);
178 |
179 | };
180 |
--------------------------------------------------------------------------------
/src/RcppExports.cpp:
--------------------------------------------------------------------------------
1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand
2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
3 |
4 | #include
5 |
6 | using namespace Rcpp;
7 |
8 | #ifdef RCPP_USE_GLOBAL_ROSTREAM
9 | Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get();
10 | Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
11 | #endif
12 |
13 | // main_cpp
14 | Rcpp::List main_cpp(Rcpp::List args);
15 | RcppExport SEXP _drjacoby_main_cpp(SEXP argsSEXP) {
16 | BEGIN_RCPP
17 | Rcpp::RObject rcpp_result_gen;
18 | Rcpp::RNGScope rcpp_rngScope_gen;
19 | Rcpp::traits::input_parameter< Rcpp::List >::type args(argsSEXP);
20 | rcpp_result_gen = Rcpp::wrap(main_cpp(args));
21 | return rcpp_result_gen;
22 | END_RCPP
23 | }
24 |
25 | static const R_CallMethodDef CallEntries[] = {
26 | {"_drjacoby_main_cpp", (DL_FUNC) &_drjacoby_main_cpp, 1},
27 | {NULL, NULL, 0}
28 | };
29 |
30 | RcppExport void R_init_drjacoby(DllInfo *dll) {
31 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
32 | R_useDynamicSymbols(dll, FALSE);
33 | }
34 |
--------------------------------------------------------------------------------
/src/System.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "System.h"
3 | #include "misc.h"
4 |
5 | using namespace std;
6 |
7 | void System::load(Rcpp::List args) {
8 |
9 | // split argument lists
10 | Rcpp::List args_params = args["args_params"];
11 | Rcpp::List args_functions = args["args_functions"];
12 | Rcpp::List args_progress = args["args_progress"];
13 | Rcpp::List args_progress_burnin = args_progress["pb_burnin"];
14 |
15 | // data
16 | x = args_params["x"];
17 |
18 | // misc
19 | misc = args_params["misc"];
20 |
21 | // model parameters
22 | theta_vector = args_params["theta_vector"];
23 | theta_min = rcpp_to_vector_double(args_params["theta_min"]);
24 | theta_max = rcpp_to_vector_double(args_params["theta_max"]);
25 | block = rcpp_to_matrix_int(args_params["block"]);
26 | n_block = rcpp_to_int(args_params["n_block"]);
27 | trans_type = rcpp_to_vector_int(args_params["trans_type"]);
28 | skip_param = rcpp_to_vector_bool(args_params["skip_param"]);
29 | d = int(theta_min.size());
30 | target_acceptance = rcpp_to_double(args_params["target_acceptance"]);
31 |
32 | // MCMC parameters
33 | burnin = rcpp_to_int(args_params["burnin"]);
34 | samples = rcpp_to_int(args_params["samples"]);
35 | rungs = rcpp_to_int(args_params["rungs"]);
36 | coupling_on = rcpp_to_bool(args_params["coupling_on"]);
37 | beta_raised = rcpp_to_vector_double(args_params["beta_raised"]);
38 | chain = rcpp_to_int(args_params["chain"]);
39 |
40 | // misc parameters
41 | save_hot_draws = rcpp_to_bool(args_params["save_hot_draws"]);
42 | pb_markdown = rcpp_to_bool(args_params["pb_markdown"]);
43 | silent = rcpp_to_bool(args_params["silent"]);
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/System.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 |
6 | #include
7 |
8 | //------------------------------------------------
9 | // class holding all data, parameters and functions
10 | class System {
11 |
12 | public:
13 | // PUBLIC OBJECTS
14 |
15 | // data
16 | Rcpp::List x;
17 |
18 | // misc object
19 | Rcpp::List misc;
20 |
21 | // model parameters
22 | Rcpp::NumericVector theta_vector;
23 | std::vector theta_min;
24 | std::vector theta_max;
25 | std::vector> block;
26 | int n_block;
27 | std::vector trans_type;
28 | std::vector skip_param;
29 | int d;
30 |
31 | // MCMC parameters
32 | int burnin;
33 | int samples;
34 | int rungs;
35 | bool coupling_on;
36 | std::vector beta_raised;
37 | int chain;
38 | double target_acceptance;
39 |
40 | // misc parameters
41 | bool save_hot_draws;
42 | bool pb_markdown;
43 | bool silent;
44 |
45 | // PUBLIC FUNCTIONS
46 |
47 | // constructors
48 | System() {};
49 |
50 | // public methods
51 | void load(Rcpp::List args);
52 |
53 | };
54 |
--------------------------------------------------------------------------------
/src/main.h:
--------------------------------------------------------------------------------
1 |
2 | #include "System.h"
3 | #include "Particle.h"
4 |
5 | #include
6 |
7 |
8 | //------------------------------------------------
9 | // main Rcpp function, deployed from R
10 | // [[Rcpp::export]]
11 | Rcpp::List main_cpp(Rcpp::List args);
12 |
13 | //------------------------------------------------
14 | // run MCMC
15 | template
16 | Rcpp::List run_mcmc(Rcpp::List args, TYPE1 get_loglike, TYPE2 get_logprior);
17 |
18 | //------------------------------------------------
19 | // Metropolis-coupling over temperature rungs
20 | void coupling(std::vector &particle_vec, std::vector &mc_accept);
21 |
--------------------------------------------------------------------------------
/tests/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/tests/.DS_Store
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | library(drjacoby)
3 |
4 | test_check("drjacoby")
5 |
--------------------------------------------------------------------------------
/tests/testthat/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrc-ide/drjacoby/edfea6339eb5a828410a0109bc9fef70c22ee210/tests/testthat/.DS_Store
--------------------------------------------------------------------------------
/tests/testthat/test-cpp_template.R:
--------------------------------------------------------------------------------
1 | test_that("template works", {
2 | expect_error(cpp_template(NULL))
3 | expect_error(cpp_template(1))
4 | expect_error(cpp_template(save_as = "foo"))
5 | expect_error(cpp_template(save_as = "foo.csv"))
6 | })
7 |
8 |
--------------------------------------------------------------------------------
/tests/testthat/test-diagnostic.R:
--------------------------------------------------------------------------------
1 | #------------------------------------------------
2 | test_that("gelman_rubin works", {
3 |
4 | # create dummy posterior draws matrix
5 | m <- cbind(chain = rep(1:2, each = 1e2), mu = rnorm(2e2))
6 |
7 | # expect error when running with single chain or sample
8 | expect_error(gelman_rubin(m, 1, 10))
9 | expect_error(gelman_rubin(m, 2, 1))
10 |
11 | # expect no warnings when run with correct parameters
12 | expect_silent(gelman_rubin(m, 2, 10))
13 | })
14 |
15 | #------------------------------------------------
16 | test_that("test_convergence works", {
17 |
18 | set.seed(1)
19 |
20 | # FALSE if single value
21 | x <- rnorm(1e2)
22 | expect_false(test_convergence(x, n = 1))
23 |
24 | # FALSE if ESS gives warning
25 | x <- rep(1e100, 100)
26 | #coda::effectiveSize(x)
27 | expect_false(test_convergence(x, n = 10))
28 |
29 | # FALSE if ESS too small
30 | x <- rep(0, 100)
31 | #coda::effectiveSize(x)
32 | expect_false(test_convergence(x, n = 100))
33 |
34 | # expect TRUE when run with appropriate values
35 | x <- rnorm(1e2)
36 | expect_true(test_convergence(x, n = 100))
37 |
38 | })
39 |
40 | #------------------------------------------------
41 | test_that("geweke_pvalue works", {
42 |
43 | # NaN if coda's geweke.diag() cannot calculate valid output
44 | expect_true(is.nan(geweke_pvalue(rep(0, 10))))
45 |
46 | # expect no warnings when run with appropriate values
47 | expect_silent(geweke_pvalue(rnorm(10)))
48 |
49 | })
50 |
51 |
--------------------------------------------------------------------------------
/tests/testthat/test-mcmc-with-cpp-likelihood-and-prior.R:
--------------------------------------------------------------------------------
1 | context("test-mcmc-with-cpp-likelihood-and-prior")
2 |
3 | test_that("Cpp likelihood and prior", {
4 | set.seed(1)
5 |
6 | # define true parameter values
7 | mu_true <- 3
8 | sigma_true <- 2
9 |
10 | # draw example data
11 | x <- rnorm(1000, mean = mu_true, sd = sigma_true)
12 | data_list <- list(x = x)
13 |
14 | # define parameters dataframe
15 | df_params <- define_params(name = "mu", min = -10, max = 10, init = 5,
16 | name = "sigma", min = 0, max = 10, init = 1)
17 |
18 | # Source Rcpp likehood and prior functions
19 | Rcpp::sourceCpp("test_input_files/loglike_logprior.cpp")
20 | # run MCMC
21 | cpp_mcmc_null <- run_mcmc(data = data_list,
22 | df_params = df_params,
23 | loglike = "loglikenull",
24 | logprior = "logprior",
25 | burnin = 1e3,
26 | samples = 1e3,
27 | chains = 1,
28 | silent = TRUE)
29 |
30 | # subset output
31 | pe <- dplyr::filter(cpp_mcmc_null$output, phase == "sampling") %>%
32 | dplyr::select(mu, sigma)
33 |
34 | # check posterior estimates
35 | posterior_estimate <- apply(pe, 2, median)
36 | expect_lt(posterior_estimate["mu"] - 6, 0.1) # should approximately follow the values in logprior_strong
37 | expect_lt(posterior_estimate["sigma"] - 1, 0.1) # should approximately follow the values in logprior_strong
38 |
39 | # run MCMC with null prior
40 | cpp_mcmc_data <- run_mcmc(data = data_list,
41 | df_params = df_params,
42 | loglike = "loglike",
43 | logprior = "logpriornull",
44 | burnin = 1e3,
45 | samples = 1e4,
46 | silent = TRUE)
47 |
48 | # subset output
49 | pe <- dplyr::filter(cpp_mcmc_data$output, phase == "sampling") %>%
50 | dplyr::select(mu, sigma)
51 |
52 | # check posterior estimates
53 | posterior_estimate2 <- apply(pe, 2, median)
54 | expect_lt(posterior_estimate2["mu"] - mu_true, 0.1)
55 | expect_lt(posterior_estimate2["sigma"] - sigma_true, 0.1)
56 |
57 | ## Multiple chains
58 | cpp_mcmc_chains <- run_mcmc(data = data_list,
59 | df_params = df_params,
60 | loglike = "loglike",
61 | logprior = "logpriornull",
62 | chains = 2,
63 | burnin = 1e3,
64 | samples = 1e4,
65 | silent = TRUE)
66 | expect_length(cpp_mcmc_chains, 4)
67 |
68 | # subset output to chain1 and check posterior estimates
69 | pe <- dplyr::filter(cpp_mcmc_chains$output, phase == "sampling", chain == 1) %>%
70 | dplyr::select(mu, sigma)
71 | posterior_estimate3a <- apply(pe, 2, median)
72 | expect_lt(posterior_estimate3a["mu"] - mu_true, 0.1)
73 | expect_lt(posterior_estimate3a["sigma"] - sigma_true, 0.1)
74 |
75 | # subset output to chain2 and check posterior estimates
76 | pe <- dplyr::filter(cpp_mcmc_chains$output, phase == "sampling", chain == 2) %>%
77 | dplyr::select(mu, sigma)
78 | posterior_estimate3b <- apply(pe, 2, median)
79 | expect_lt(posterior_estimate3b["mu"] - mu_true, 0.1)
80 | expect_lt(posterior_estimate3b["sigma"] - sigma_true, 0.1)
81 |
82 | ## Metropolis coupling
83 | mcmc_out_MC <- run_mcmc(data = data_list,
84 | df_params = df_params,
85 | loglike = "loglike",
86 | logprior = "logpriornull",
87 | burnin = 1e3,
88 | samples = 1e4,
89 | rungs = 4,
90 | silent = TRUE)
91 |
92 | # subset output
93 | pe <- dplyr::filter(mcmc_out_MC$output, phase == "sampling") %>%
94 | dplyr::select(mu, sigma)
95 |
96 | # check posterior estimates
97 | posterior_estimate4 <- apply(pe, 2, median)
98 | expect_lt(posterior_estimate4["mu"] - mu_true, 0.1)
99 | expect_lt(posterior_estimate4["sigma"] - sigma_true, 0.1)
100 | })
101 |
--------------------------------------------------------------------------------
/tests/testthat/test-mcmc-wth-mixed-likelihood-and-prior.R:
--------------------------------------------------------------------------------
1 | context("test-mcmc-with-mixed-likelihood-and-prior")
2 |
3 | test_that("Cpp likelihood and R prior, and vice versa", {
4 | set.seed(1)
5 |
6 | # define true parameter values
7 | mu_true <- 3
8 | sigma_true <- 2
9 |
10 | # draw example data
11 | x <- rnorm(1000, mean = mu_true, sd = sigma_true)
12 | data_list <- list(x = x)
13 |
14 | # define parameters dataframe
15 | df_params <- define_params(name = "mu", min = -10, max = 10, init = 5,
16 | name = "sigma", min = 0, max = 10, init = 1)
17 | # Source Rcpp likehood and prior functions
18 | Rcpp::sourceCpp("test_input_files/loglike_logprior.cpp")
19 |
20 | # R likelihood and prior
21 | r_loglike_null <- function(params, data, misc) {
22 | return(0)
23 | }
24 | r_logprior_null <- function(params, misc) {
25 | return(0)
26 | }
27 |
28 | # expect no warnings when run MCMC with both combinations of prior and
29 | # likelihood
30 | expect_silent(run_mcmc(data = data_list,
31 | df_params = df_params,
32 | loglike = "loglikenull",
33 | logprior = r_logprior_null,
34 | burnin = 1e2,
35 | samples = 1e2,
36 | chains = 1,
37 | silent = TRUE))
38 |
39 | expect_silent(run_mcmc(data = data_list,
40 | df_params = df_params,
41 | loglike = r_loglike_null,
42 | logprior = "logpriornull",
43 | burnin = 1e2,
44 | samples = 1e2,
45 | chains = 1,
46 | silent = TRUE))
47 |
48 | })
49 |
--------------------------------------------------------------------------------
/tests/testthat/test-mcmc_with_multi_function_cpp.R:
--------------------------------------------------------------------------------
1 | test_that("Multi function cpp likelihood and prior", {
2 | set.seed(1)
3 |
4 | # define true parameter values
5 | mu_true <- 3
6 | sigma_true <- 2
7 |
8 | # draw example data
9 | x <- rnorm(1000, mean = mu_true, sd = sigma_true)
10 | data_list <- list(x = x)
11 |
12 | # define parameters dataframe
13 | df_params <- define_params(name = "mu", min = -10, max = 10, init = 5,
14 | name = "sigma", min = 0, max = 10, init = 1)
15 |
16 | # Source Rcpp likehood and prior functions
17 | Rcpp::sourceCpp("test_input_files/multi_loglike_logprior.cpp")
18 |
19 | mcmc <- run_mcmc(data = data_list,
20 | df_params = df_params,
21 | loglike = "loglike",
22 | logprior = "logprior",
23 | burnin = 1e3,
24 | samples = 1e3,
25 | chains = 1,
26 | silent = TRUE)
27 |
28 | # subset output
29 | pe <- dplyr::filter(mcmc$output, phase == "sampling") %>%
30 | dplyr::select(mu, sigma)
31 |
32 | # check posterior estimates
33 | posterior_estimate <- apply(pe, 2, median)
34 | expect_lt(posterior_estimate["mu"] - mu_true, 0.1)
35 | expect_lt(posterior_estimate["sigma"] - sigma_true, 0.1)
36 | })
37 |
--------------------------------------------------------------------------------
/tests/testthat/test-plot.R:
--------------------------------------------------------------------------------
1 |
2 | #------------------------------------------------
3 | test_that("plots do not produce errors", {
4 |
5 | # define example data
6 | # (use small series so that loglikelihood comes back +ve in some cases, which
7 | # forces down a particular path in some plotting functions)
8 | data_list <- list(x = (1:10)*1e-2)
9 |
10 | # define parameters dataframe
11 | df_params <- rbind.data.frame(list("mu", -10, 10, 5),
12 | list("sigma", 0, 20, 1))
13 | names(df_params) <- c("name", "min", "max", "init")
14 |
15 | # log likelihood
16 | r_loglike <- function(params, data, misc) {
17 | sum(dnorm(data$x, mean = params["mu"], sd = params["sigma"], log = TRUE))
18 | }
19 |
20 | # log prior
21 | r_logprior <- function(params, misc) {
22 | dnorm(params["mu"], log = TRUE) + dlnorm(params["sigma"])
23 | }
24 |
25 | # run MCMC
26 | mcmc_out <- run_mcmc(data = data_list,
27 | df_params = df_params,
28 | loglike = r_loglike,
29 | logprior = r_logprior,
30 | burnin = 1e2,
31 | samples = 3e3,
32 | rungs = 2,
33 | chains = 2,
34 | silent = TRUE)
35 |
36 | # expect no output (messages or warnings) in all standard plotting functions
37 | expect_silent(plot_autocorrelation(mcmc_out))
38 |
39 | expect_silent(plot_mc_acceptance(mcmc_out))
40 | expect_silent(plot_mc_acceptance(mcmc_out, chain = 1))
41 | expect_silent(plot_mc_acceptance(mcmc_out, x_axis_type = 2))
42 |
43 | expect_silent(plot_trace(mcmc_out, display = FALSE))
44 | expect_silent(plot_trace(mcmc_out, show = "mu", display = FALSE))
45 | expect_silent(plot_trace(mcmc_out, hide = "mu", display = FALSE))
46 | expect_silent(plot_trace(mcmc_out, phase = "both", display = FALSE))
47 |
48 | expect_silent(plot_scatter(mcmc_out, parameter1 = "mu", parameter2 = "sigma"))
49 | expect_silent(plot_scatter(mcmc_out, parameter1 = "mu", parameter2 = "sigma", phase = "both"))
50 |
51 | expect_silent(plot_cor_mat(mcmc_out))
52 | expect_silent(plot_cor_mat(mcmc_out, show = c("mu", "sigma")))
53 | expect_silent(plot_cor_mat(mcmc_out, phase = "both"))
54 |
55 | expect_silent(plot_credible(mcmc_out))
56 | expect_silent(plot_credible(mcmc_out, show = "mu"))
57 | expect_silent(plot_credible(mcmc_out, phase = "both"))
58 |
59 | expect_silent(plot_rung_loglike(mcmc_out))
60 | expect_silent(plot_rung_loglike(mcmc_out, x_axis_type = 2))
61 | expect_silent(plot_rung_loglike(mcmc_out, y_axis_type = 2))
62 | expect_silent(plot_rung_loglike(mcmc_out, y_axis_type = 3))
63 |
64 | # repeat run with single rung
65 | mcmc_out <- run_mcmc(data = data_list,
66 | df_params = df_params,
67 | loglike = r_loglike,
68 | logprior = r_logprior,
69 | burnin = 1e2,
70 | samples = 3e3,
71 | rungs = 1,
72 | chains = 2,
73 | silent = TRUE)
74 |
75 | # further tests
76 | expect_error(plot_mc_acceptance(mcmc_out))
77 |
78 | })
79 |
--------------------------------------------------------------------------------
/tests/testthat/test_input_files/loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // Log likelihood
5 | // [[Rcpp::export]]
6 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
7 |
8 | // unpack data
9 | std::vector x = Rcpp::as< std::vector >(data["x"]);
10 |
11 | // unpack params
12 | double mu = params["mu"];
13 | double sigma = params["sigma"];
14 |
15 | // calculate log-likelihood
16 | double ret = 0.0;
17 | for (unsigned int i = 0; i < x.size(); ++i) {
18 | ret += R::dnorm(x[i], mu, sigma, 1);
19 | }
20 |
21 | // catch underflow
22 | if (!std::isfinite(ret)) {
23 | const double OVERFLO_DOUBLE = DBL_MAX/100.0;
24 | ret = -OVERFLO_DOUBLE;
25 | }
26 |
27 | return Rcpp::wrap(ret);
28 | }
29 |
30 | // Log prior
31 | // [[Rcpp::export]]
32 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
33 |
34 | // unpack params
35 | double mu = params["mu"];
36 | double sigma = params["sigma"];
37 |
38 | // calculate logprior
39 | double ret = R::dnorm(mu, 6, 0.1, 1) + R::dnorm(sigma, 1, 0.1, 1);
40 |
41 | // catch underflow
42 | if (!std::isfinite(ret)) {
43 | const double OVERFLO_DOUBLE = DBL_MAX/100.0;
44 | ret = -OVERFLO_DOUBLE;
45 | }
46 |
47 | // return as SEXP
48 | return Rcpp::wrap(ret);
49 | }
50 |
51 | // Null log likelihood
52 | // [[Rcpp::export]]
53 | SEXP loglikenull(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
54 | return Rcpp::wrap(0.0);
55 | }
56 |
57 | // Null log prior
58 | // [[Rcpp::export]]
59 | SEXP logpriornull(Rcpp::NumericVector params, Rcpp::List misc) {
60 | return Rcpp::wrap(0.0);
61 | }
62 |
63 |
64 | // [[Rcpp::export]]
65 | SEXP create_xptr(std::string function_name) {
66 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
67 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
68 |
69 | if (function_name == "loglike"){
70 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
71 | }
72 | if (function_name == "loglikenull"){
73 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglikenull))) ;
74 | }
75 | if (function_name == "logprior"){
76 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
77 | }
78 | if (function_name == "logpriornull"){
79 | return(Rcpp::XPtr(new funcPtr_prior(&logpriornull))) ;
80 | }
81 |
82 | stop("cpp function %i not found", function_name);
83 | }
84 |
--------------------------------------------------------------------------------
/tests/testthat/test_input_files/multi_loglike_logprior.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | using namespace Rcpp;
3 |
4 | // Silly example so we can show that our loglike function can call others internally
5 | double none(){
6 | double x = 0.0;
7 | return x;
8 | }
9 |
10 | // Log likelihood
11 | // [[Rcpp::export]]
12 | SEXP loglike(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc) {
13 |
14 | // unpack data
15 | std::vector x = Rcpp::as< std::vector >(data["x"]);
16 |
17 | // unpack params
18 | double mu = params["mu"];
19 | double sigma = params["sigma"];
20 |
21 | // calculate log-likelihood
22 | double ret = 0.0;
23 | for (unsigned int i = 0; i < x.size(); ++i) {
24 | ret += R::dnorm(x[i], mu, sigma, 1) + none();
25 | }
26 |
27 | // catch underflow
28 | if (!std::isfinite(ret)) {
29 | const double OVERFLO_DOUBLE = DBL_MAX/100.0;
30 | ret = -OVERFLO_DOUBLE;
31 | }
32 |
33 | return Rcpp::wrap(ret);
34 | }
35 |
36 | // Log prior
37 | // [[Rcpp::export]]
38 | SEXP logprior(Rcpp::NumericVector params, Rcpp::List misc) {
39 |
40 | // unpack params
41 | double mu = params["mu"];
42 | double sigma = params["sigma"];
43 |
44 | // calculate logprior
45 | double ret = none();
46 |
47 | // return as SEXP
48 | return Rcpp::wrap(ret);
49 | }
50 |
51 | // [[Rcpp::export]]
52 | SEXP create_xptr(std::string function_name) {
53 | typedef SEXP (*funcPtr_likelihood)(Rcpp::NumericVector params, Rcpp::List data, Rcpp::List misc);
54 | typedef SEXP (*funcPtr_prior)(Rcpp::NumericVector params, Rcpp::List misc);
55 |
56 | if (function_name == "loglike"){
57 | return(Rcpp::XPtr(new funcPtr_likelihood(&loglike)));
58 | }
59 | if (function_name == "logprior"){
60 | return(Rcpp::XPtr(new funcPtr_prior(&logprior)));
61 | }
62 |
63 | stop("cpp function %i not found", function_name);
64 | }
65 |
--------------------------------------------------------------------------------
/vignettes/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.R
3 |
--------------------------------------------------------------------------------
/vignettes/checks_double_well.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Double well"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Double well}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include=FALSE}
13 | knitr::opts_chunk$set(echo = TRUE)
14 | ```
15 |
16 | ```{r, echo = FALSE}
17 | # set random seed
18 | set.seed(1)
19 |
20 | # load the drjacoby package
21 | library(drjacoby)
22 | ```
23 |
24 | **Purpose:** to compare *drjacoby* results for a challenging problem involving a multimodal posterior, both with and without temperature rungs.
25 |
26 | ## Model
27 |
28 | We assume a single parameter `mu` drawn from a double well potential distribution, defined by the formula:
29 |
30 | $$
31 | \begin{aligned}
32 | \mu &\propto exp\left(-\gamma(\mu^2 - 1)^2\right)
33 | \end{aligned}
34 | $$
35 | where $\gamma$ is a parameter that defines the strength of the well (higher $\gamma$ leads to a deeper valley and hence more challenging problem). NB, there is no data in this example, as the likelihood is defined exactly by these parameters.
36 |
37 | Likelihood and prior:
38 |
39 | ```{r, echo = FALSE, comment = ''}
40 | Rcpp::sourceCpp(system.file("extdata/checks/", "doublewell_loglike_logprior.cpp", package = 'drjacoby', mustWork = TRUE))
41 | ```
42 |
43 | Parameters dataframe:
44 |
45 | ```{r}
46 | L <- 2
47 | gamma <- 30
48 | df_params <- define_params(name = "mu", min = -L, max = L,
49 | name = "gamma", min = gamma, max = gamma)
50 | ```
51 |
52 |
53 | ## Single temperature rung (no Metropolis coupling)
54 |
55 | ```{r}
56 | mcmc <- run_mcmc(data = list(x = -1),
57 | df_params = df_params,
58 | loglike = "loglike",
59 | logprior = "logprior",
60 | burnin = 1e3,
61 | samples = 1e5,
62 | chains = 1,
63 | rungs = 1,
64 | silent = TRUE)
65 | ```
66 |
67 | ```{r}
68 | # trace plot
69 | plot_trace(mcmc, show = "mu")
70 | ```
71 |
72 | ```{r}
73 | # extract posterior draws
74 | output_sub <- subset(mcmc$output, phase == "sampling")
75 | mu_draws <- output_sub$mu
76 |
77 | # get analytical solution
78 | x <- seq(-L, L, l = 1001)
79 | fx <- exp(-gamma*(x^2 - 1)^2)
80 | fx <- fx / sum(fx) * 1/(x[2]-x[1])
81 |
82 | # overlay plots
83 | hist(mu_draws, breaks = seq(-L, L, l = 201), probability = TRUE, main = "", col = "black")
84 | lines(x, fx, col = 2, lwd = 2)
85 | ```
86 |
87 |
88 | ## Multiple temperature rungs
89 |
90 | ```{r}
91 | mcmc <- run_mcmc(data = list(x = -1),
92 | df_params = df_params,
93 | loglike = "loglike",
94 | logprior = "logprior",
95 | burnin = 1e3,
96 | samples = 1e5,
97 | chains = 1,
98 | rungs = 11,
99 | alpha = 2,
100 | pb_markdown = TRUE)
101 | ```
102 |
103 | ```{r, fig.width=6, fig.height=5}
104 | # trace plot
105 | plot_trace(mcmc, show = "mu")
106 |
107 | # coupling acceptance plot
108 | plot_mc_acceptance(mcmc)
109 | ```
110 |
111 | ```{r, fig.width=6, fig.height=5}
112 | # extract posterior draws
113 | output_sub <- subset(mcmc$output, phase == "sampling")
114 | mu_draws <- output_sub$mu
115 |
116 | # overlay plots
117 | hist(mu_draws, breaks = seq(-L, L, l = 201), probability = TRUE, main = "", col = "black")
118 | lines(x, fx, col = 2, lwd = 2)
119 | ```
120 |
--------------------------------------------------------------------------------
/vignettes/checks_multilevel_blocks.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Multilevel example with blocks"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Multilevel example with blocks}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include=FALSE}
13 | knitr::opts_chunk$set(echo = TRUE)
14 | ```
15 |
16 | ```{r, echo = FALSE}
17 | # set random seed
18 | set.seed(1)
19 |
20 | # load the drjacoby package
21 | library(drjacoby)
22 | ```
23 |
24 | **Purpose:** to compare *drjacoby* infered posterior distribution against the exact analytical solution in a multi-level model implemented via likelihood blocks.
25 |
26 | ## Model
27 |
28 | There are `g` groups with means `mu_1` to `mu_g`. These component means are drawn from a normal distribution with mean 0 and sd 1.0. Within each group there are `n` observations drawn around each component mean from normal disribution with sd 1.0.
29 |
30 |
31 | ```{r}
32 | g <- 5
33 | mu <- rnorm(g)
34 |
35 | n <- 5
36 | data_list <- list()
37 | for (i in 1:g) {
38 | data_list[[i]] <- rnorm(n, mean = mu[i])
39 | }
40 | names(data_list) <- sprintf("group%s", 1:g)
41 | ```
42 |
43 | Likelihood and prior:
44 |
45 | ```{r, echo = FALSE, comment = ''}
46 | Rcpp::sourceCpp(system.file("extdata/checks/", "multilevel_loglike_logprior.cpp", package = 'drjacoby', mustWork = TRUE))
47 | ```
48 |
49 | Parameters dataframe:
50 |
51 | ```{r}
52 | L <- 5
53 | df_params <- define_params(name = "mu_1", min = -L, max = L, block = c(1, 6),
54 | name = "mu_2", min = -L, max = L, block = c(2, 6),
55 | name = "mu_3", min = -L, max = L, block = c(3, 6),
56 | name = "mu_4", min = -L, max = L, block = c(4, 6),
57 | name = "mu_5", min = -L, max = L, block = c(5, 6))
58 | ```
59 |
60 | ## MCMC
61 |
62 | ```{r cars}
63 | mcmc <- run_mcmc(data = data_list,
64 | df_params = df_params,
65 | loglike = "loglike",
66 | logprior = "logprior",
67 | burnin = 1e3,
68 | samples = 1e5,
69 | chains = 10,
70 | silent = TRUE)
71 | ```
72 |
73 | ## Plots
74 |
75 | Black = posterior draws
76 |
77 | Red = analytical solution to multi-level model
78 |
79 | Green = analytical solution assuming independent groups (no second level)
80 |
81 | ```{r, fig.width=6, fig.height=5}
82 | # extract sampling draws
83 | output_sub <- subset(mcmc$output, phase == "sampling")
84 |
85 | for (i in 1:5) {
86 | # get posterior draws
87 | mu_draws <- output_sub[[sprintf("mu_%s", i)]]
88 |
89 | # get analytical solution for this group
90 | x <- seq(-L, L, l = 1001)
91 | m <- mean(data_list[[i]])
92 | fx <- dnorm(x, mean = m * n/(n + 1), sd = sqrt(1/(n + 1)))
93 |
94 | # get analytical solution if no multi-level model
95 | fx2 <- dnorm(x, mean = m, sd = sqrt(1/n))
96 |
97 | # overlay plots
98 | hist(mu_draws, breaks = seq(-L, L, l = 1001), probability = TRUE, col = "black",
99 | main = sprintf("mu_%s", i))
100 | lines(x, fx, col = 2, lwd = 4)
101 | lines(x, fx2, col = 3, lwd = 4)
102 | }
103 | ```
104 |
--------------------------------------------------------------------------------
/vignettes/checks_normal_model.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Normal model"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Normal model}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include=FALSE}
13 | knitr::opts_chunk$set(echo = TRUE)
14 | ```
15 |
16 | ```{r, echo = FALSE}
17 | # set random seed
18 | set.seed(1)
19 |
20 | # load the drjacoby package
21 | library(drjacoby)
22 | ```
23 |
24 | **Purpose:** to compare *drjacoby* infered posterior distribution against the exact analytical solution for a very simple model.
25 |
26 | ## Model
27 |
28 | A vector of data `x` drawn from a normal distribution with unknown mean `mu` and known variance of 1.0.
29 |
30 | ```{r}
31 | n <- 1e2
32 | data_list <- list(x = rnorm(n))
33 | ```
34 |
35 | Likelihood and prior:
36 |
37 | ```{r, echo = FALSE, comment = ''}
38 | Rcpp::sourceCpp(system.file("extdata/checks/", "normal_loglike_logprior.cpp", package = 'drjacoby', mustWork = TRUE))
39 | ```
40 |
41 | Parameters dataframe:
42 |
43 | ```{r}
44 | df_params <- define_params(name = "mu", min = -1, max = 1)
45 | ```
46 |
47 | ## MCMC
48 |
49 | ```{r cars}
50 | mcmc <- run_mcmc(data = data_list,
51 | df_params = df_params,
52 | loglike = "loglike",
53 | logprior = "logprior",
54 | burnin = 1e3,
55 | samples = 1e5,
56 | chains = 10,
57 | silent = TRUE)
58 | ```
59 |
60 | ## Posterior plots
61 |
62 | ```{r, fig.width=6, fig.height=5}
63 | # extract sampling draws
64 | mu_draws <- subset(mcmc$output, phase = "sampling")$mu
65 |
66 | # calculate analytical solution
67 | x <- seq(-1, 1, l = 1001)
68 | fx <- dnorm(x, mean = mean(data_list$x), sd = sqrt(1/n))
69 |
70 | # histogram and overlay analytical
71 | hist(mu_draws, breaks = seq(-1, 1, 0.01), probability = TRUE, col = "black")
72 | lines(x, fx, col = 2, lwd = 4)
73 | ```
74 |
--------------------------------------------------------------------------------
/vignettes/checks_return_prior.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Return prior"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Return prior}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include=FALSE}
13 | knitr::opts_chunk$set(echo = TRUE)
14 | ```
15 |
16 | ```{r, echo = FALSE}
17 | # set random seed
18 | set.seed(1)
19 |
20 | # load the drjacoby package
21 | library(drjacoby)
22 | ```
23 | **Purpose:** to check that *drjacoby* returns the prior distribution when no likelihood is used.
24 |
25 | ## Model
26 |
27 | Four parameters, each representing a different one of the four possible parameter transformations internally. Each parameter is given a suitable informative prior over it's domain.
28 |
29 | Parameters dataframe:
30 |
31 | ```{r}
32 | df_params <- define_params(name = "real_line", min = -Inf, max = Inf,
33 | name = "neg_line", min = -Inf, max = 0,
34 | name = "pos_line", min = 0, max = Inf,
35 | name = "unit_interval", min = 0, max = 1)
36 | ```
37 |
38 | Likelihood and prior:
39 |
40 | ```{r, echo = FALSE, comment = ''}
41 | Rcpp::sourceCpp(system.file("extdata/checks/", "returnprior_loglike_logprior.cpp", package = 'drjacoby', mustWork = TRUE))
42 | ```
43 |
44 | ## Run MCMC
45 |
46 | ```{r}
47 | mcmc <- run_mcmc(data = list(x = -1),
48 | df_params = df_params,
49 | loglike = "loglike",
50 | logprior = "logprior",
51 | burnin = 1e3,
52 | samples = 1e5,
53 | chains = 10,
54 | silent = TRUE)
55 | ```
56 |
57 | ## Plots
58 |
59 | ```{r, fig.width=6, fig.height=5}
60 | output_sub <- subset(mcmc$output, phase == "sampling")
61 |
62 | # real_line
63 | hist(output_sub$real_line, breaks = 100, probability = TRUE, col = "black", main = "real_line")
64 | x <- seq(-5, 5, l = 1001)
65 | lines(x, dnorm(x), col = 2, lwd = 2)
66 |
67 | # neg_line
68 | hist(output_sub$neg_line, breaks = 100, probability = TRUE, col = "black", main = "neg_line")
69 | x <- seq(-100, 0, l = 1001)
70 | lines(x, dgamma(-x, shape = 5, scale = 5), col = 2, lwd = 2)
71 |
72 | # pos_line
73 | hist(output_sub$pos_line, breaks = 100, probability = TRUE, col = "black", main = "pos_line")
74 | x <- seq(0, 100, l = 1001)
75 | lines(x, dgamma(x, shape = 5, scale = 5), col = 2, lwd = 2)
76 |
77 | # unit_interval
78 | hist(output_sub$unit_interval, breaks = seq(0, 1, 0.01), probability = TRUE, col = "black", main = "unit_interval")
79 | x <- seq(0, 10, l = 1001)
80 | lines(x, dbeta(x, shape1 = 3, shape2 = 3), col = 2, lwd = 2)
81 | ```
82 |
--------------------------------------------------------------------------------
/vignettes/getting_model_fits.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Getting Model Fits"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Getting Model Fits}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r, include = FALSE}
13 | knitr::opts_chunk$set(
14 | collapse = TRUE,
15 | comment = "#>"
16 | )
17 | ```
18 |
19 | ```{r, echo = FALSE}
20 | # set random seed
21 | set.seed(1)
22 |
23 | # load the drjacoby package
24 | library(drjacoby)
25 |
26 | # load other packages
27 | library(ggplot2)
28 | ```
29 |
30 | Sometimes our likelihood function involves calculating a series of intermediate values. A good example is when we have a model that describes our fit to the data, which may be combined with an error term to capture random noise around this prediction. Once the MCMC has finished, we may want to extract this model fit so that we can see how good it really is. Having already written down the code for calculating the model fit once within the likelihood, we don't want to have to write it down again when visualising results as this duplication of work is not only a waste of time but also can introduce errors.
31 |
32 | This vignette demonstrates how intermediate values can be obtained from MCMC output using a single likelihood function.
33 |
34 |
35 | ## A simple model of population growth
36 |
37 | For this example we will use some data on population growth and we will attempt to fit a simple curve through these points. The data can be loaded directly from within the *drjacoby* package, and takes the form of a simple data.frame of sampling times and population sizes:
38 |
39 | ```{r, width = 10, height = 5}
40 | # load example data
41 | data("population_data")
42 | head(population_data)
43 |
44 | # plot points
45 | plot(population_data$time, population_data$pop, ylim = c(0, 120), pch = 20,
46 | xlab = "Time (days)", ylab = "Population size")
47 | ```
48 |
49 | For our model we will assume a discrete-time logistic growth model, in which the population starts at size $N_0$ and grows at a rate $r$ each timestep, but also experiences density-dependent death causing it to plateau out at a maximum population size $N_{max}$. If we use $N_t$ to denote the population size at time $t$ then we can write this model as follows:
50 |
51 | $$
52 | \begin{align}
53 | N_{t+1} = rN_t(1 - N_t / K), \hspace{10mm} \mbox{where } K = N_{\mbox{max}}\left(\frac{r}{r-1}\right).
54 | \end{align}
55 | $$
56 |
57 | There are three parameters in this model, and so our parameters dataframe should contain the three variables with suitable ranges as follows:
58 |
59 | ```{r}
60 | # define parameters dataframe
61 | df_params <- define_params(name = "N_0", min = 1, max = 100,
62 | name = "r", min = 1, max = 2,
63 | name = "N_max", min = 50, max = 200)
64 | ```
65 |
66 | Within our likelihood function we need to calculate our expected population growth curve. This curve is perfectly smooth, but the real data is noisy so we will use a Poisson distribution to link the data to the model.
67 |
68 | Crucially, when defining the likelihood function we will create an option to return the model prediction, rather than the log-likelihood. We use the `misc` input to the function to control whether this option is active or not. If we set `misc$output_type = 1` then we will obtain the log-likelihood, but if we set `misc$output_type = 2` then we will obtain the complete curve of model-predicted values:
69 |
70 | ```{r}
71 | # define log-likelihood function
72 | loglike <- function(params, data, misc) {
73 |
74 | # extract parameter values
75 | N_0 <- params["N_0"]
76 | r <- params["r"]
77 | N_max <- params["N_max"]
78 |
79 | # calculate expected population growth
80 | K <- N_max * r / (r - 1)
81 | x <- rep(0, 100)
82 | x[1] <- N_0
83 | for (i in 2:length(x)) {
84 | x[i] <- r * x[i-1] * (1 - x[i-1] / K)
85 | }
86 |
87 | # option to return model prediction rather than log-likelihood
88 | if (misc$output_type == 2) {
89 | return(x)
90 | }
91 |
92 | # calculate log-likelihood
93 | ret <- sum(dpois(data$pop, lambda = x[data$time], log = TRUE))
94 |
95 | # return
96 | return(ret)
97 | }
98 |
99 | # define R logprior function
100 | logprior <- function(params, misc) {
101 | dunif(params["N_0"], 1, 100, log = TRUE) +
102 | dunif(params["r"], 1, 2, log = TRUE) +
103 | dunif(params["N_max"], 50, 200, log = TRUE)
104 | }
105 | ```
106 |
107 | When running the MCMC we want to make sure `misc$output_type = 1`:
108 |
109 | ```{r}
110 | # run MCMC
111 | mcmc <- run_mcmc(data = population_data,
112 | df_params = df_params,
113 | misc = list(output_type = 1),
114 | loglike = loglike,
115 | logprior = logprior,
116 | burnin = 1e3,
117 | samples = 1e4,
118 | chains = 3,
119 | pb_markdown = TRUE)
120 | ```
121 |
122 | Once we have MCMC output we should perform our usual checks to make sure that everything has converged nicely (we will skip this step here in the interest of time). Assuming everything is OK, we can move on to exploring model fits. We will do this by running the same likelihood function but with `misc$output_type = 2`, using the posterior parameter draws as inputs. We may not want to use every posterior parameter draw, in which case we can use the `sample_chains()` function to sub-sample the output down as needed:
123 |
124 | ```{r}
125 | # sample from posterior
126 | param_draws <- sample_chains(mcmc, 1000)
127 |
128 | # get matrix of model predictions
129 | model_matrix <- apply(param_draws, 1, function(x) {
130 | loglike(params = x, data = population_data, misc = list(output_type = 2))
131 | })
132 | ```
133 |
134 | Plotting these against the data we can see that - reassuringly - the posterior model fits make a smooth curve through the data points:
135 |
136 | ```{r}
137 | matplot(model_matrix, type = 'l', lty = 1, col = "#99000010", ylim = c(0, 120),
138 | xlab = "Time (days)", ylab = "Population size")
139 | points(population_data$time, population_data$pop, pch = 20)
140 | ```
141 |
142 | Notice that we only had to define the model fitting computation once in this pipeline, as the same likelihood function was used for both inference and obtaining the fit. There are more complicated versions of this approach that may be useful in some settings, for example using `switch` functions or multiple `if` statements to allow for several different types of output from the likelihood function, or even varying aspects of the core model such as the error distribution using flags contained in `misc`.
143 |
--------------------------------------------------------------------------------
/vignettes/installation.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Installing drjacoby"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Installing drjacoby}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include = FALSE}
13 | knitr::opts_chunk$set(
14 | collapse = TRUE,
15 | comment = "#>"
16 | )
17 | ```
18 |
19 | ## Installing Rcpp
20 |
21 | *drjacoby* relies on the [Rcpp](https://cran.r-project.org/web/packages/Rcpp/index.html) package, which requires the following OS-specific steps:
22 |
23 | * Windows
24 | - Download and install the appropriate version of [Rtools](https://cran.rstudio.com/bin/windows/Rtools/) for your version of R. On installation, ensure you check the box to arrange your system PATH as recommended by Rtools
25 | * Mac OS X
26 | - Download and install [XCode](http://itunes.apple.com/us/app/xcode/id497799835?mt=12)
27 | - Within XCode go to Preferences : Downloads and install the Command Line Tools
28 | * Linux (Debian/Ubuntu)
29 | - Install g++ with
30 |
31 | ```{}
32 | sudo apt-get update
33 | sudo apt-get install g++
34 | ```
35 |
36 | Irrespective of which system you use above, you should then install and load Rcpp with
37 |
38 | ```{r, eval = FALSE}
39 | install.packages("Rcpp")
40 | library(Rcpp)
41 | ```
42 |
43 | You can check the version number to make sure it has properly installed
44 | ```{r, eval = FALSE}
45 | packageVersion("Rcpp")
46 | ```
47 |
48 |
49 | ## Installing and loading *drjacoby*
50 |
51 | Next, in R, ensure that you have the [devtools](https://www.rstudio.com/products/rpackages/devtools/) package installed by running
52 |
53 | ```{r, eval = FALSE}
54 | install.packages("devtools", repos='http://cran.us.r-project.org')
55 | ```
56 |
57 | Then install the *drjacoby* package directly from GitHub by running
58 |
59 | ```{r, eval = FALSE}
60 | devtools::install_github("mrc-ide/drjacoby")
61 | ```
62 |
63 | If you have any problems installing then please [raise an issue](https://github.com/mrc-ide/drjacoby/issues) on github.
64 |
65 | Assuming everything installed correctly, we need to load the package:
66 |
67 | ```{r}
68 | library(drjacoby)
69 | ```
70 |
71 | You can test that the package is loaded and working by running the following command, which should produce this output:
72 |
73 | ```{r}
74 | check_drjacoby_loaded()
75 | ```
76 |
77 |
78 |
--------------------------------------------------------------------------------
/vignettes/parallel.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Running in Parallel"
3 | author: "Bob Verity and Pete Winskill"
4 | date: "`r Sys.Date()`"
5 | output: rmarkdown::html_vignette
6 | vignette: >
7 | %\VignetteIndexEntry{Running in Parallel}
8 | %\VignetteEngine{knitr::rmarkdown}
9 | %\VignetteEncoding{UTF-8}
10 | ---
11 |
12 | ```{r setup, include = FALSE}
13 | knitr::opts_chunk$set(
14 | collapse = TRUE,
15 | comment = "#>"
16 | )
17 | ```
18 |
19 | ```{r, echo = FALSE}
20 | # set random seed
21 | set.seed(1)
22 |
23 | # load the drjacoby package
24 | library(drjacoby)
25 | ```
26 |
27 | Running multiple chains is a good way of checking that our MCMC is working well. Each chain is completely independent of all others, and so this qualifies as an [embarrassingly parallel](https://en.wikipedia.org/wiki/Embarrassingly_parallel) problem.
28 |
29 | This vignette will demonstrate how to run *drjacoby* with multiple chains, first in serial and then in parallel over multiple cores.
30 |
31 | ## Setup
32 |
33 | As always, we require some data, some parameters, and some functions to work with (see [earlier examples](https://mrc-ide.github.io/drjacoby/articles/example.html)). The underlying model is not our focus here, so we will use a very basic setup
34 |
35 |
36 | ```{r}
37 | # define data
38 | data_list <- list(x = rnorm(10))
39 |
40 | # define parameters dataframe
41 | df_params <- data.frame(name = "mu", min = -10, max = 10, init = 0)
42 |
43 | # define loglike function
44 | r_loglike <- function(params, data, misc) {
45 | sum(dnorm(data$x, mean = params["mu"], sd = 1.0, log = TRUE))
46 | }
47 |
48 | # define logprior function
49 | r_logprior <- function(params, misc) {
50 | dunif(params["mu"], min = -10, max = 10, log = TRUE)
51 | }
52 | ```
53 |
54 | ## Running multiple chains
55 |
56 | Whenever the input argument `cluster` is `NULL`, chains will run in serial. This is true by default, so running multiple chains in serial is simply a case of specifying the `chains` argument:
57 |
58 | ```{r}
59 | # run MCMC in serial
60 | mcmc <- run_mcmc(data = data_list,
61 | df_params = df_params,
62 | loglike = r_loglike,
63 | logprior = r_logprior,
64 | burnin = 1e3,
65 | samples = 1e3,
66 | chains = 2,
67 | pb_markdown = TRUE)
68 | ```
69 |
70 | When we look at our MCMC output (using the `plot_trace()` function) we can see that there are 2 chains, each of which contains a series of draws from the posterior. If we used multiple [temperature rungs](https://mrc-ide.github.io/drjacoby/articles/metropolis_coupling.html) then these would also be duplicated over chains.
71 |
72 | ```{r, fig.width=10, fig.height=4}
73 | # summarise output
74 | mcmc
75 |
76 | # compare mu over both chains
77 | plot_trace(mcmc, "mu", phase = "both")
78 | ```
79 |
80 | Running in parallel is only slightly more complex. Before running anything we need to know how many cores our machine has. You may know this number already, but if you don't then the `parallel` package has a handy function for detecting the number of cores for you:
81 |
82 | ```{r, eval = FALSE}
83 | cores <- parallel::detectCores()
84 | ```
85 |
86 | Next we make a cluster object, which creates multiple copies of R running in parallel over different cores. Here we are using all available cores, but if you want to hold some back for other intensive tasks then simply use a smaller number of cores when specifying this cluster.
87 |
88 | ```{r, eval = FALSE}
89 | cl <- parallel::makeCluster(cores)
90 | ```
91 |
92 | We then run the usual `run_mcmc()` function, this time passing in the cluster object as an argument. This causes *drjacoby* to use a `clusterApplyLB()` call rather than an ordinary `lapply()` call over different chains. Each chain is added to a queue over the specified number of cores - when the first job completes, the next job is placed on the node that has become available and this continues until all jobs are complete.
93 |
94 | Note that output is supressed when running in parallel to avoid sending print commands to multiple cores, so you will not see the usual progress bars.
95 |
96 | ```{r, eval = FALSE}
97 | # run MCMC in parallel
98 | mcmc <- run_mcmc(data = data_list,
99 | df_params = df_params,
100 | loglike = r_loglike,
101 | logprior = r_logprior,
102 | burnin = 1e3,
103 | samples = 1e3,
104 | chains = 2,
105 | cluster = cl,
106 | pb_markdown = TRUE)
107 | ```
108 |
109 | Finally, it is good practice to shut down the workers once we are finished:
110 |
111 | ```{r, eval = FALSE}
112 | parallel::stopCluster(cl)
113 | ```
114 |
115 |
116 | ## Running multiple chains using C++ log likelihood or log prior functions
117 |
118 | To run the MCMC in parallel with C++ log-likelihood or log-prior functions there is on additional step to take when setting up
119 | the cluster. Each node must be able to access a version of the compiled C++ functions, so after initialising the cluster with:
120 |
121 | ```{r, eval = FALSE}
122 | cl <- parallel::makeCluster(cores)
123 | ```
124 |
125 | we must also make the C++ functions accessible, by running the Rcpp::sourceCPP command for our C++ file for each node:
126 |
127 | ```{r, eval = FALSE}
128 | cl_cpp <- parallel::clusterEvalQ(cl, Rcpp::sourceCpp("my_cpp.cpp"))
129 | ```
130 |
131 | After which we can run the mcmc in the same way:
132 |
133 | ```{r, eval = FALSE}
134 | # run MCMC in parallel
135 | mcmc <- run_mcmc(data = data_list,
136 | df_params = df_params,
137 | loglike = "loglike",
138 | logprior = "logprior",
139 | burnin = 1e3,
140 | samples = 1e3,
141 | chains = 2,
142 | cluster = cl,
143 | pb_markdown = TRUE)
144 | parallel::stopCluster(cl)
145 | ```
146 |
147 |
--------------------------------------------------------------------------------