Caches & updates

A local target can still be expensive if every proposal reconstructs a large deterministic quantity. HOBBS uses persistent deterministic caches to maintain such quantities incrementally.

Initialize once

cache mu(n) {
  for (i = 1:n) {
    for (k = 1:p) {
      mu(i) += beta(k) * x(i,k);
    }
  }
}

The cache initializer builds mu from the accepted parameter state.

Update only the affected part

Changing beta(j) by \(\Delta\) changes the predictor by \(\Delta x_{ij}\):

update mu(n) {
  for (i = 1:n) {
    mu(i) += (proposal(beta(j)) - current(beta(j))) * x(i,j);
  }
}

This replaces repeated \(O(np)\) predictor reconstruction with an \(O(n)\) rank-one update for each coefficient proposal.

Transactional behavior

Cache updates are staged with the proposal. If the proposal is accepted, the parameter and cache become the new state together. If it is rejected, HOBBS restores the previous cache automatically. The model author writes only the forward deterministic update.

More than one block can maintain a cache

A group effect can update only the rows belonging to its group:

block u(j, l) {
  build_sig();
  u(j, 1:2) ~ dmvn(zero2, Sigma_u);
  pllk();
} update mu(n) {
  for (i = gstart(j):gend(j)) {
    mu(i) += (proposal(u(j,l)) - current(u(j,l))) * x(i,l);
  }
}

The cache stays exact only if every parameter that can change it has a correct update rule. In state-dependent models, the condition controlling whether a term affects the target must agree with the condition controlling whether the cache changes.

Deterministic caches vs distribution caches

A cache / update pair represents exact deterministic model state. log_cache is different: it enables numerical lookup tables for selected expensive functions and is an explicit approximation. Keep these two ideas conceptually separate.