Functions & probability statements

Reusable code chunks

Repeated calculations can be written once using func:

func llk() {
  double sigma = exp(logsigma(1));
  for (i = 1:n) {
    y(i) ~ dnorm(mu(i), sigma);
  }
}

A HOBBS func is a zero-argument reusable code chunk. The translator expands it at each call site rather than treating it as a conventional run-time function.

block logsigma(1) {
  logsigma(1) ~ dnorm(0, 2);
  llk();
}

Because expansion occurs inside the surrounding block, the chunk can use the block’s index symbols and locally available names. Avoid temporary-name collisions between expanded chunks and surrounding code.

Probability statements

The basic statistical statement is

value(i) ~ distribution(arguments);

It adds the corresponding log density or log probability to the current block target. The same syntax is used for priors and likelihood contributions.

beta(j) ~ dnorm(0, 10);
y(i) ~ dnorm(mu(i), sigma);
gamma(j) ~ dbern(pi);

For multivariate densities, ranges can be used:

u(j,1:2) ~ dmvn(zero2, Sigma_u);

Custom log-density terms

The target can also be modified directly. This makes the built-in distribution list extensible without changing the sampler:

func custom_laplace() {
  target += -log(2.0 * b) - fabs(x(i) - mu_i) / b;
}

The model body otherwise remains close to C: scalar declarations, transformations, loops, conditionals, assignments, and numerical expressions can be mixed with probability statements.