Creates a control object for nlmixr2(est = "adgh"). The GH estimator
integrates model predictions against the random-effects prior
\(\eta \sim N(0, \Omega)\) using a deterministic tensor-product
Gauss-Hermite quadrature grid. It is unbiased at any IIV magnitude (unlike
FO), noise-free (unlike MC), and much faster than MC for models with up to
~4 etas.
Usage
adghControl(
studies = list(),
n_nodes = 5L,
grad = c("analytical", "fd", "none"),
algorithm = NULL,
maxeval = 500L,
ftol_rel = .Machine$double.eps^(1/2),
print = 10L,
seed = 12345L,
cores = rxode2::rxCores(),
nDisplayProgress = .Machine$integer.max,
grad_h = 1e-04,
grad_bounds = 5,
cov_h = 0.001,
cov_h_outer = .Machine$double.eps^(1/4),
covMethod = c("r", "none"),
n_restarts = 1L,
restart_sd = 0.5,
workers = 1L,
rxControl = NULL,
calcTables = FALSE,
compress = TRUE,
ci = 0.95,
sigdig = NULL,
sigdigTable = NULL,
addProp = c("combined2", "combined1"),
optExpression = TRUE,
sumProd = FALSE,
literalFix = TRUE,
returnAdmr = FALSE,
resid_nodes = 81L,
...
)Arguments
- studies
Named list of study specifications (same format as
admControl():E,V,n,times,ev, optionalmethod; or anobservationslist for multi-compartment fits – seeadmControl()).- n_nodes
Number of quadrature nodes per eta dimension (default 5). Total nodes =
n_nodes^n_eta.n_nodes = 5achieves near-exact covariance moments for IIV SD up to ~0.5;n_nodes = 7extends coverage to SD ~0.7. For models with >= 5 etas the node count grows steeply; consider reducingn_nodesor using a different estimator.- grad
Gradient mode.
"analytical"(default) uses closed-form contractions through the sensitivity equations – cheapest and exact."fd"uses central finite differences (forward differencing was removed in 0.4.1; seeadfoControl())."none"uses derivative-free BOBYQA.- algorithm
nloptr algorithm, or
NULL(default) to pick the default that matchesgrad:"NLOPT_LD_LBFGS"with a gradient,"NLOPT_LN_BOBYQA"whengrad = "none". Any algorithm reported bynloptr::nloptr.print.options()is accepted. An explicit algorithm is reconciled withgrad: whengrad = "none"a gradient-based algorithm (NLOPT_LD_*/NLOPT_GD_*) falls back to"NLOPT_LN_BOBYQA"; when a gradient is requested a derivative-free algorithm (NLOPT_LN_*/NLOPT_GN_*) turns the gradient off. Both emit a message.- maxeval
Maximum function evaluations (default 500).
- ftol_rel
Relative tolerance (default
sqrt(.Machine$double.eps)).Print-frequency for live progress (0 = silent).
- seed
Random seed (used for restarts).
- cores
OpenMP threads for
rxSolve(). Defaults torxode2::rxCores(). Whenworkers > 1it is a total budget, split across the workers.- nDisplayProgress
Passed to
rxSolve(): show the solver's text progress bar only once a single solve exceeds this many subjects. The default (.Machine$integer.max) keeps it off for clean script/vignette output; lower it (e.g.1000L) to see progress during long fits.- grad_h
Finite-difference step for unpaired struct theta gradient and FD Jacobian fallback.
- grad_bounds
Box-constraint half-width when using gradients: the fit is confined to
p0 +/- grad_boundson the optimizer scale, which for a log-scale parameter is a factor ofexp(grad_bounds)(~148 at the default 5). This bound is admixr2's, not the model's – an unbounded parameter has no other – and nloptr reports normal convergence at a box corner, so a warning is emitted if an estimate finishes on it.- cov_h
Inner FD step for the gradient-based Hessian (only used when
covMethod = "r"andgrad != "none").- cov_h_outer
Outer step scale for numerical Hessian. Default
eps^(1/4)(tighter than admc'seps^(1/5)because the GH surface is noise-free).- covMethod
"r"computes covariance via a numerical Hessian over the structural, residual-error and omega parameters;"none"skips it. Omega is included because excluding it also biases the STRUCTURAL standard errors downward – a theta carrying an eta is correlated with that eta's variance. If the weakly-identified omega Cholesky makes the Hessian non-positive definite, the structural + residual sub-block is reported with a warning.All three blocks are reported on the scale the ESTIMATES are printed on, as
nlmixr2estdoes: structural thetas on the log/optimizer scale, residual error as an SD, and omega as the variance/covariance entries (namedom.<eta>andcov.<eta_i>.<eta_j>). The omega block is rotated by the full Jacobian of Omega with respect to the log-Cholesky, which is not diagonal once omega is correlated.- n_restarts
Number of optimizer restarts (1 = no multi-start).
- restart_sd
SD of random perturbations of initial struct thetas at each restart.
- workers
Number of parallel workers (mirai daemons) for multi-restart (default 1 = sequential). Requires the
miraipackage.- rxControl
rxode2::rxControl()object. Created automatically whenNULL.- calcTables, compress, ci, sigdigTable, optExpression, sumProd, literalFix
Passed to
nlmixr2est::foceiControl()for the table/output machinery.- sigdig
Significant digits asked of the ODE solver, or
NULL(the default) to leave rxode2's own solver tolerances alone. When set, it is passed torxode2::rxSolve()'s ownsigdigargument for every solve the estimator issues – rxode2 owns the mapping toatol/rtoland has changed it between releases, which is why the digits, not the tolerances, are what travels – and tonlmixr2est::foceiControl()for the post-fit tables.It is a speed lever, and an opt-in one because it is not free. The estimators finite-difference the solve with steps of the same order:
grad_h(1e-4),cov_h(1e-3) andcov_h_outer(~2.5e-3), whilesigdig = 4maps to a relative tolerance of ~1e-4 on current rxode2. Differencing a solution whose own noise is 1e-4 with a 1e-4 step returns noise, and it surfaces as a moved objective and an indefinite covariance Hessian (everySEreportedNA) rather than as an error. Most worthwhile where the gradient is fully analytic and nothing differences the solve –adfoControl(grad = "analytical")measured ~4.8x faster atsigdig = 4with standard errors unchanged to 4 significant figures. Elsewhere, compare the objective and the standard errors againstNULLbefore relying on it. Table formatting is unaffected either way:sigdigTabledefaults to 4 regardless.- addProp
How combined additive+proportional error is parameterised in the nlmixr2 output tables:
"combined2"(default) or"combined1".- returnAdmr
If
TRUE, return a plain list instead of the full nlmixr2 fit object.- resid_nodes
Gauss-Hermite nodes used to integrate the RESIDUAL for a transform-both-sides endpoint (
boxCox,yeoJohnson,logitNorm,probitNorm), wherey = g(h(f) + sigma*eps)has no closed-form mean and variance. Ignored by every other error model, which has closed forms. Default 81. Measured worst-case relative error against an independent quadrature, over all four transforms and residual SD of 0.5, 1, 2 and 3: n = 15 gives 5.7e-2, 31 gives 4.5e-3, 81 gives 5.0e-5. The error is dominated by large residual SD; at SD <= 1, n = 31 already gives 1e-7 or better.This is an ACCURACY dial, not a speed one. The quadrature is linear in
resid_nodesin isolation (~50 us at 15, 300 us at 81 for an 8-row study) but negligible beside the ODE solve: a full NLL evaluation measured 0.750 s per 60 evaluations at BOTH 31 and 81 nodes. Raise it if you have a saturating endpoint with a large residual SD; there is little to gain by lowering it.- ...
Unused arguments (trigger an error).
Examples
ctl <- adghControl()
ctl$n_nodes
#> [1] 5
ctl$grad
#> [1] "analytical"
# More nodes for large IIV, analytical gradient
ctl2 <- adghControl(n_nodes = 7L, grad = "analytical", maxeval = 300L)
# \donttest{
library(rxode2)
library(nlmixr2)
data("examplomycin")
obs <- examplomycin[examplomycin$EVID == 0, ]
obs <- obs[order(obs$ID, obs$TIME), ]
times <- sort(unique(obs$TIME))
ids <- unique(obs$ID)
dv_mat <- do.call(rbind, lapply(ids, function(i) {
sub <- obs[obs$ID == i, ]; sub$DV[order(sub$TIME)]
}))
E <- colMeans(dv_mat)
V <- cov.wt(dv_mat, method = "ML")$cov
pk_model <- function() {
ini({
tcl <- log(5); tv <- log(30)
prop.sd <- c(0, 0.2)
eta.cl ~ 0.09; eta.v ~ 0.04
})
model({
cl <- exp(tcl + eta.cl)
v <- exp(tv + eta.v)
d/dt(central) <- -(cl/v) * central
cp <- central / v
cp ~ prop(prop.sd)
})
}
fit <- nlmixr2(
pk_model, admData(), est = "adgh",
control = adghControl(
studies = list(study1 = list(E = E, V = V, n = length(ids),
times = times, ev = et(amt = 100)))
)
)
#>
#>
#>
#>
#> ℹ parameter labels from comments are typically ignored in non-interactive mode
#> ℹ Need to run with the source intact to parse comments
#> → loading into symengine environment...
#> → pruning branches (`if`/`else`) of full model...
#> ✔ done
#> → calculate sensitivities
#> → finding duplicate expressions in admixr2 sensitivity model...
#> → optimizing duplicate expressions in admixr2 sensitivity model...
#>
#>
#> === admixr2: Aggregate Data Modeling (GH) ===
#> Obs units: 1 | Params: 5 | Nodes: 5^2=25 | Cores: 2 | Grad: Analytical | Restarts: 1
#> +----------+----------+----------+----------+----------+----------+----------+
#> | | -2LL | tcl | tv | prop.sd | eta.cl | eta.v |
#> +----------+----------+----------+----------+----------+----------+----------+
#> | 0010 | 1000.18 | 6.203 | 35.45 | 0.3103 | 0.08888 | 0.05562 |
#> | 0020 | 805.78 | 6.666 | 37.33 | 0.3781 | 0.1041 | 0.05946 |
#> | 0022 ✓ | 805.78 | 6.667 | 37.33 | 0.378 | 0.104 | 0.05944 |
#> | 0.4 sec | | | | | | |
#> Computing covariance (R method, Analytical-Hessian, 6 gradient evaluations)
#> → compress origData in nlmixr2 object, save 1160
#>
#>
# }
