rxEtaDistExpand() writes the declared distribution's parameters into the
model as bare thetas inside an inverse-CDF call. Nothing about that shape is
theta + eta, so every one of them comes out non-mu-referenced – which
is the case both saem and the FOCEi family handle worst, and it is why a
cold-started fit of a declared-distribution model tends to settle a long way
from the answer.
Arguments
- ui
rxode2 model with at least one
dist()declaration- variance
variance to fix each helper random effect at.
variance = 0is the preferred spelling, and it is NONMEM's own: Bauer's control streams mu-reference every distribution parameter and put each helper on$OMEGA (0.0 FIXED). It declares what is true – the helper carries no between-subject variability – and hands the question of what to do about that to the estimation method, wherenlmixr2est::saemControl(zeroOmegaTune=, zeroOmegaAnneal=, zeroOmegaDirect=)can act on it.A NONZERO value writes a sampling width into the model itself and bypasses that machinery entirely. It is what this function did before saem had a direct-maximization M-step for these thetas, and it is kept because it still works: the helper has to MOVE, or the conditional mean saem shifts its theta by is identically zero and the theta never budges.
Wider is not generally better. Measured on Bauer's gamma model (300 subjects, cold start) widening degraded every parameter monotonically: at 0.1 / 1 / 4 the residual SD came out 0.150 / 0.162 / 0.170 against a truth of 0.141, and Q came out 2.29 / 2.48 / 2.60 against 2.13. 0.1 recovered CL 5.60 and V1 4.77 against truths of 5.03 and 4.66. That a constant cannot be right twice – wide enough early to explore, tight enough late to settle – is what
zeroOmegaAnneal=addresses.
Details
This carries each of those parameters on its own random effect with a small
FIXED variance, which is what puts them back into a theta + eta form and so
back onto the mu-referenced path. It is the same structure NONMEM control
streams get from MU_5 = THETA(5) with $OMEGA (0.0 FIXED), with one
important difference: the helper variance must not be ~0 here.
nlmixr2's mu-theta M-step is weighted by omega^-1, so a ~0 variance pins
the parameter at its starting value instead of freeing it (NONMEM updates
such a parameter by direct maximization, so the idiom works there).
The result is a different model – the helper variance is real between-subject variability on the distribution's parameters – so this is a way to travel, not a way to finish. Use it as the first stage of a chain and refit the model you actually mean from its estimates:
stage1 <- nlmixr2(rxEtaDistMuRef(mod), data, est = "saem")
final <- nlmixr2(mod |> ini(stage1), data, est = "focei",
control = foceiControl(mceta = 100))Measured on Bauer's gamma-distributed CL/V1 data (300 subjects), that chain
recovers the structural parameters essentially exactly (CL 5.04 against a
simulation truth of 5.03, Q 2.15 against 2.13) where a cold start of either
method alone does not. Stage two has to be a gradient method: saem as the
second stage moved the residual error further from the truth than stage one
had it.
Examples
# \donttest{
if (requireNamespace("lotri", quietly = TRUE) &&
"lotriEtaDists" %in% getNamespaceExports("lotri")) {
mod <- function() {
ini({
lclm <- log(5)
lclrv <- log(0.09)
tv <- 3.45
dist(eta.cl) ~ dgamma(shape = 1 / exp(lclrv),
rate = 1 / (exp(lclrv) * exp(lclm)))
add.sd <- 0.7
})
model({
cl <- eta.cl
v <- exp(tv)
linCmt() ~ add(add.sd)
})
}
rxEtaDistMuRef(mod)
}
# }
