Practical Bayesian Modeling with Stan and brms

Author

Masatoshi Katabuchi

Published

November 3, 2025

1 Docker (optional)

Run this on your terminal to start an RStudio Server with all the necessary packages installed.

# run in afec-bayes-2025 directory
docker run -d -p 8787:8787 \
  -e PASSWORD=123456  \
  -v $(pwd):/home/rstudio/workspace \
  mattocci/afec-bayes:prod

Access 127.0.0.1:8787 and log in with the username rstudio and the password 123456 (or your own password).

Note: Because you’re running this container on your own machine and mapping only to 127.0.0.1:8787, the RStudio Server isn’t accessible from outside your computer. So, using a simple password like 123456 is fine and safe for local development.

2 renv settings (Optional)

Comment out the following lines in .Rprofile if you are not using renv.

# source("renv/activate.R")
# Sys.setenv(RENV_DOWNLOAD_METHOD = "curl")
# options(renv.install.staged = FALSE)
# renv::settings$use.cache(TRUE)
# Sys.setenv(RENV_INSTALL_PAK_ENABLED = "TRUE")

3 Setup

library(brms)
library(cmdstanr)
library(tidyverse)
library(bayesplot)
library(patchwork)
cmdstanr::cmdstan_path()
[1] "/home/rstudio/.cmdstan/cmdstan-2.37.0"
my_theme <- theme_bw() + theme(
    axis.text = element_text(size = 14),
    strip.text = element_text(size = 14)
  )

theme_set(my_theme)

4 Normal model

Leaf mass per area (LMA) is an important trait that reflects plant strategies. LMA for interspecific data often shows a log-normal distribution.

\[ \text{log}(y_i) \sim N(\mu, \sigma) \]

Q: What are the mean (\(\mu\)) and standard deviation (\(\sigma\)) of log LMA?

4.1 Dummy data

set.seed(123)
n <- 100
mu <- 4.6
sigma <- 0.7
y <- rnorm(n, mean = mu, sd = sigma)

hist(y)

cmdstan uses list format for data input.

normal_list <- list(
  y = y,
  N = length(y)
)

brms uses dataframe (tibble) format for data input like lme4::lmer and stats::glm.

normal_df <- tibble(
  y = y,
  N = length(y)
)

4.2 CmdStan

We need to write and save Stan programs (.stan files).

stan/normal.stan

 // observed data
data {
  int<lower=0> N;
  vector[N] y;
}

// parameters to be estimated
parameters {
  real mu;
  real<lower=0> sigma;
}

model {
  // likelihood
  y ~ normal(mu, sigma);
  // priors
  mu ~ normal(0, 10);
  sigma ~ cauchy(0, 2.5);
} 

stan/normal_vague.stan

 data {
  int<lower=0> N;
  vector[N] y;
}

parameters {
  real mu;
  real<lower=0> sigma;
}

model {
  y ~ normal(mu, sigma);
  mu ~ normal(0, 1e+4); // Vague prior for mu
  sigma ~ normal(0, 1e+4); // Vague prior for sigma
} 

These commands compile the models, translating Stan code to C++ and then into machine-executable files. This takes a while (about 20 secs to 1 min) the first time only.

normal_mod <- cmdstan_model("stan/normal.stan")
normal_vague_mod <- cmdstan_model("stan/normal_vague.stan")
# Persist CSV outputs to avoid /tmp cleanup during render
normal_out_dir <- file.path("assets", "cmdstan", "normal")
if (!dir.exists(normal_out_dir)) dir.create(normal_out_dir, recursive = TRUE)

normal_fit <- normal_mod$sample(
  data = normal_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000,  # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  refresh = 500,        # print update every 500 iters
  output_dir = normal_out_dir
)
Running MCMC with 4 parallel chains...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: normal_lpdf: Scale parameter is 0, but must be positive! (in '/tmp/Rtmp10tuT6/model-23c7595ff71.stan', line 15, column 2 to column 24)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.3 seconds.

We don’t have to recompile the model when we want to change the number of iterations, chains, or data.

normal_vague_fit <- normal_vague_mod$sample(
  data = normal_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  refresh = 500 # print update every 500 iters
)

4.2.1 Summary

We use this output when writing manuscripts and for model diagnostics.

normal_fit$summary()
# A tibble: 3 × 10
  variable   mean median     sd    mad     q5    q95  rhat ess_bulk ess_tail
  <chr>     <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl>    <dbl>    <dbl>
1 lp__     -6.35  -6.04  0.997  0.731  -8.36  -5.38   1.00    1801.    2192.
2 mu        4.66   4.66  0.0657 0.0661  4.56   4.77   1.00    3300.    2446.
3 sigma     0.648  0.646 0.0466 0.0449  0.577  0.730  1.00    3181.    2516.
  • lp__: log posterior.
    • \(\text{log}\; p(\theta \mid \text{data}) \propto \text{log}\; p(\text{data} \mid \theta) + \text{log}\; p(\theta)\)
  • rhat (Gelman-Rubin statistic): should be close to 1.
    • Measures the convergence of the chains.
    • rhat > 1.1: Definitely bad.
    • 1.01 < rhat < 1.1: Suspicious.
    • rhat <= 1.01: Good.
  • ess_bulk and ess_tail: Effective sample size for bulk and tail of the posterior distribution.
    • ess_bulk: sampling efficiency for the bulk posteriors (e.g., mean, SD).
    • ess_tail: sampling efficiency for the tails (e.g., quantiles like 2.5%, 97.5%).
    • ess_bulk, ess_tail > 400: Good.

4.2.2 Draws (posterior samples)

For each parameter, we have 1000 iterations \(\times\) 4 chains = 4000 posteriors. We use this for visualization and diagnostics.

normal_fit$draws()
# A draws_array: 1000 iterations, 4 chains, and 3 variables
, , variable = lp__

         chain
iteration    1    2    3    4
        1 -7.9 -5.5 -6.7 -6.2
        2 -7.6 -5.4 -8.9 -6.8
        3 -6.7 -5.3 -6.4 -8.3
        4 -6.1 -5.6 -5.9 -6.7
        5 -7.8 -5.6 -6.3 -7.3

, , variable = mu

         chain
iteration   1   2   3   4
        1 4.6 4.6 4.6 4.7
        2 4.6 4.7 4.8 4.7
        3 4.6 4.7 4.7 4.8
        4 4.6 4.7 4.6 4.7
        5 4.8 4.7 4.7 4.7

, , variable = sigma

         chain
iteration    1    2    3    4
        1 0.75 0.63 0.72 0.65
        2 0.57 0.65 0.59 0.57
        3 0.69 0.64 0.61 0.57
        4 0.64 0.62 0.66 0.58
        5 0.65 0.62 0.70 0.58

# ... with 995 more iterations

Trace plots for diagnosing convergence and mixing of the chains.

normal_draws <- as_draws_df(normal_fit$draws())

color_scheme_set("viridis")
mcmc_trace(normal_draws, pars = c("mu", "sigma"))

Histograms of the posterior distributions of the parameters.

mcmc_hist(normal_draws, pars = c("mu", "sigma"))
`stat_bin()` using `bins = 30`. Pick better value `binwidth`.

4.2.3 Diagnostic summary

normal_fit$diagnostic_summary()
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

$ebfmi
[1] 1.151656 1.101592 1.137121 1.100724
  • num_divergent: indicates the number of iterations (sampling transitions) where the Hamiltonian trajectory failed (divergent transition).
    • Even 1-2 divergent transitions suggest that model may not be reliable, especially in hierarchical models.
    • We need reparameterization or increase adapt_delta to make the sampler take smaller steps.
  • num_max_treedepth: indicates the number of iterations where there are not enough leapfrog steps.
    • This can indicate that the model is complex or that the priors are too tight.
    • We can increase max_treedepth to allow more steps, but this can increase the computation time.
  • ebfmi: Energy-Bayesian Fraction of Missing Information
    • Measures whether the resampled momentum generates enough variation in energy to explore the posterior efficiently.
    • Low ebfmi means the sampler may be stuck in tight regions and exploring poorly, even if Rhat and ESS look fine.
    • Guidelines:
      • ebfmi < 0.3: Bad
      • 0.3 < ebfmi <= 1: Acceptable
      • ebfmi >= 1: Good

4.2.4 Methods text example

“We estimated posterior distributions of all parameters using the Hamiltonian Monte Carlo (HMC) algorithm implemented in Stan (Carpenter et al., 2017) with weakly informative priors (Gelman et al., 2008). The HMC algorithm uses gradient information to propose new states in the Markov chain, leading to a more efficient exploration of the target distribution than traditional Markov chain Monte Carlo (MCMC) methods that rely on random proposals (Carpenter et al., 2017). This efficiency allows us to achieve convergence with fewer iterations than traditional MCMC methods. Four independent chains were run for 2000 iterations for each model with a warm-up of 1000 iterations. Convergence of the posterior distribution was assessed using the Gelman–Rubin statistic with a convergence threshold of 1.1 (Gelman et al., 2013), ensuring effective sample sizes greater than 400 (Vehtari et al., 2021), and by monitoring divergent transitions (Betancourt, 2016) for all parameters.”

4.3 brms

brms uses an lme4-like syntax.

normal_fit_brm0 <- brm(y ~ 1, family = gaussian(), data = normal_df)

We can also specify priors for the parameters. The prior argument can be a bit tricky, since it is not always obvious which parameter names correspond to which parts of the model, especially when the model becomes more complex.

We can also control the number of warmup iterations, total iterations, and other sampling settings.

The cmdstanr backend is generally faster than the default rstan backend.

normal_fit_brm <- brm(
  y ~ 1,
  family = gaussian(),
  data = normal_df,
  prior = c(
    prior(normal(0, 5), class = "Intercept"),
    prior(cauchy(0, 2.5), class = "sigma")
  ),
  seed = 123,
  iter = 2000, # total iterations (warmup + post-warmup)
  warmup = 1000,
  chains = 4, cores = 4,
  backend = "cmdstanr"
)
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:11,
                 from stan/lib/stan_math/stan/math/prim/fun/hypergeometric_pFq.hpp:8,
                 from stan/lib/stan_math/stan/math/prim/fun/hypergeometric_2F1.hpp:19,
                 from stan/lib/stan_math/stan/math/prim/fun/grad_2F1.hpp:14,
                 from stan/lib/stan_math/stan/math/prim/fun.hpp:117,
                 from stan/lib/stan_math/stan/math/prim/constraint.hpp:4,
                 from stan/lib/stan_math/stan/math/rev/constraint.hpp:24,
                 from stan/lib/stan_math/stan/math/rev.hpp:11,
                 from stan/lib/stan_math/stan/math.hpp:19,
                 from stan/src/stan/model/model_header.hpp:6:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp: In instantiation of ‘std::pair<_ForwardIterator, _ForwardIterator> boost::math::detail::hypergeometric_pFq_checked_series_impl(const Seq&, const Seq&, const Real&, const Policy&, const Terminal&, long long int&) [with Seq = std::vector<double>; Real = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>; Terminal = iteration_terminator]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:61:107:   required from ‘typename boost::math::tools::promote_args<Real, typename Seq::value_type>::type boost::math::hypergeometric_pFq(const Seq&, const Seq&, const Real&, Real*, const Policy&) [with Seq = std::vector<double>; Real = double; Policy = policies::poli
cy<policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; typename tools::promote_args<Real, typename Seq::value_type>::type = double; typename Seq::value_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:72:35:   required from ‘typename boost::math::tools::promote_args<Real, typename Seq::value_type>::type boost::math::hypergeometric_pFq(const Seq&, const Seq&, const Real&, Real*) [with Seq = std::vector<double>; Real = double; typename tools::promote_args<Real, typename Seq::value_type>::type = double; typename Seq::value_type = double]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_pFq.hpp:53:41:   required from ‘stan::return_type_t<T, L, U> stan::math::hypergeometric_pFq(const Ta&, const Tb&, const Tz&) [with Ta = Eigen::Matrix<double, -1, 1>; Tb = Eigen::Matrix<double, -1, 1>; Tz = double; stan::require_all_vector_st<std::is_arithmetic, Ta, Tb>* <anonymous> = 0; stan::require_arithmetic_t<Tz>* <anonymous> = 0; stan::return_type_t<T, L, U> = double]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_3F2.hpp:130:28:   required from ‘auto stan::math::hypergeometric_3F2(const Ta&, const Tb&, const Tz&) [with Ta = std::vector<double>; Tb = std::vector<double>; Tz = double; stan::require_all_vector_t<T_y, T_high>* <anonymous> = 0; stan::require_stan_scalar_t<T2>* <anonymous> = 0]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_3F2.hpp:154:28:   required from ‘auto stan::math::hypergeometric_3F2(const std::initializer_list<_Tp>&, const std::initializer_list<_Value>&, const Tz&) [with Ta = double; Tb = double; Tz = double; stan::require_all_stan_scalar_t<T, L, U>* <anonymous> = 0]’
stan/lib/stan_math/stan/math/rev/fun/inv_inc_beta.hpp:80:37:   required from here
stan/lib/s
tan_math/lib/boost_1.87.0/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp:122:28: note: parameter passing for argument of type ‘std::pair<long double, long double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  122 |      std::pair<Real, Real> hypergeometric_pFq_checked_series_impl(const Seq& aj, const Seq& bj, const Real& z, const Policy& pol, const Terminal& termination, long long& log_scale)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/factorials.hpp:17,
                 from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/binomial.hpp:16,
                 from stan/lib/stan_math/stan/math/prim/fun/choose.hpp:7,
                 from stan/lib/stan_math/stan/math/prim/functor/hcubature.hpp:21,
                 from stan/lib/stan_math/stan/math/prim/functor.hpp:14,
                 from stan/lib/stan_math/stan/math/rev/fun/to_arena.hpp:7,
                 from stan/lib/stan_math/stan/math/rev/core/operator_division.hpp:15,
                 from stan/lib/stan_math/stan/math/rev/core/operator_divide_equal.hpp:5,
                 from stan/lib/stan_math/stan/math/rev/core.hpp:29,
                 from stan/src/stan/model/model_base.hpp:8,
                 from stan/src/stan/model/model_header.hpp:4:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp: In instantiation of ‘boost::math::detail::upper_incomplete_gamma_fract<T>::result_type boost::math::detail::upper_incomplete_gamma_fract<T>::operator()() [with T = double; result_type = std::pair<double, double>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:259:20:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_a_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<double>; U = double; typename fraction_traits<Gen>::result_type = double; uintmax_t = long unsigned int]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:311:44:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::continued_fraction_a(Gen&, const U&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<double>; U = double; typename detail::fraction_traits<Gen>::result_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_
functions/gamma.hpp:378:68:   required from ‘T boost::math::detail::upper_gamma_fraction(T, T, T) [with T = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:1679:44:   required from ‘T boost::math::detail::gamma_incomplete_imp(T, T, bool, bool, const Policy&, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:2461:35:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::gamma_p(RT1, RT2, const Policy&) [with RT1 = double; RT2 = double; Policy = policies::policy<policies::overflow_error<boost::math::policies::errno_on_error>, policies::pole_error<boost::math::policies::errno_on_error>, policies::promote_double<false>, policies::digits2<0>, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/stan/math/prim/fun/gamma_p.hpp:76:30:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:363:39: note: parameter passing for argument of type ‘std::pair<double, double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  363 |    BOOST_MATH_GPU_ENABLED result_type 
operator()()
      |                                       ^~~~~~~~
In file included from stan/lib/stan_math/stan/math/prim/fun/owens_t.hpp:6,
                 from stan/lib/stan_math/stan/math/prim/fun.hpp:238:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp: In instantiation of ‘std::pair<_FIter, _FIter> boost::math::detail::owens_t_T1_accelerated(T, T, const Policy&) [with T = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:874:46:   required from ‘RealType boost::math::detail::owens_t_dispatch(RealType, RealType, RealType, const Policy&, const std::integral_constant<int, 65>&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:979:36:   required from ‘RealType boost::math::detail::owens_t_dispatch(RealType, RealType, RealType, const Policy&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::polici
es::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1000:38:   required from ‘RealType boost::math::detail::owens_t(RealType, RealType, const Policy&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1072:86:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::owens_t(T1, T2, const Policy&) [with T1 = double; T2 = double; Policy = policies::policy<policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1078:24:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::owens_t(T1, T2) [with T1 = double; T2 = double; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/stan/math/prim/fun/owens_t.hpp:62:30:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:543:26: note: parameter passing for argument of type ‘std::pair<long double, long
 double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  543 |          std::pair<T, T> owens_t_T1_accelerated(T h, T a, const Policy& pol)
      |                          ^~~~~~~~~~~~~~~~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/binomial.hpp:17:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp: In instantiation of ‘boost::math::detail::ibeta_fraction2_t<T>::result_type boost::math::detail::ibeta_fraction2_t<T>::operator()() [with T = double; result_type = std::pair<double, double>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:134:20:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_b_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::ibeta_fraction2_t<double>; U = double; typename fraction_traits<Gen>::result_type = double; uintmax_t = long unsigned int]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:184:44:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::continued_fraction_b(Gen&, const U&) [with Gen = boost::math::detail::ibeta_fraction2_t<double>; U = double; typename detail::fraction_traits<Gen>::result_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:847:54:   required from ‘T boost::math::detail::ibeta_fraction2(T, T, T, T, const Policy&, bool, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math
/special_functions/beta.hpp:1499:36:   required from ‘T boost::math::detail::ibeta_imp(T, T, T, const Policy&, bool, bool, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:1536:20:   required from ‘T boost::math::detail::ibeta_imp(T, T, T, const Policy&, bool, bool) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:1690:93:   required from ‘boost::math::tools::promote_args_t<RT1, RT2, A> boost::math::ibeta(RT1, RT2, RT3, const Policy&) [with RT1 = double; RT2 = double; RT3 = double; Policy = policies::policy<policies::overflow_error<boost::math::policies::errno_on_error>, policies::pole_error<boost::math::policies::errno_on_error>, p
olicies::promote_double<false>, policies::digits2<0>, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2, A> = double]’
stan/lib/stan_math/stan/math/prim/fun/inc_beta.hpp:29:28:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:810:39: note: parameter passing for argument of type ‘std::pair<double, double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  810 |    BOOST_MATH_GPU_ENABLED result_type operator()()
      |                                       ^~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:19:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp: In function ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_a_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<long double>; U = long double]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:259:15: note: parameter passing for argument of type ‘std::pair<long double, long double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  259 |    value_type v = g();
      |               ^
Start sampling
Running MCMC with 4 parallel chains...

Chain 1 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 1 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 1 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 1 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 1 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 1 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 1 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 1 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 1 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 1 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 1 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 1 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 1 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 1 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 1 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 1 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 1 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 1 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 1 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 1 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 1 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 1 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 2 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 2 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 2 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 2 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 2 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 2 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 2 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 2 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 2 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 2 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 2 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 2 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 2 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 2 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 2 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 2 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 2 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 2 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 2 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 2 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 2 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 2 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 3 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 3 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 3 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 3 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 3 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 3 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 3 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 3 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 3 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 3 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 3 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 3 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 3 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 3 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 3 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 3 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 3 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 3 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 3 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 3 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 3 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 3 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 4 Iteration:    1 / 2000 [  0%]  (Warmup) 
Chain 4 Iteration:  100 / 2000 [  5%]  (Warmup) 
Chain 4 Iteration:  200 / 2000 [ 10%]  (Warmup) 
Chain 4 Iteration:  300 / 2000 [ 15%]  (Warmup) 
Chain 4 Iteration:  400 / 2000 [ 20%]  (Warmup) 
Chain 4 Iteration:  500 / 2000 [ 25%]  (Warmup) 
Chain 4 Iteration:  600 / 2000 [ 30%]  (Warmup) 
Chain 4 Iteration:  700 / 2000 [ 35%]  (Warmup) 
Chain 4 Iteration:  800 / 2000 [ 40%]  (Warmup) 
Chain 4 Iteration:  900 / 2000 [ 45%]  (Warmup) 
Chain 4 Iteration: 1000 / 2000 [ 50%]  (Warmup) 
Chain 4 Iteration: 1001 / 2000 [ 50%]  (Sampling) 
Chain 4 Iteration: 1100 / 2000 [ 55%]  (Sampling) 
Chain 4 Iteration: 1200 / 2000 [ 60%]  (Sampling) 
Chain 4 Iteration: 1300 / 2000 [ 65%]  (Sampling) 
Chain 4 Iteration: 1400 / 2000 [ 70%]  (Sampling) 
Chain 4 Iteration: 1500 / 2000 [ 75%]  (Sampling) 
Chain 4 Iteration: 1600 / 2000 [ 80%]  (Sampling) 
Chain 4 Iteration: 1700 / 2000 [ 85%]  (Sampling) 
Chain 4 Iteration: 1800 / 2000 [ 90%]  (Sampling) 
Chain 4 Iteration: 1900 / 2000 [ 95%]  (Sampling) 
Chain 4 Iteration: 2000 / 2000 [100%]  (Sampling) 
Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.1 seconds.
Loading required namespace: rstan

4.3.1 Summary

summary(normal_fit_brm)
 Family: gaussian 
  Links: mu = identity 
Formula: y ~ 1 
   Data: normal_df (Number of observations: 100) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     4.66      0.07     4.53     4.79 1.00     3560     2246

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.65      0.05     0.56     0.75 1.00     3307     2776

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).

4.3.2 Draws (posterior samples)

normal_draws_brm <- posterior::as_draws_df(normal_fit_brm)
normal_draws_brm
# A draws_df: 1000 iterations, 4 chains, and 5 variables
   b_Intercept sigma Intercept lprior lp__
1          4.6  0.80       4.6   -4.4 -106
2          4.6  0.79       4.6   -4.4 -106
3          4.6  0.81       4.6   -4.4 -107
4          4.6  0.60       4.6   -4.4 -103
5          4.8  0.75       4.8   -4.4 -105
6          4.7  0.73       4.7   -4.4 -103
7          4.7  0.58       4.7   -4.4 -103
8          4.6  0.67       4.6   -4.4 -102
9          4.6  0.61       4.6   -4.4 -102
10         4.7  0.70       4.7   -4.4 -103
# ... with 3990 more draws
# ... hidden reserved variables {'.chain', '.iteration', '.draw'}
mcmc_trace(normal_draws_brm, pars = c("b_Intercept", "sigma"))

mcmc_hist(normal_draws_brm, pars = c("b_Intercept", "sigma"))
`stat_bin()` using `bins = 30`. Pick better value `binwidth`.

4.3.3 Diagnostic summary

rstan::check_hmc_diagnostics(normal_fit_brm$fit)

Divergences:
0 of 4000 iterations ended with a divergence.

Tree depth:
0 of 4000 iterations saturated the maximum tree depth of 10.

Energy:
E-BFMI indicated no pathological behavior.

5 Poisson model

Count data (e.g., species richness, seed counts, pollinator visits…) often follow a Poisson distribution.

\[ y_i \sim \text{Poisson}(\lambda) \]

\[ y_i \sim \operatorname{Poisson\_log}(\log \lambda) \]

Q: What are the mean and variance (\(\lambda\)) of species richness per plot?

5.1 Dummy data

set.seed(123)
y <- rpois(n, lambda = 12.3)

pois_list <- list(
  y = y,
  N = n
)

pois_df <- tibble(
  y = y,
  N = n
)

5.2 CmdStan

stan/pois.stan

 data {
  int<lower=0> N;
  array[N] int<lower=0> y;
}

parameters {
  real<lower=0> lambda;
}

model {
  y ~ poisson(lambda);
  lambda ~ normal(0, 10);
} 

stan/pois_re.stan

 data {
  int<lower=0> N;
  array[N] int<lower=0> y;
}

parameters {
  real log_lambda;
}

model {
  y ~ poisson_log(log_lambda);
  log_lambda ~ normal(0, 2.5);
} 
pois_mod <- cmdstan_model("stan/pois.stan")
pois_re_mod <- cmdstan_model("stan/pois_re.stan")
pois_fit <- pois_mod$sample(
  data = pois_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  refresh = 0 # print update every 500 iters
)
Running MCMC with 4 parallel chains...

Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.2 seconds.
pois_re_fit <- pois_re_mod$sample(
  data = pois_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  refresh = 0 # print update every 500 iters
)
Running MCMC with 4 parallel chains...

Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.2 seconds.
pois_fit$summary()
# A tibble: 2 × 10
  variable   mean median    sd   mad     q5    q95  rhat ess_bulk ess_tail
  <chr>     <dbl>  <dbl> <dbl> <dbl>  <dbl>  <dbl> <dbl>    <dbl>    <dbl>
1 lp__     1836.  1836.  0.710 0.305 1834.  1836.   1.00    1898.    2556.
2 lambda     12.2   12.2 0.349 0.344   11.6   12.8  1.00    1370.    1871.
pois_re_fit$summary()
# A tibble: 2 × 10
  variable      mean  median     sd    mad     q5    q95  rhat ess_bulk ess_tail
  <chr>        <dbl>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl> <dbl>    <dbl>    <dbl>
1 lp__       1833.   1834.   0.714  0.324  1.83e3 1.83e3  1.00    2170.    2574.
2 log_lambda    2.50    2.50 0.0287 0.0295 2.46e0 2.55e0  1.00    1367.    2197.

5.3 brms

pois_fit_brm <- brm(y ~ 1,
  family = poisson(),
  data = pois_df,
  refresh = 0,
  backend = "cmdstanr")
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:11,
                 from stan/lib/stan_math/stan/math/prim/fun/hypergeometric_pFq.hpp:8,
                 from stan/lib/stan_math/stan/math/prim/fun/hypergeometric_2F1.hpp:19,
                 from stan/lib/stan_math/stan/math/prim/fun/grad_2F1.hpp:14,
                 from stan/lib/stan_math/stan/math/prim/fun.hpp:117,
                 from stan/lib/stan_math/stan/math/prim/constraint.hpp:4,
                 from stan/lib/stan_math/stan/math/rev/constraint.hpp:24,
                 from stan/lib/stan_math/stan/math/rev.hpp:11,
                 from stan/lib/stan_math/stan/math.hpp:19,
                 from stan/src/stan/model/model_header.hpp:6:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp: In instantiation of ‘std::pair<_ForwardIterator, _ForwardIterator> boost::math::detail::hypergeometric_pFq_checked_series_impl(const Seq&, const Seq&, const Real&, const Policy&, const Terminal&, long long int&) [with Seq = std::vector<double>; Real = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>; Terminal = iteration_terminator]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:61:107:   required from ‘typename boost::math::tools::promote_args<Real, typename Seq::value_type>::type boost::math::hypergeometric_pFq(const Seq&, const Seq&, const Real&, Real*, const Policy&) [with Seq = std::vector<double>; Real = double; Policy = policies::poli
cy<policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; typename tools::promote_args<Real, typename Seq::value_type>::type = double; typename Seq::value_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/hypergeometric_pFq.hpp:72:35:   required from ‘typename boost::math::tools::promote_args<Real, typename Seq::value_type>::type boost::math::hypergeometric_pFq(const Seq&, const Seq&, const Real&, Real*) [with Seq = std::vector<double>; Real = double; typename tools::promote_args<Real, typename Seq::value_type>::type = double; typename Seq::value_type = double]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_pFq.hpp:53:41:   required from ‘stan::return_type_t<T, L, U> stan::math::hypergeometric_pFq(const Ta&, const Tb&, const Tz&) [with Ta = Eigen::Matrix<double, -1, 1>; Tb = Eigen::Matrix<double, -1, 1>; Tz = double; stan::require_all_vector_st<std::is_arithmetic, Ta, Tb>* <anonymous> = 0; stan::require_arithmetic_t<Tz>* <anonymous> = 0; stan::return_type_t<T, L, U> = double]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_3F2.hpp:130:28:   required from ‘auto stan::math::hypergeometric_3F2(const Ta&, const Tb&, const Tz&) [with Ta = std::vector<double>; Tb = std::vector<double>; Tz = double; stan::require_all_vector_t<T_y, T_high>* <anonymous> = 0; stan::require_stan_scalar_t<T2>* <anonymous> = 0]’
stan/lib/stan_math/stan/math/prim/fun/hypergeometric_3F2.hpp:154:28:   required from ‘auto stan::math::hypergeometric_3F2(const std::initializer_list<_Tp>&, const std::initializer_list<_Value>&, const Tz&) [with Ta = double; Tb = double; Tz = double; stan::require_all_stan_scalar_t<T, L, U>* <anonymous> = 0]’
stan/lib/stan_math/stan/math/rev/fun/inv_inc_beta.hpp:80:37:   required from here
stan/lib/s
tan_math/lib/boost_1.87.0/boost/math/special_functions/detail/hypergeometric_pFq_checked_series.hpp:122:28: note: parameter passing for argument of type ‘std::pair<long double, long double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  122 |      std::pair<Real, Real> hypergeometric_pFq_checked_series_impl(const Seq& aj, const Seq& bj, const Real& z, const Policy& pol, const Terminal& termination, long long& log_scale)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/factorials.hpp:17,
                 from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/binomial.hpp:16,
                 from stan/lib/stan_math/stan/math/prim/fun/choose.hpp:7,
                 from stan/lib/stan_math/stan/math/prim/functor/hcubature.hpp:21,
                 from stan/lib/stan_math/stan/math/prim/functor.hpp:14,
                 from stan/lib/stan_math/stan/math/rev/fun/to_arena.hpp:7,
                 from stan/lib/stan_math/stan/math/rev/core/operator_division.hpp:15,
                 from stan/lib/stan_math/stan/math/rev/core/operator_divide_equal.hpp:5,
                 from stan/lib/stan_math/stan/math/rev/core.hpp:29,
                 from stan/src/stan/model/model_base.hpp:8,
                 from stan/src/stan/model/model_header.hpp:4:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp: In instantiation of ‘boost::math::detail::upper_incomplete_gamma_fract<T>::result_type boost::math::detail::upper_incomplete_gamma_fract<T>::operator()() [with T = double; result_type = std::pair<double, double>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:259:20:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_a_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<double>; U = double; typename fraction_traits<Gen>::result_type = double; uintmax_t = long unsigned int]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:311:44:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::continued_fraction_a(Gen&, const U&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<double>; U = double; typename detail::fraction_traits<Gen>::result_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_
functions/gamma.hpp:378:68:   required from ‘T boost::math::detail::upper_gamma_fraction(T, T, T) [with T = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:1679:44:   required from ‘T boost::math::detail::gamma_incomplete_imp(T, T, bool, bool, const Policy&, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:2461:35:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::gamma_p(RT1, RT2, const Policy&) [with RT1 = double; RT2 = double; Policy = policies::policy<policies::overflow_error<boost::math::policies::errno_on_error>, policies::pole_error<boost::math::policies::errno_on_error>, policies::promote_double<false>, policies::digits2<0>, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/stan/math/prim/fun/gamma_p.hpp:76:30:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:363:39: note: parameter passing for argument of type ‘std::pair<double, double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  363 |    BOOST_MATH_GPU_ENABLED result_type 
operator()()
      |                                       ^~~~~~~~
In file included from stan/lib/stan_math/stan/math/prim/fun/owens_t.hpp:6,
                 from stan/lib/stan_math/stan/math/prim/fun.hpp:238:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp: In instantiation of ‘std::pair<_FIter, _FIter> boost::math::detail::owens_t_T1_accelerated(T, T, const Policy&) [with T = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:874:46:   required from ‘RealType boost::math::detail::owens_t_dispatch(RealType, RealType, RealType, const Policy&, const std::integral_constant<int, 65>&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:979:36:   required from ‘RealType boost::math::detail::owens_t_dispatch(RealType, RealType, RealType, const Policy&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::polici
es::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1000:38:   required from ‘RealType boost::math::detail::owens_t(RealType, RealType, const Policy&) [with RealType = long double; Policy = boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1072:86:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::owens_t(T1, T2, const Policy&) [with T1 = double; T2 = double; Policy = policies::policy<policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:1078:24:   required from ‘boost::math::tools::promote_args_t<RT1, RT2> boost::math::owens_t(T1, T2) [with T1 = double; T2 = double; tools::promote_args_t<RT1, RT2> = double]’
stan/lib/stan_math/stan/math/prim/fun/owens_t.hpp:62:30:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/owens_t.hpp:543:26: note: parameter passing for argument of type ‘std::pair<long double, long
 double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  543 |          std::pair<T, T> owens_t_T1_accelerated(T h, T a, const Policy& pol)
      |                          ^~~~~~~~~~~~~~~~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/binomial.hpp:17:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp: In instantiation of ‘boost::math::detail::ibeta_fraction2_t<T>::result_type boost::math::detail::ibeta_fraction2_t<T>::operator()() [with T = double; result_type = std::pair<double, double>]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:134:20:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_b_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::ibeta_fraction2_t<double>; U = double; typename fraction_traits<Gen>::result_type = double; uintmax_t = long unsigned int]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:184:44:   required from ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::continued_fraction_b(Gen&, const U&) [with Gen = boost::math::detail::ibeta_fraction2_t<double>; U = double; typename detail::fraction_traits<Gen>::result_type = double]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:847:54:   required from ‘T boost::math::detail::ibeta_fraction2(T, T, T, T, const Policy&, bool, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math
/special_functions/beta.hpp:1499:36:   required from ‘T boost::math::detail::ibeta_imp(T, T, T, const Policy&, bool, bool, T*) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:1536:20:   required from ‘T boost::math::detail::ibeta_imp(T, T, T, const Policy&, bool, bool) [with T = double; Policy = boost::math::policies::policy<boost::math::policies::pole_error<boost::math::policies::errno_on_error>, boost::math::policies::overflow_error<boost::math::policies::errno_on_error>, boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy>]’
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:1690:93:   required from ‘boost::math::tools::promote_args_t<RT1, RT2, A> boost::math::ibeta(RT1, RT2, RT3, const Policy&) [with RT1 = double; RT2 = double; RT3 = double; Policy = policies::policy<policies::overflow_error<boost::math::policies::errno_on_error>, policies::pole_error<boost::math::policies::errno_on_error>, p
olicies::promote_double<false>, policies::digits2<0>, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy, policies::default_policy>; tools::promote_args_t<RT1, RT2, A> = double]’
stan/lib/stan_math/stan/math/prim/fun/inc_beta.hpp:29:28:   required from here
stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/beta.hpp:810:39: note: parameter passing for argument of type ‘std::pair<double, double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  810 |    BOOST_MATH_GPU_ENABLED result_type operator()()
      |                                       ^~~~~~~~
In file included from stan/lib/stan_math/lib/boost_1.87.0/boost/math/special_functions/gamma.hpp:19:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp: In function ‘typename boost::math::tools::detail::fraction_traits<Gen>::result_type boost::math::tools::detail::continued_fraction_a_impl(Gen&, const U&, uintmax_t&) [with Gen = boost::math::detail::upper_incomplete_gamma_fract<long double>; U = long double]’:
stan/lib/stan_math/lib/boost_1.87.0/boost/math/tools/fraction.hpp:259:15: note: parameter passing for argument of type ‘std::pair<long double, long double>’ when C++17 is enabled changed to match C++14 in GCC 10.1
  259 |    value_type v = g();
      |               ^
Start sampling
Running MCMC with 4 sequential chains...

Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.5 seconds.
summary(pois_fit_brm)
 Family: poisson 
  Links: mu = log 
Formula: y ~ 1 
   Data: pois_df (Number of observations: 100) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     2.50      0.03     2.44     2.56 1.00     1650     1850

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).

6 Linear model

The simplest multiple linear regression model is the following, with two predictors (\(x_1\) and \(x_2\)), two slopes (\(\beta_1\) and \(\beta_2\)) and intercept coefficient (\(\beta_0\)), and normally distributed noise (\(\sigma\)). This model can be written using standard regression notation as

\[ y_i \sim N(\mu_i = \beta_0 + \beta_1 x_{1i} + \beta_2 x_{2i}, \sigma) \]

or

\[ y_i \sim N(\mu_i = \boldsymbol{\beta \cdot x_i}, \sigma) \]

where \(\boldsymbol{\beta}\) is a 1 \(\times\) 3 coefficient matrix (or row vector) and \(\boldsymbol{x_i}\) is a 3 \(\times\) 1 predictor matrix (including the intercept). We use \(\boldsymbol{x}\) (3 \(\times\) N) to compute the \(\mu\) vector with length \(N\).

Q: What are the posterior means and 95% credible intervals of the coefficients (\(\beta_0\), \(\beta_1\), \(\beta_2\))?

set.seed(123)
n <- 100
beta <- c(2, 1.2, -0.8)
x1 <- rnorm(n, mean = 0, sd = 1)
x2 <- rnorm(n, mean = 0, sd = 1)
sigma <- 0.4
y <- rnorm(n, mean = beta[1] + beta[2] * x1 + beta[3] * x2, sd = sigma)
lm_list <- list(
  y = y,
  N = length(y),
  x = rbind(1, x1, x2) # 3 x N matrix
)

lm_df <- tibble(
  y = y,
  N = length(y),
  x1 = x1,
  x2 = x2
)

6.1 CmdStan

stan/lm.stan

 data {
  int<lower=0> N;
  vector[N] y;
  matrix[3, N] x;
}

parameters {
  row_vector[3] beta;
  real<lower=0> sigma;
}

model {
  y ~ normal(beta * x, sigma);
  beta ~ normal(0, 2.5);
  sigma ~ cauchy(0, 2.5);
} 
lm_mod <- cmdstan_model("stan/lm.stan")

lm_fit <- lm_mod$sample(
  data = lm_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  refresh = 0 # don't print update
)
Running MCMC with 4 parallel chains...

Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.2 seconds.

Use posterior::summarise_draws to check more detailed summary statistics.

my_summary <- function(draws, probs = c(0.025, 0.05, 0.25, 0.5, 0.75, 0.95, 0.975)) {
  posterior::summarise_draws(
    draws,
    mean = ~mean(.x),
    sd   = ~sd(.x),
    ~posterior::quantile2(.x, probs = probs)
  )
}

my_summary(lm_fit$draws())
# A tibble: 5 × 10
  variable   mean     sd   q2.5     q5    q25    q50    q75    q95  q97.5
  <chr>     <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1 lp__     44.6   1.43   41.0   41.9   43.9   44.9   45.6   46.2   46.4  
2 beta[1]   2.05  0.0393  1.98   1.99   2.03   2.05   2.08   2.12   2.13 
3 beta[2]   1.15  0.0417  1.06   1.08   1.12   1.15   1.17   1.21   1.23 
4 beta[3]  -0.791 0.0410 -0.869 -0.857 -0.819 -0.791 -0.763 -0.724 -0.708
5 sigma     0.385 0.0285  0.333  0.341  0.365  0.383  0.403  0.433  0.444

6.2 brms

lm_fit_brm <- brm(
  y ~ x1 + x2,
  family = gaussian(),
  data = lm_df,
  prior = c(
    prior(normal(0, 2.5), class = "Intercept"),
    prior(normal(0, 2.5), class = "b"),
    prior(cauchy(0, 2.5), class = "sigma")
  ),
  refresh = 0, # don't print update
  backend = "cmdstanr"
)
Running MCMC with 4 sequential chains...

Chain 1 finished in 0.0 seconds.
Chain 2 finished in 0.0 seconds.
Chain 3 finished in 0.0 seconds.
Chain 4 finished in 0.0 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.0 seconds.
Total execution time: 0.5 seconds.
summary(lm_fit_brm)
 Family: gaussian 
  Links: mu = identity 
Formula: y ~ x1 + x2 
   Data: lm_df (Number of observations: 100) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     2.05      0.04     1.98     2.13 1.00     4619     3168
x1            1.15      0.04     1.06     1.23 1.00     4356     2728
x2           -0.79      0.04    -0.87    -0.71 1.00     4392     3066

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.39      0.03     0.33     0.45 1.00     4206     2649

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).

7 Varying intercepts

H: There is a power-law relationship (\(y =\beta_0x^{\beta_1}\)) between tree diameter (DBH) and tree maximum height, and the scaling factor \(\beta_0\) varies among species.

\[ \text{log}\; y_{ij} \sim N(\text{log}\; \beta_{0j} + \beta_1 x_{i}, \sigma) \]

\[ \text{log}\; \beta_{0j} \sim N(\mu_0, \tau) \]

\[ \mu_0 \sim N(0, 2.5) \]

\[ \tau \sim \text{Half-Cauchy}(0, 1) \]

Q: What are the species-level scaling factors (\(\beta_{0j}\)) and the global scaling exponent (\(\beta_1\))?

7.1 Dummy data

  • We simulate a dataset with 10 species and 20 trees per species.
  • Each species has wood density (wd) values.
set.seed(12345)

n_sp <- 10
n_rep <- 20
wd <- rnorm(n_sp, 0, 1)
gamma0 <- 0.6
gamma1 <- 0.1
sigma_y <- 0.1

b1_hat <- gamma1 * wd + gamma0
b1 <- rnorm(n_sp, b1_hat, 0.01)
log_b0 <- rnorm(n_sp, 0.55, 0.05)

# ---- simulate ----
allo_df <- tibble(
  sp     = factor(rep(paste0("sp", 1:n_sp), each = n_rep)),
  wd  = rep(wd, each = n_rep),
  # now log_xx ~ Normal(mean log-dbh, sd = 0.5)
  log_xx = rnorm(n_sp * n_rep, mean = log(40), sd = 0.5)) |>
  mutate(
    # add observation-level noise on log-height
    log_y = rnorm(
      n(),
      log_b0[as.integer(sp)] + b1[as.integer(sp)] * log_xx,
      sigma_y),
    dbh = exp(log_xx),
    h = exp(log_y)) |>
  select(sp, wd, dbh, h)

dbh_hist <- allo_df |>
  ggplot(aes(dbh)) +
  geom_histogram() +
  xlab("DBH (cm)") +
  theme(
    legend.position = "none",
    plot.title = element_text(size = 24)
    )

h_hist <- allo_df |>
  ggplot(aes(h)) +
  geom_histogram() +
  xlab("Height (m)") +
  theme(
    legend.position = "none",
    plot.title = element_text(size = 24)
    )

p1 <- allo_df |>
  ggplot(aes(x = dbh, y = h, col = sp)) +
  geom_point() +
  xlab("DBH (cm)") +
  ylab(expression("Height (m)")) +
  theme(
    legend.position = "none",
    plot.title = element_text(size = 24)
    )

p2 <- p1 +
  scale_x_log10() +
  scale_y_log10()


dbh_hist + h_hist + p1 + p2

allo_list <- list(
  log_h = log(allo_df$h),
  log_dbh = log(allo_df$dbh),
  sp = as.integer(allo_df$sp),
  N = nrow(allo_df),
  J = allo_df$sp |> unique() |> length()
)

7.2 CmdStan

stan/vint.stan

 data {
  int<lower=1> N;               // total number of observations
  int<lower=1> J;               // number of species
  array[N] int<lower=1, upper=J> sp; // sp index
  vector[N] log_dbh;            // predictor: log(DBH)
  vector[N] log_h;              // response: log(height)
}

parameters {
  real        mu0;         // grand mean of log(mu₀)
  real<lower=0> tau;            // SD of species intercepts
  vector[J]   log_beta0;            // species‐level intercepts = log(β₀ⱼ)
  real        beta1;            // common slope on log(DBH)
  real<lower=0> sigma;          // residual SD
}

model {
  // Likelihood
  log_h ~ normal(log_beta0[sp] + beta1 * log_dbh, sigma);

  // Priors
  mu0 ~ normal(0, 2.5);
  tau      ~ cauchy(0, 1);
  log_beta0  ~ normal(mu0, tau);
  beta1    ~ normal(0, 2.5);
  sigma    ~ cauchy(0, 1);
}

// we need this for model selection
generated quantities {
  vector[N] log_lik;
  for (i in 1:N)
    log_lik[i] = normal_lpdf(log_h[i]
                    | log_beta0[sp[i]] + beta1 * log_dbh[i],
                      sigma);
} 
vint_mod <- cmdstan_model("stan/vint.stan")

vint_fit <- vint_mod$sample(
  data = allo_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  refresh = 0 # don't print update
)
Running MCMC with 4 parallel chains...
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: normal_lpdf: Scale parameter is 0, but must be positive! (in '/tmp/Rtmp10tuT6/model-23ced1a250.stan', line 19, column 2 to column 57)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: normal_lpdf: Scale parameter is 0, but must be positive! (in '/tmp/Rtmp10tuT6/model-23ced1a250.stan', line 19, column 2 to column 57)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: normal_lpdf: Scale parameter is 0, but must be positive! (in '/tmp/Rtmp10tuT6/model-23ced1a250.stan', line 19, column 2 to column 57)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 1 finished in 0.3 seconds.
Chain 2 finished in 0.3 seconds.
Chain 3 finished in 0.3 seconds.
Chain 4 finished in 0.3 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.3 seconds.
Total execution time: 0.4 seconds.
#vint_fit$summary()
my_summary(vint_fit$draws())
# A tibble: 215 × 10
   variable        mean     sd    q2.5      q5      q25      q50     q75     q95
   <chr>          <dbl>  <dbl>   <dbl>   <dbl>    <dbl>    <dbl>   <dbl>   <dbl>
 1 lp__        365.     2.71   359.    360.    364.     366.     3.67e+2 3.69e+2
 2 mu0           0.479  0.121    0.239   0.280   0.403    0.479  5.54e-1 6.68e-1
 3 tau           0.333  0.0909   0.208   0.220   0.269    0.315  3.79e-1 5.05e-1
 4 log_beta0[…   0.779  0.0565   0.671   0.686   0.740    0.778  8.16e-1 8.74e-1
 5 log_beta0[…   0.918  0.0542   0.812   0.830   0.880    0.916  9.55e-1 1.01e+0
 6 log_beta0[…   0.436  0.0554   0.331   0.345   0.398    0.436  4.74e-1 5.28e-1
 7 log_beta0[…   0.261  0.0582   0.150   0.166   0.222    0.261  3.01e-1 3.58e-1
 8 log_beta0[…   0.617  0.0534   0.514   0.529   0.580    0.617  6.53e-1 7.06e-1
 9 log_beta0[…  -0.0183 0.0546  -0.124  -0.107  -0.0563  -0.0185 1.93e-2 7.34e-2
10 log_beta0[…   0.707  0.0534   0.605   0.622   0.669    0.706  7.43e-1 7.96e-1
# ℹ 205 more rows
# ℹ 1 more variable: q97.5 <dbl>
vint_fit$diagnostic_summary()
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

$ebfmi
[1] 1.0810518 0.9796202 1.0400008 0.9993418

7.3 brms

priors <- c(
  prior(normal(0, 2.5), class = Intercept),
  prior(normal(0, 2.5),   class = b),
  prior(cauchy(0, 1),   class = sd),
  prior(cauchy(0, 1),   class = sigma)
)

vint_fit_brm <- brm(
  log(h) ~ log(dbh) + (1 | sp),
  family = gaussian(),
  data = allo_df,
  prior = priors,
  seed = 123,
  refresh = 0, # don't print update
  backend = "cmdstanr"
)
Running MCMC with 4 sequential chains...

Chain 1 finished in 0.7 seconds.
Chain 2 finished in 0.7 seconds.
Chain 3 finished in 0.7 seconds.
Chain 4 finished in 0.6 seconds.

All 4 chains finished successfully.
Mean chain execution time: 0.7 seconds.
Total execution time: 2.9 seconds.
summary(vint_fit_brm)
 Family: gaussian 
  Links: mu = identity 
Formula: log(h) ~ log(dbh) + (1 | sp) 
   Data: allo_df (Number of observations: 200) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~sp (Number of levels: 10) 
              Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)     0.34      0.10     0.21     0.58 1.00      590     1242

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     0.48      0.12     0.25     0.71 1.00      897     1238
logdbh        0.61      0.01     0.58     0.64 1.00     2371     2261

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.10      0.01     0.09     0.11 1.00     2165     2143

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
plot(vint_fit_brm)

7.4 lme4

vint_fit_lme <- lme4::lmer(
  log(h) ~ log(dbh) + (1 | sp),
  data = allo_df)

summary(vint_fit_lme)
Linear mixed model fit by REML ['lmerMod']
Formula: log(h) ~ log(dbh) + (1 | sp)
   Data: allo_df

REML criterion at convergence: -298.3

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-2.66966 -0.62316  0.04254  0.60122  2.28315 

Random effects:
 Groups   Name        Variance Std.Dev.
 sp       (Intercept) 0.084434 0.29057 
 Residual             0.009794 0.09897 
Number of obs: 200, groups:  sp, 10

Fixed effects:
            Estimate Std. Error t value
(Intercept)  0.47916    0.10454   4.584
log(dbh)     0.61154    0.01314  46.525

Correlation of Fixed Effects:
         (Intr)
log(dbh) -0.472

7.5 Visualization and predictions

7.5.1 CmdStan

# Predicted DBH-height curves per species with 95% intervals
pred_grid <- allo_df |>
  dplyr::group_by(sp) |>
  dplyr::summarise(
    dbh = list(seq(min(dbh), max(dbh), length.out = 60)),
    .groups = "drop"
  ) |>
  tidyr::unnest(dbh)

cmd_draws <- posterior::as_draws_matrix(vint_fit$draws(c("beta1", "log_beta0")))
beta1_draws <- cmd_draws[, "beta1"]
log_beta0_cols <- grep("^log_beta0\\[", colnames(cmd_draws), value = TRUE)
species_levels <- levels(allo_df$sp)

DBH for predicted lines for each species.

pred_grid
# A tibble: 600 × 2
   sp      dbh
   <fct> <dbl>
 1 sp1    12.2
 2 sp1    14.0
 3 sp1    15.8
 4 sp1    17.6
 5 sp1    19.5
 6 sp1    21.3
 7 sp1    23.1
 8 sp1    25.0
 9 sp1    26.8
10 sp1    28.6
# ℹ 590 more rows

Posterior draws of the slope parameter (\(\beta_1\)).

str(beta1_draws)
 'draws_matrix' num [1:4000, 1] 0.615 0.614 0.598 0.609 0.61 ...
 - attr(*, "dimnames")=List of 2
  ..$ draw    : chr [1:4000] "1" "2" "3" "4" ...
  ..$ variable: chr "beta1"
 - attr(*, "nchains")= int 4

Column names for log-transformed species-level intercepts (\(\log \beta_{0j}\)). We use this to extract posterior draws for each species.

str(log_beta0_cols)
 chr [1:10] "log_beta0[1]" "log_beta0[2]" "log_beta0[3]" "log_beta0[4]" ...
pred_cmd_summary <- dplyr::bind_cols(
  pred_grid,
  purrr::map2_dfr(
    match(pred_grid$sp, species_levels),
    log(pred_grid$dbh),
    ~{
      log_mu <- cmd_draws[, log_beta0_cols[.x]] + beta1_draws * .y
      height_draws <- exp(log_mu)
      tibble::tibble(
        estimate = stats::median(height_draws),
        lower = stats::quantile(height_draws, 0.025),
        upper = stats::quantile(height_draws, 0.975)
      )
    }
  )
)

Compute predicted height summary for each species and DBH.

pred_cmd_summary
# A tibble: 600 × 5
   sp      dbh estimate lower upper
   <fct> <dbl>    <dbl> <dbl> <dbl>
 1 sp1    12.2     10.0  9.50  10.7
 2 sp1    14.0     10.9 10.4   11.6
 3 sp1    15.8     11.8 11.2   12.5
 4 sp1    17.6     12.6 12.0   13.3
 5 sp1    19.5     13.4 12.8   14.1
 6 sp1    21.3     14.2 13.5   14.9
 7 sp1    23.1     14.9 14.2   15.6
 8 sp1    25.0     15.6 14.9   16.4
 9 sp1    26.8     16.3 15.6   17.1
10 sp1    28.6     17.0 16.2   17.8
# ℹ 590 more rows
ggplot(pred_cmd_summary, aes(dbh, estimate, colour = sp)) +
  geom_ribbon(aes(ymin = lower, ymax = upper, fill = sp), alpha = 0.15, colour = NA) +
  geom_line() +
  geom_point(
    data = allo_df,
    aes(dbh, h, colour = sp),
    size = 1,
    alpha = 0.6,
    inherit.aes = FALSE
  ) +
  scale_x_log10() +
  scale_y_log10() +
  guides(fill = "none") +
  labs(
    x = "DBH (cm)",
    y = "Height (m)",
    colour = "Species"
  )

7.5.2 brms

With fitted() available, preparing pred_summary is simpler than in the CmdStan workflow.

pred_summary <- fitted(vint_fit_brm, newdata = pred_grid, re_formula = NULL) |>
  tibble::as_tibble() |>
  dplyr::bind_cols(pred_grid) |>
  dplyr::mutate(
    estimate = exp(Estimate),
    lower = exp(Q2.5),
    upper = exp(Q97.5)
  )

ggplot(pred_summary, aes(dbh, estimate, color = sp)) +
  geom_ribbon(aes(ymin = lower, ymax = upper, fill = sp), alpha = 0.15, colour = NA) +
  geom_line() +
  geom_point(
    data = allo_df,
    aes(dbh, h, colour = sp),
    size = 1,
    alpha = 0.6,
    inherit.aes = FALSE
  ) +
  scale_x_log10() +
  scale_y_log10() +
  guides(fill = "none") +
  labs(
    x = "DBH (cm)",
    y = "Height (m)",
    colour = "Species"
  )

8 Reparameterization for multilevel models

Diagnosing Biased Inference with Divergences by Michael Betancourt

8.1 Eight schools example

What is the effect of the treatment (a new education method) on students’ scores for each school and across schools?

school y sigma
School_1 28 15
School_2 8 10
School_3 -3 16
School_4 7 11
School_5 -1 9
School_6 1 11
School_7 18 10
School_8 12 18

8.2 Centered parameterization

\[ y_j \sim \mathcal{N}\bigl(\theta_j,\;\sigma_j\bigr) \]

\[ \theta_j \sim \mathcal{N}\bigl(\mu,\;\tau \bigr) \]

\[ \mu \sim \mathcal{N}\bigl(0,\;5 \bigr) \]

\[ \tau \sim \text{Half-Cauchy}(0, 2.5) \]

Note: \(\sigma_j\) is known constant (i.e., data), we don’t have to estimate it.

This parameterization is intuitive, but it often leads to convergence issues in hierarchical models.

8.3 Non-centered parameterization

\[ \tilde{\theta_j} \sim \mathcal{N}\bigl(0,\;1 \bigr) \]

\[ \theta_j = \mu + \tau \cdot \tilde{\theta_j} \]

In this parameterization, we introduce a new latent variable \(\tilde{\theta_j}\) that is independent of the other parameters. This allows the sampler to explore the posterior more efficiently and avoids problems with convergence. We often use this parameterization when we have hierarchical models with varying intercepts or slopes.

9 Varying slopes and intercepts

H: There is a power-law relationship (\(y =\beta_0 x^{\beta_1}\)) between tree diameter (DBH) and tree maximum height, and both the scaling factor \(\beta_0\) and the exponent \(\beta_1\) vary among species.

\[ \text{log}\; y_{ij} \sim N(\text{log}\; \beta_{0j} + \beta_{1j} x_{ij}, \sigma) \]

\[ \begin{bmatrix} \text{log}\; \beta_{0j} \\ \beta_{1j} \end{bmatrix} \sim \mathrm{MVN} \Biggl( \boldsymbol{\mu} = \begin{bmatrix} \mu_{0} \\ \mu_{1} \end{bmatrix}, \boldsymbol{\Sigma} = \begin{bmatrix} \sigma_{0}^2 & \rho \sigma_{0} \sigma_{1} \\ \rho \sigma_{0} \sigma_{1} & \sigma_{1}^2 \end{bmatrix} \Biggr) \]

For reference, the multivariate normal (MVN) distribution looks like this:

Q: What are the species-level scaling factors (\(\beta_{0j}\)) and scaling exponent (\(\beta_{0j}\))? What are the global scaling exponent (\(\mu_0\)) and scaling exponent (\(\mu_1\))?

9.1 Centered parameterization

\[ \boldsymbol{\mu} \sim N(0, 2.5) \]

\[ \boldsymbol{\Sigma} \sim \mathcal{W}^{-1}_p(\nu,\,\Psi) \]

The inverse-whishart distribution \(\mathcal{W}^{-1}\) is a conjugate for the covariance matrix \(\boldsymbol{\Sigma}\) in MVN. However, despite conjugacy, inverse–Wishart priors are often computationally difficult to sample.

Recommendation: use a Cholesky factorization with independent priors on scales and correlation (below).

9.2 Non-Centered parameterization

Optimization through Cholesky factorization

\[ \boldsymbol{z}_j \sim \mathcal{N}(\mathbf0,\,I_2) \] \[ \begin{pmatrix}\log\beta_{0j}\\[3pt]\beta_{1j}\end{pmatrix} = \boldsymbol{\mu} + L\, \boldsymbol{z}_j \]

Some linear algebra (Cholesky decomposition):

\[ \Sigma \;=\;L\,L^\top \;=\;\bigl[\mathrm{diag}(\tau)\,L_\Omega\bigr]\, \bigl[L_\Omega^\top\,\mathrm{diag}(\tau)\bigr] \;=\;\mathrm{diag}(\tau)\,\Omega\,\mathrm{diag}(\tau). \]

\[ L_\Omega \sim \mathrm{LKJ\_Corr\_Cholesky}(2) \quad \text{or} \quad \Omega \sim \mathrm{LKJ\_Corr}(2) \] \[ \tau_i \sim \mathrm{Half\text{-}Cauchy}(0,1) \]

Rather than sampling the covariance matrix \(\Sigma\) or correlation matrix \(\Omega\) directly, sampling the Cholesky factor \(L_\Omega\) is computationally more efficient and numerically stable.

In the LKJ() prior:

  • \(\eta = 1\) indicates a flat prior (uniform distribution) over correlation matrices.

  • \(\eta > 1\) indicates weaker correlations.

We usually use \(\eta = 2\) because we assume at least weak correlations among the parameters while still allowing moderate to strong correlations.

9.3 CmdStan

stan/vslope.stan

 data {
  int<lower=0> N;              // num individuals
  int<lower=1> K;              // num ind predictors
  int<lower=1> J;              // num groups
  array[N] int<lower=1, upper=J> jj;  // group for individual
  int<lower=1> L;              // number of group-level predictors
  matrix[N, K] x;              // individual predictors
  matrix[L, J] u;              // group predictors
  vector[N] y;                 // outcomes
}

parameters {
  matrix[K, L] gamma;          // coefficients across groups
  real<lower=0> sigma;         // prediction error scale
  vector<lower=0>[K] tau;      // prior scale
  matrix[K, J] z;
  cholesky_factor_corr[K] L_Omega;
}

transformed parameters {
  matrix[K, J] beta;
  // mu = gamma * u
  beta = gamma * u + diag_pre_multiply(tau, L_Omega) * z;
}

model {
  to_vector(z) ~ std_normal();
  tau ~ cauchy(0, 2.5);
  L_Omega ~ lkj_corr_cholesky(2);
  to_vector(gamma) ~ normal(0, 2.5);
  for (n in 1:N) {
    y[n] ~ normal(x[n, ] * beta[, jj[n]], sigma);
  }
} 
allo_vslope_list <- list(
  y = log(allo_df$h),
  x = cbind(1, log(allo_df$dbh)),
  jj = as.integer(allo_df$sp),
  N = nrow(allo_df),
  K = 2, # number of predictors (intercept + slope)
  J = allo_df$sp |> unique() |> length(),
  L = 1 # number of group-level predictors (intercept)
)
allo_vslope_list$u <- matrix(1, nrow = allo_vslope_list$L, ncol = allo_vslope_list$J)
vslope_mod <- cmdstan_model("stan/vslope.stan")

vslope_fit <- vslope_mod$sample(
  data = allo_vslope_list,
  seed = 123,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  adapt_delta = 0.99, # increase adapt_delta to avoid divergent transitions
  max_treedepth = 15, # increase max_treedepth to avoid max treedepth errors
  refresh = 0, # don't print update
  show_messages = FALSE   # suppress Stan’s “Informational Message” output
)
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: normal_lpdf: Scale parameter is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 32, column 4 to column 49)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
my_summary(vslope_fit$draws())
# A tibble: 50 × 10
   variable      mean      sd     q2.5       q5      q25     q50     q75     q95
   <chr>        <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl>   <dbl>   <dbl>
 1 lp__       3.60e+2 4.39     3.50e+2  3.52e+2 357.     3.60e+2 3.63e+2 366.   
 2 gamma[1,1] 5.01e-1 0.0555   3.91e-1  4.10e-1   0.465  5.02e-1 5.38e-1   0.591
 3 gamma[2,1] 6.07e-1 0.0313   5.47e-1  5.56e-1   0.587  6.06e-1 6.26e-1   0.660
 4 sigma      9.24e-2 0.00481  8.37e-2  8.49e-2   0.0889 9.21e-2 9.55e-2   0.101
 5 tau[1]     7.10e-2 0.0570   2.34e-3  4.79e-3   0.0278 5.94e-2 9.96e-2   0.181
 6 tau[2]     8.75e-2 0.0290   4.73e-2  5.14e-2   0.0679 8.24e-2 1.01e-1   0.142
 7 z[1,1]     6.05e-1 0.895   -1.27e+0 -9.15e-1   0.0410 6.46e-1 1.23e+0   2.01 
 8 z[2,1]     6.61e-1 0.653   -7.88e-1 -5.21e-1   0.286  7.03e-1 1.09e+0   1.65 
 9 z[1,2]     1.95e-1 0.975   -1.83e+0 -1.50e+0  -0.441  2.23e-1 8.80e-1   1.73 
10 z[2,2]     1.30e+0 0.658   -4.72e-2  1.93e-1   0.900  1.32e+0 1.73e+0   2.33 
# ℹ 40 more rows
# ℹ 1 more variable: q97.5 <dbl>
vslope_fit$diagnostic_summary()
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

$ebfmi
[1] 0.8136123 0.8535260 0.8342642 0.7602623

9.4 brms

priors <- c(
  prior(normal(0, 2.5), class = Intercept),
  prior(normal(0, 2.5),   class = b),
  prior(lkj(2), class = cor),                # Ω ~ LKJ(2)
  prior(cauchy(0, 2.5), class = sigma)
)

slope_fit_brm <- brm(
  log(h) ~ log(dbh) + (1 + log(dbh) | sp),
  family = gaussian(),
  data = allo_df,
  prior = priors,
  refresh = 0, # don't print update
  seed = 123,
  chains = 4, cores = 4,
  control = list(
    adapt_delta  = 0.99,  # default is 0.8
    max_treedepth = 15    # default is 10
  ),
  backend = "cmdstanr"
)
Running MCMC with 4 parallel chains...

Chain 1 finished in 7.2 seconds.
Chain 4 finished in 9.3 seconds.
Chain 3 finished in 10.0 seconds.
Chain 2 finished in 10.3 seconds.

All 4 chains finished successfully.
Mean chain execution time: 9.2 seconds.
Total execution time: 10.4 seconds.
summary(slope_fit_brm)
 Family: gaussian 
  Links: mu = identity 
Formula: log(h) ~ log(dbh) + (1 + log(dbh) | sp) 
   Data: allo_df (Number of observations: 200) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~sp (Number of levels: 10) 
                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
sd(Intercept)             0.07      0.06     0.00     0.21 1.00     1853
sd(logdbh)                0.09      0.03     0.05     0.16 1.00     1320
cor(Intercept,logdbh)     0.09      0.42    -0.74     0.84 1.00      672
                      Tail_ESS
sd(Intercept)             1523
sd(logdbh)                1835
cor(Intercept,logdbh)     1251

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     0.50      0.05     0.40     0.61 1.00     3410     3243
logdbh        0.60      0.03     0.54     0.66 1.00     1362     1765

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.09      0.00     0.08     0.10 1.00     6219     2878

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).

9.5 lme4

slope_fit_lme <- lme4::lmer(
  log(h) ~ log(dbh) + (1 + log(dbh) | sp),
  data = allo_df)
boundary (singular) fit: see help('isSingular')
summary(slope_fit_lme)
Linear mixed model fit by REML ['lmerMod']
Formula: log(h) ~ log(dbh) + (1 + log(dbh) | sp)
   Data: allo_df

REML criterion at convergence: -327.2

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-3.14307 -0.63987  0.08714  0.61696  3.03735 

Random effects:
 Groups   Name        Variance Std.Dev. Corr
 sp       (Intercept) 0.001326 0.03641      
          log(dbh)    0.004607 0.06787  1.00
 Residual             0.008397 0.09164      
Number of obs: 200, groups:  sp, 10

Fixed effects:
            Estimate Std. Error t value
(Intercept)  0.49980    0.04759   10.50
log(dbh)     0.60637    0.02468   24.57

Correlation of Fixed Effects:
         (Intr)
log(dbh) -0.264
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')

9.6 Visualization

Almost the same as before, but now we have varying slopes as well as intercepts.

9.6.1 CmdStan

vslope_matrix <- posterior::as_draws_matrix(vslope_fit$draws("beta"))
intercept_cols <- grep("^beta\\[1,", colnames(vslope_matrix), value = TRUE)
slope_cols <- grep("^beta\\[2,", colnames(vslope_matrix), value = TRUE)

str(vslope_matrix)
 'draws_matrix' num [1:4000, 1:20] 0.749 0.482 0.469 0.633 0.688 ...
 - attr(*, "dimnames")=List of 2
  ..$ draw    : chr [1:4000] "1" "2" "3" "4" ...
  ..$ variable: chr [1:20] "beta[1,1]" "beta[2,1]" "beta[1,2]" "beta[2,2]" ...
 - attr(*, "nchains")= int 4
str(intercept_cols)
 chr [1:10] "beta[1,1]" "beta[1,2]" "beta[1,3]" "beta[1,4]" "beta[1,5]" ...
str(slope_cols)
 chr [1:10] "beta[2,1]" "beta[2,2]" "beta[2,3]" "beta[2,4]" "beta[2,5]" ...
pred_grid_vslope <- pred_grid |>
  dplyr::mutate(log_dbh = log(dbh))

pred_cmd_summary <- dplyr::bind_cols(
  pred_grid_vslope,
  purrr::map2_dfr(
    match(pred_grid_vslope$sp, species_levels),
    pred_grid_vslope$log_dbh,
    ~{
      intercept_draws <- vslope_matrix[, intercept_cols[.x]]
      slope_draws <- vslope_matrix[, slope_cols[.x]]
      log_mu <- intercept_draws + slope_draws * .y
      height_draws <- exp(log_mu)
      tibble::tibble(
        estimate = stats::median(height_draws),
        lower = stats::quantile(height_draws, 0.025),
        upper = stats::quantile(height_draws, 0.975)
      )
    }
  )
)

ggplot(pred_cmd_summary, aes(dbh, estimate, colour = sp)) +
  geom_ribbon(
    aes(ymin = lower, ymax = upper, fill = sp),
    alpha = 0.15,
    colour = NA
  ) +
  geom_line() +
  geom_point(
    data = allo_df,
    aes(dbh, h, colour = sp),
    size = 1,
    alpha = 0.6,
    inherit.aes = FALSE
  ) +
  scale_x_log10() +
  scale_y_log10() +
  guides(fill = "none", colour = guide_legend(title = "Species")) +
  labs(
    x = "Diameter at breast height (cm)",
    y = "Height (m)",
    title = "Posterior predictive curves from CmdStan model"
  )

9.6.2 brms

pred_brm_summary <- fitted(
  slope_fit_brm,
  newdata = pred_grid,
  re_formula = NULL
) |>
  tibble::as_tibble() |>
  dplyr::bind_cols(pred_grid) |>
  dplyr::mutate(
    estimate = exp(Estimate),
    lower = exp(Q2.5),
    upper = exp(Q97.5)
  )

ggplot(pred_brm_summary, aes(dbh, estimate, colour = sp)) +
  geom_ribbon(
    aes(ymin = lower, ymax = upper, fill = sp),
    alpha = 0.15,
    colour = NA
  ) +
  geom_line() +
  geom_point(
    data = allo_df,
    aes(dbh, h, colour = sp),
    size = 1,
    alpha = 0.6,
    inherit.aes = FALSE
  ) +
  scale_x_log10() +
  scale_y_log10() +
  guides(fill = "none", colour = guide_legend(title = "Species")) +
  labs(
    x = "Diameter at breast height (cm)",
    y = "Height (m)",
    title = "Posterior predictive curves from brms model"
  )

10 Varying slopes and intercepts, and group-level predictors

H: There is a power-law relationship (\(y =\beta_0 x^{\beta_1}\)) between tree diameter (DBH) and tree maximum height, and both the scaling factor \(\beta_0\) and the exponent \(\beta_1\) vary among species. Those species-level variations can be explained by wood density (group-level predictor: \(u\)) of each species.

\[ \text{log}\; y_{ij} \sim N(\text{log}\; \beta_{0j} + \beta_{1j} x_{ij}, \sigma) \]

\[ \begin{bmatrix} \text{log}\; \beta_{0j} \\ \beta_{1j} \end{bmatrix} \sim \mathrm{MVN} \Biggl( \underbrace{ \boldsymbol{\mu} = \begin{bmatrix} \gamma_{0}^{(\beta_0)} + \gamma_{1}^{(\beta_0)}u_{j} \\ \gamma_{0}^{(\beta_1)} + \gamma_{1}^{(\beta_1)}u_{j} \end{bmatrix}}_{\text{mean depends on }u_j}, \boldsymbol{\Sigma} = \begin{bmatrix} \sigma_{0}^2 & \rho \sigma_{0} \sigma_{1} \\ \rho \sigma_{0} \sigma_{1} & \sigma_{1}^2 \end{bmatrix} \Biggr) \]

Q: What are the coefficients for species-level regressions (\(\gamma_{0}^{(\beta_0)}\), \(\gamma_{1}^{(\beta_0}\), \(\gamma_{0}^{(\beta_1)}\), and \(\gamma_{1}^{(\beta_1}\))?

10.1 CmdStan

Now we have two group-level predictors (intercept and slope), and \(u\) includes the intercept and wood density. The rest of this section follows the setup from the varying slopes and intercepts section.

allo_vslope_list$L <- 2

allo_vslope_list$u <- rbind(
  rep(1, allo_vslope_list$J),
  trait
)
str(allo_vslope_list)
List of 8
 $ y : num [1:200] 3.29 3.66 3.7 3.58 3.19 ...
 $ x : num [1:200, 1:2] 1 1 1 1 1 1 1 1 1 1 ...
 $ jj: int [1:200] 1 1 1 1 1 1 1 1 1 1 ...
 $ N : int 200
 $ K : num 2
 $ J : int 10
 $ L : num 2
 $ u : num [1:2, 1:10] 1 0.586 1 0.709 1 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:2] "" "trait"
  .. ..$ : NULL
vgrp_fit <- vslope_mod$sample(
  data = allo_vslope_list,
  seed = 1234,
  chains = 4,
  parallel_chains = 4,
  iter_warmup = 1000, # number of warmup iterations
  iter_sampling = 1000, # number of sampling iterations
  adapt_delta = 0.95, # increase adapt_delta to avoid divergent transitions
  max_treedepth = 15, # increase max_treedepth to avoid max treedepth errors
  refresh = 0 # don't print update
)
Running MCMC with 4 parallel chains...
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 1 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 1 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 1 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 1 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 1 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 2 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 2 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 2 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 2 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 2 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 3 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 3 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 3 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 3 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 3 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 Informational Message: The current Metropolis proposal is about to be rejected because of the following issue:
Chain 4 Exception: lkj_corr_cholesky_lpdf: Random variable[2] is 0, but must be positive! (in '/tmp/Rtmpg5kVA9/model-b56e6f41d818.stan', line 29, column 2 to column 33)
Chain 4 If this warning occurs sporadically, such as for highly constrained variable types like covariance matrices, then the sampler is fine,
Chain 4 but if this warning occurs often then your model may be either severely ill-conditioned or misspecified.
Chain 4 
Chain 4 finished in 6.3 seconds.
Chain 1 finished in 6.7 seconds.
Chain 3 finished in 6.7 seconds.
Chain 2 finished in 7.6 seconds.

All 4 chains finished successfully.
Mean chain execution time: 6.8 seconds.
Total execution time: 7.6 seconds.
my_summary(vgrp_fit$draws())
# A tibble: 52 × 10
   variable      mean      sd     q2.5       q5      q25     q50     q75     q95
   <chr>        <dbl>   <dbl>    <dbl>    <dbl>    <dbl>   <dbl>   <dbl>   <dbl>
 1 lp__       3.58e+2 4.48     3.48e+2  3.50e+2  3.55e+2 3.58e+2 3.61e+2 3.65e+2
 2 gamma[1,1] 5.02e-1 0.0551   3.91e-1  4.14e-1  4.66e-1 5.02e-1 5.39e-1 5.92e-1
 3 gamma[2,1] 6.18e-1 0.0154   5.87e-1  5.93e-1  6.08e-1 6.18e-1 6.28e-1 6.43e-1
 4 gamma[1,2] 4.35e-2 0.0660  -8.68e-2 -6.03e-2  4.12e-4 4.31e-2 8.54e-2 1.54e-1
 5 gamma[2,2] 8.01e-2 0.0190   4.29e-2  4.89e-2  6.79e-2 8.04e-2 9.24e-2 1.11e-1
 6 sigma      9.26e-2 0.00476  8.37e-2  8.51e-2  8.94e-2 9.24e-2 9.56e-2 1.01e-1
 7 tau[1]     6.70e-2 0.0486   2.96e-3  5.76e-3  2.92e-2 6.04e-2 9.40e-2 1.55e-1
 8 tau[2]     2.39e-2 0.0131   1.81e-3  3.96e-3  1.55e-2 2.28e-2 3.07e-2 4.66e-2
 9 z[1,1]     4.33e-1 0.809   -1.27e+0 -9.48e-1 -5.01e-2 4.40e-1 9.42e-1 1.75e+0
10 z[2,1]     1.46e-1 0.756   -1.45e+0 -1.18e+0 -3.06e-1 1.65e-1 6.41e-1 1.32e+0
# ℹ 42 more rows
# ℹ 1 more variable: q97.5 <dbl>
vgrp_fit$diagnostic_summary()
$num_divergent
[1] 0 0 0 0

$num_max_treedepth
[1] 0 0 0 0

$ebfmi
[1] 0.7879778 0.8287131 0.8046688 0.7915153

10.2 brms

priors <- c(
  prior(normal(0, 2.5), class = Intercept),
  prior(normal(0, 2.5), class = b),
  prior(lkj(2), class = cor),                # Ω ~ LKJ(2)
  prior(cauchy(0, 2.5), class = sigma)
)
vgrp_fit_brm <- brm(
  log(h) ~ log(dbh) * wd + (1 + log(dbh) | sp),
  family = gaussian(),
  data = allo_df,
  prior = priors,
  refresh = 0, # don't print update
  seed = 1234,
  chains = 4, cores = 4,
  control = list(
    adapt_delta  = 0.95,  # default is 0.8
    max_treedepth = 15    # default is 10
  ),
  backend = "cmdstanr"
)
Start sampling
Running MCMC with 4 parallel chains...

Chain 1 finished in 6.2 seconds.
Chain 4 finished in 6.7 seconds.
Chain 3 finished in 6.8 seconds.
Chain 2 finished in 7.3 seconds.

All 4 chains finished successfully.
Mean chain execution time: 6.7 seconds.
Total execution time: 7.3 seconds.
Warning: 1 of 4000 (0.0%) transitions ended with a divergence.
See https://mc-stan.org/misc/warnings for details.

Divergent transitions! We need to increase adapt_delta and max_treedepth to avoid divergent transitions. We may also need to change the priors. Reparameterization is less straightforward in brms than writing your own Stan code.

summary(vgrp_fit_brm)
Warning: There were 1 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
 Family: gaussian 
  Links: mu = identity 
Formula: log(h) ~ log(dbh) * wd + (1 + log(dbh) | sp) 
   Data: allo_df (Number of observations: 200) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Multilevel Hyperparameters:
~sp (Number of levels: 10) 
                      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
sd(Intercept)             0.09      0.07     0.00     0.25 1.00     1710
sd(logdbh)                0.08      0.03     0.04     0.15 1.00     1533
cor(Intercept,logdbh)     0.15      0.43    -0.72     0.85 1.00     1018
                      Tail_ESS
sd(Intercept)             1911
sd(logdbh)                2119
cor(Intercept,logdbh)     1332

Regression Coefficients:
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept     0.50      0.06     0.39     0.62 1.00     3881     2832
logdbh        0.60      0.03     0.54     0.66 1.00     1778     2069
wd            0.02      0.07    -0.12     0.17 1.00     3192     2064
logdbh:wd    -0.05      0.04    -0.13     0.02 1.00     2444     2240

Further Distributional Parameters:
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     0.09      0.00     0.08     0.10 1.00     5598     3118

Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).

10.3 lme4

grp_fit_lme <- lme4::lmer(
  log(h) ~ log(dbh) * wd + (1 + log(dbh) | sp),
  data = allo_df)
boundary (singular) fit: see help('isSingular')

Not convergent! It’s hard to fit this kind of complex multilevel model with MLE framework like lme4.

11 References

devtools::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.1 (2025-06-13)
 os       Ubuntu 24.04.3 LTS
 system   aarch64, linux-gnu
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Etc/UTC
 date     2025-11-03
 pandoc   3.8.1 @ /usr/bin/ (via rmarkdown)
 quarto   1.7.32 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 ! package        * version  date (UTC) lib source
 P abind            1.4-8    2024-09-12 [?] RSPM
 P backports        1.5.0    2024-05-23 [?] RSPM
 P bayesplot      * 1.14.0   2025-08-31 [?] RSPM (R 4.5.1)
 P boot             1.3-32   2025-08-29 [?] RSPM (R 4.5.1)
 P bridgesampling   1.1-2    2021-04-16 [?] RSPM (R 4.5.1)
 P brms           * 2.23.0   2025-09-09 [?] RSPM (R 4.5.1)
 P Brobdingnag      1.2-9    2022-10-19 [?] RSPM (R 4.5.1)
 P cachem           1.1.0    2024-05-16 [?] RSPM
 P checkmate        2.3.3    2025-08-18 [?] RSPM
 P cli              3.6.5    2025-04-23 [?] RSPM
 P cmdstanr       * 0.9.0    2025-03-30 [?] repository (https://github.com/stan-dev/cmdstanr@da99e2b)
 P coda             0.19-4.1 2024-01-31 [?] RSPM (R 4.5.1)
   codetools        0.2-20   2024-03-31 [3] CRAN (R 4.5.1)
 P data.table       1.17.8   2025-07-10 [?] RSPM
   devtools         2.4.6    2025-10-03 [2] RSPM (R 4.5.0)
 P digest           0.6.37   2024-08-19 [?] RSPM
 P distributional   0.5.0    2024-09-17 [?] RSPM
 P dplyr          * 1.1.4    2023-11-17 [?] RSPM (R 4.5.1)
   ellipsis         0.3.2    2021-04-29 [2] RSPM (R 4.5.0)
 P evaluate         1.0.5    2025-08-27 [?] RSPM
 P farver           2.1.2    2024-05-13 [?] RSPM (R 4.5.1)
 P fastmap          1.2.0    2024-05-15 [?] RSPM
 P forcats        * 1.0.1    2025-09-25 [?] RSPM (R 4.5.1)
 P fs               1.6.6    2025-04-12 [?] RSPM
 P generics         0.1.4    2025-05-09 [?] RSPM
 P ggplot2        * 4.0.0    2025-09-11 [?] RSPM (R 4.5.1)
 P glue             1.8.0    2024-09-30 [?] RSPM
 P gridExtra        2.3      2017-09-09 [?] RSPM (R 4.5.1)
 P gtable           0.3.6    2024-10-25 [?] RSPM (R 4.5.1)
 P hms              1.1.4    2025-10-17 [?] RSPM (R 4.5.1)
 P htmltools        0.5.8.1  2024-04-04 [?] RSPM
   htmlwidgets      1.6.4    2023-12-06 [2] RSPM (R 4.5.0)
 P inline           0.3.21   2025-01-09 [?] RSPM (R 4.5.1)
 P isoband          0.2.7    2022-12-20 [?] RSPM (R 4.5.1)
 P jsonlite         2.0.0    2025-03-27 [?] RSPM
 P kableExtra       1.4.0    2024-01-24 [?] RSPM (R 4.5.1)
 P knitr          * 1.50     2025-03-16 [?] RSPM
 P labeling         0.4.3    2023-08-29 [?] RSPM (R 4.5.1)
   lattice          0.22-7   2025-04-02 [3] CRAN (R 4.5.1)
 P lifecycle        1.0.4    2023-11-07 [?] RSPM
 P lme4             1.1-37   2025-03-26 [?] RSPM (R 4.5.1)
 P loo              2.8.0    2024-07-03 [?] RSPM (R 4.5.1)
 P lubridate      * 1.9.4    2024-12-08 [?] RSPM (R 4.5.1)
 P magrittr         2.0.4    2025-09-12 [?] RSPM
   MASS             7.3-65   2025-02-28 [3] CRAN (R 4.5.1)
 P Matrix           1.7-4    2025-08-28 [?] RSPM (R 4.5.1)
 P matrixStats      1.5.0    2025-01-07 [?] RSPM
 P memoise          2.0.1    2021-11-26 [?] RSPM
   mgcv             1.9-3    2025-04-04 [3] CRAN (R 4.5.1)
 P minqa            1.2.8    2024-08-17 [?] RSPM (R 4.5.1)
 P mvtnorm          1.3-3    2025-01-10 [?] RSPM (R 4.5.1)
   nlme             3.1-168  2025-03-31 [3] CRAN (R 4.5.1)
 P nloptr           2.2.1    2025-03-17 [?] RSPM (R 4.5.1)
 P patchwork      * 1.3.2    2025-08-25 [?] RSPM (R 4.5.1)
 P pillar           1.11.1   2025-09-17 [?] RSPM
 P pkgbuild         1.4.8    2025-05-26 [?] RSPM
 P pkgconfig        2.0.3    2019-09-22 [?] RSPM
   pkgload          1.4.1    2025-09-23 [2] RSPM (R 4.5.0)
 P plyr             1.8.9    2023-10-02 [?] RSPM (R 4.5.1)
 P posterior        1.6.1    2025-02-27 [?] RSPM
 P processx         3.8.6    2025-02-21 [?] RSPM
 P ps               1.9.1    2025-04-12 [?] RSPM
 P purrr          * 1.1.0    2025-07-10 [?] RSPM
 P QuickJSR         1.8.1    2025-09-20 [?] RSPM (R 4.5.1)
 P R6               2.6.1    2025-02-15 [?] RSPM
 P rbibutils        2.3      2024-10-04 [?] RSPM (R 4.5.1)
 P RColorBrewer     1.1-3    2022-04-03 [?] RSPM (R 4.5.1)
 P Rcpp           * 1.1.0    2025-07-02 [?] RSPM
 P RcppParallel     5.1.11-1 2025-08-27 [?] RSPM (R 4.5.1)
 P Rdpack           2.6.4    2025-04-09 [?] RSPM (R 4.5.1)
 P readr          * 2.1.5    2024-01-10 [?] RSPM (R 4.5.1)
 P reformulas       0.4.1    2025-04-30 [?] RSPM (R 4.5.1)
   remotes          2.5.0    2024-03-17 [2] RSPM (R 4.5.0)
   renv             1.1.5    2025-07-24 [1] RSPM (R 4.5.1)
 P reshape2         1.4.4    2020-04-09 [?] RSPM (R 4.5.1)
 P rlang            1.1.6    2025-04-11 [?] RSPM
 P rmarkdown        2.30     2025-09-28 [?] RSPM
 P rstan            2.32.7   2025-03-10 [?] RSPM (R 4.5.1)
 P rstantools       2.5.0    2025-09-01 [?] RSPM (R 4.5.1)
 P rstudioapi       0.17.1   2024-10-22 [?] RSPM
 P S7               0.2.0    2024-11-07 [?] RSPM (R 4.5.1)
 P scales           1.4.0    2025-04-24 [?] RSPM (R 4.5.1)
   sessioninfo      1.2.3    2025-02-05 [2] RSPM (R 4.5.0)
 P StanHeaders      2.32.10  2024-07-15 [?] RSPM (R 4.5.1)
 P stringi          1.8.7    2025-03-27 [?] RSPM
 P stringr        * 1.5.2    2025-09-08 [?] RSPM
 P svglite          2.2.1    2025-05-12 [?] RSPM (R 4.5.1)
 P systemfonts      1.3.1    2025-10-01 [?] RSPM
 P tensorA          0.36.2.1 2023-12-13 [?] RSPM
 P textshaping      1.0.4    2025-10-10 [?] RSPM
 P tibble         * 3.3.0    2025-06-08 [?] RSPM
 P tictoc         * 1.2.1    2024-03-18 [?] RSPM (R 4.5.1)
 P tidyr          * 1.3.1    2024-01-24 [?] RSPM (R 4.5.1)
 P tidyselect       1.2.1    2024-03-11 [?] RSPM (R 4.5.1)
 P tidyverse      * 2.0.0    2023-02-22 [?] RSPM (R 4.5.1)
 P timechange       0.3.0    2024-01-18 [?] RSPM (R 4.5.1)
 P tzdb             0.5.0    2025-03-15 [?] RSPM (R 4.5.1)
   usethis          3.2.1    2025-09-06 [2] RSPM (R 4.5.0)
 P utf8             1.2.6    2025-06-08 [?] RSPM
 P vctrs            0.6.5    2023-12-01 [?] RSPM
 P viridisLite      0.4.2    2023-05-02 [?] RSPM (R 4.5.1)
 P withr            3.0.2    2024-10-28 [?] RSPM
 P xfun             0.54     2025-10-30 [?] RSPM
 P xml2             1.4.1    2025-10-27 [?] RSPM (R 4.5.1)
 P yaml             2.3.10   2024-07-26 [?] RSPM

 [1] /workspaces/afec-bayes-2025/renv/library/linux-ubuntu-noble/R-4.5/aarch64-unknown-linux-gnu
 [2] /usr/local/lib/R/site-library
 [3] /usr/local/lib/R/library

 * ── Packages attached to the search path.
 P ── Loaded and on-disk path mismatch.

──────────────────────────────────────────────────────────────────────────────