# Tier 2 integration: end-to-end estimator pipeline. # Exercises the full nlmixr2(model, admData(), est = ...) entry points # (nlmixr2Est.admc / .adfo / .adirmc) that the other integration files # deliberately avoid by calling internals directly. A minimal end-to-end check: # a valid admFit is returned with a finite objective and near-truth estimates -- # not a convergence test. All fits are built once in .int_pipeline_setup(). # Loose band around the data-generating truth. Init values equal truth, so a # short fit cannot drift far; a wide band keeps the test about "did the pipeline # return something sensible", not "did it converge". .expect_sensible <- function(fit, tcl_true, tv_true) { th <- fit$theta expect_true(all(is.finite(th))) expect_true(is.finite(fit$objective)) expect_gt(fit$objective, 0) expect_equal(unname(th[["tcl"]]), tcl_true, tolerance = 0.5) expect_equal(unname(th[["tv"]]), tv_true, tolerance = 0.5) om <- fit$omega expect_true(is.matrix(om) && all(is.finite(om))) } # ---- admc -------------------------------------------------------------------- test_that("admc pipeline: returns a finite admFit with method 'admc'", { env <- .int_pipeline_setup() fit <- env$fit_admc expect_s3_class(fit, "admFit") expect_identical(fit$env$method, "admc") .expect_sensible(fit, env$tcl_true, env$tv_true) }) test_that("admc pipeline: objective statistics (logLik/AIC/BIC) are finite", { env <- .int_pipeline_setup() fit <- env$fit_admc expect_true(is.finite(as.numeric(logLik(fit)))) expect_true(is.finite(AIC(fit))) expect_true(is.finite(BIC(fit))) }) # ---- adfo -------------------------------------------------------------------- test_that("adfo pipeline: returns a finite admFit with method 'adfo'", { env <- .int_pipeline_setup() fit <- env$fit_adfo expect_s3_class(fit, "admFit") expect_identical(fit$env$method, "adfo") .expect_sensible(fit, env$tcl_true, env$tv_true) }) # ---- adirmc (exercises .adirmcPhaseLoop end-to-end) -------------------------- test_that("adirmc pipeline: returns a finite admFit with method 'adirmc'", { env <- .int_pipeline_setup() fit <- env$fit_adirmc expect_s3_class(fit, "admFit") expect_identical(fit$env$method, "adirmc") .expect_sensible(fit, env$tcl_true, env$tv_true) }) # ---- adgh -------------------------------------------------------------------- test_that("adgh pipeline: returns a finite admFit with method 'adgh'", { env <- .int_pipeline_setup() fit <- env$fit_adgh expect_s3_class(fit, "admFit") expect_identical(fit$env$method, "adgh") .expect_sensible(fit, env$tcl_true, env$tv_true) }) # ---- multi-restart (.admRunRestarts best-result selection) ------------------- test_that("admc multi-restart: records one trace per restart", { env <- .int_pipeline_setup() fit <- env$fit_restart expect_identical(fit$env$method, "admc") expect_length(fit$env$admExtra$all_traces, 2L) expect_true(is.finite(fit$objective)) }) test_that("admc multi-restart: reported objective matches the best trace", { env <- .int_pipeline_setup() fit <- env$fit_restart # Best-result selection: the returned objective is the minimum over restarts. best_per_restart <- vapply(fit$env$admExtra$all_traces, function(tr) min(tr$nll_trace), numeric(1)) expect_equal(fit$objective, min(best_per_restart), tolerance = 1e-6) }) # ---- multi-study ------------------------------------------------------------- test_that("admc multi-study: fit carries both studies and a finite objective", { env <- .int_pipeline_setup() fit <- env$fit_multistudy expect_length(fit$env$studies, 2L) expect_identical(names(fit$env$studies), c("s1", "s2")) .expect_sensible(fit, env$tcl_true, env$tv_true) }) # ---- aggData slot (observed + predicted aggregate moments) ------------------- test_that("pipeline: each estimator fit carries a valid aggData slot", { env <- .int_pipeline_setup() for (fit in list(env$fit_admc, env$fit_adfo, env$fit_adgh, env$fit_adirmc)) { ad <- fit$env$aggData expect_false(is.null(ad)) study <- names(fit$env$studies)[1] a <- ad[[study]] expect_false(is.null(a)) n_t <- length(fit$env$studies[[study]]$times) # Observed moments come straight from the study spec. expect_equal(as.numeric(a$obs$E), as.numeric(fit$env$studies[[study]]$E)) # Predicted moments: mean vector length n_t, symmetric finite n_t x n_t cov. expect_length(a$pred$E, n_t) expect_equal(dim(a$pred$V), c(n_t, n_t)) expect_true(all(is.finite(a$pred$E))) expect_true(all(is.finite(a$pred$V))) expect_equal(a$pred$V, t(a$pred$V)) } }) test_that("pipeline: adirmc fit records its sampling method in adirmcExtra", { env <- .int_pipeline_setup() # Regression: adirmcExtra used to omit `sampling`, so plots and aggData # silently fell back to "sobol" regardless of the control setting. expect_identical(env$fit_adirmc$env$adirmcExtra$sampling, "sobol") }) test_that("pipeline: multi-study fit has an aggData entry per study", { env <- .int_pipeline_setup() ad <- env$fit_multistudy$env$aggData expect_false(is.null(ad)) expect_identical(names(ad), c("s1", "s2")) for (a in ad) { expect_false(is.null(a)) expect_true(all(is.finite(a$pred$E))) expect_true(all(is.finite(a$pred$V))) } }) # ---- covMethod = "r" (in-pipeline .admCalcCov) ------------------------------- test_that("admc covMethod='r': pipeline attaches a covariance matrix", { env <- .int_pipeline_setup() fit <- env$fit_cov expect_identical(fit$env$covMethod, "r") expect_false(is.null(fit$cov)) expect_true(is.matrix(fit$cov)) expect_equal(nrow(fit$cov), ncol(fit$cov)) expect_true(all(is.finite(fit$cov))) # Symmetric positive-definite Hessian-derived covariance. expect_equal(fit$cov, t(fit$cov), tolerance = 1e-8) expect_true(all(eigen(fit$cov, symmetric = TRUE, only.values = TRUE)$values > 0)) }) test_that("admc covMethod='r': struct thetas get finite standard errors", { env <- .int_pipeline_setup() fit <- env$fit_cov pf <- fit$parFixedDf se_struct <- pf[c("tcl", "tv"), "SE"] expect_true(all(is.finite(se_struct))) expect_true(all(se_struct > 0)) }) test_that("covMethod='r': omega and sigma SEs survive the trip through nlmixr2est", { # nlmixr2est's C++ foceiFitCpp_ re-dimnames whatever covariance it finds in the # fit environment from its own theta-name vector, blanking the omega rows -- and # it does so IN PLACE, which also blanks the driver's copy. So this is not a # cosmetic check: without .admCovNames()/.admRestoreCovNames() the omega SE is # present but unreachable by name, and `fit$cov["om.eta.cl", ]` errors. env <- .int_pipeline_setup() # one_cmt_fn has a diagonal 2-eta omega, so nlmixr2est's naming gives om. # for both. Written out rather than derived, so a change in .admOmegaReportNames # cannot make this test agree with the code by construction. om <- c("om.eta.cl", "om.eta.v") for (nm in c("admc", "adgh", "adfo")) { fit <- switch(nm, admc = env$fit_cov, adgh = env$fit_adgh_cov, adfo = env$fit_adfo_cov) if (is.null(fit) || is.null(fit$cov)) next rn <- rownames(fit$cov) expect_false(any(is.na(rn) | rn == ""), info = paste(nm, "blank cov name")) # Reported on the variance/covariance scale, never the log-Cholesky. expect_true(all(om %in% rn), info = paste(nm, "omega missing from cov")) for (o in om) expect_true(is.finite(fit$cov[o, o]) && fit$cov[o, o] > 0, info = paste(nm, o)) # Sigma too: returning only the structural corner used to leave nlmixr2est's # popDf builder reading past the end of the matrix and printing a denormal. expect_true("add.err" %in% rn, info = paste(nm, "sigma missing from cov")) } }) # ---- plot / print on a real fit object --------------------------------------- test_that("plot.admFit on a real fit: nll/par trace panels are ggplots", { skip_if_not_installed("ggplot2") env <- .int_pipeline_setup() # which = c("nll","par") skips MC simulation -> fast, no rxSolve. p <- plot(env$fit_restart, which = c("nll", "par")) expect_true(is.list(p)) expect_true(all(c("nll_trace", "par_trace") %in% names(p))) expect_s3_class(p$nll_trace, "ggplot") expect_s3_class(p$par_trace, "ggplot") }) test_that("print.admFit on a real fit: runs without error", { env <- .int_pipeline_setup() expect_output(print(env$fit_admc)) }) # ---- restart / covariance / multi-study for the non-MC estimators ------------ # Covers the per-estimator restart workers (.adghRestartWorker / # .adfoRestartWorker / .adirmcRestartWorker), the in-pipeline covariance # branches (.adghCalcCov / .adfoCalcCov / .adirmcCalcCov) and adirmc multi-study # -- paths previously only exercised for admc at the pipeline level. # A deterministic-surface (adgh/adfo) Hessian covariance should be SPD; for the # MC-based adirmc it may legitimately fail to be PD on a short fit, so callers # pass require_pd accordingly. .expect_cov_ok <- function(fit, require_pd) { expect_s3_class(fit, "admFit") expect_true(is.finite(fit$objective)) if (is.null(fit$cov)) { if (require_pd) fail("covariance was not computed") succeed() return(invisible()) } expect_identical(fit$env$covMethod, "r") expect_true(is.matrix(fit$cov) && all(is.finite(fit$cov))) expect_equal(nrow(fit$cov), ncol(fit$cov)) expect_equal(fit$cov, t(fit$cov), tolerance = 1e-8) if (require_pd) expect_true(all(eigen(fit$cov, symmetric = TRUE, only.values = TRUE)$values > 0)) } # -- adgh ---------------------------------------------------------------------- test_that("adgh covMethod='r': pipeline computes an SPD covariance", { env <- .int_pipeline_setup() .expect_cov_ok(env$fit_adgh_cov, require_pd = TRUE) }) test_that("adgh multi-restart: records one trace per restart, finite objective", { env <- .int_pipeline_setup() fit <- env$fit_adgh_restart expect_identical(fit$env$method, "adgh") expect_length(fit$env$admExtra$all_traces, 2L) expect_true(is.finite(fit$objective)) }) # -- adfo ---------------------------------------------------------------------- test_that("adfo multi-restart: records one trace per restart, finite objective", { env <- .int_pipeline_setup() fit <- env$fit_adfo_restart expect_identical(fit$env$method, "adfo") expect_length(fit$env$admExtra$all_traces, 2L) expect_true(is.finite(fit$objective)) }) test_that("adfo covMethod='r': pipeline computes an SPD covariance", { env <- .int_pipeline_setup() .expect_cov_ok(env$fit_adfo_cov, require_pd = TRUE) }) # -- adirmc -------------------------------------------------------------------- test_that("adirmc multi-restart: records one trace per restart, finite objective", { env <- .int_pipeline_setup() fit <- env$fit_adirmc_restart expect_identical(fit$env$method, "adirmc") # adirmc stores traces in adirmcExtra (admc/adgh use admExtra). expect_length(fit$env$adirmcExtra$all_traces, 2L) expect_true(is.finite(fit$objective)) }) test_that("adirmc covMethod='r': pipeline attaches a finite covariance", { env <- .int_pipeline_setup() .expect_cov_ok(env$fit_adirmc_cov, require_pd = FALSE) }) test_that("adirmc multi-study: fit carries both studies and a finite objective", { env <- .int_pipeline_setup() fit <- env$fit_adirmc_multistudy expect_length(fit$env$studies, 2L) expect_identical(names(fit$env$studies), c("s1", "s2")) .expect_sensible(fit, env$tcl_true, env$tv_true) })