test_that("named priors are normalized and used by model", { custom <- list( alpha = list(mean = 1.5, sd = 0.7), p = list(shape1 = 2, shape2 = 3), component1 = list(k = list(meanlog = 0.1, sdlog = 0.4)), component2 = list(theta = list(shape = 2, scale = 0.8)) ) got <- ambs:::.normalize_prior(custom, "gw") expect_equal(got$alpha, custom$alpha) expect_equal(got$p, custom$p) expect_equal(got$component1$k$meanlog, 0.1) expect_equal(got$component2$theta$scale, 0.8) expect_equal(got$component1$theta$shape, 1) }) test_that("legacy prior remains compatible and records variance semantics", { legacy <- list(k = c(2, 3), theta = c(4, 5), alpha = c(1, 9), p = c(6, 7)) got <- ambs:::.normalize_prior(legacy, "ww") expect_true(isTRUE(attr(got, "legacy"))) expect_equal(got$alpha, list(mean = 1, sd = 3)) expect_equal(got$component1$theta$shape, 2) expect_equal(got$component2$theta$scale, 5) }) test_that("invalid prior scales and MCMC controls fail early", { expect_error( ambs:::.normalize_prior(list(alpha = list(sd = 0)), "ll"), "positive and finite" ) expect_error( alpmixsurv(c(1, 2), c(1, 0), mcmc = list(nburn = 1, nsamp = 0)), "nsamp" ) }) test_that("alpha sampler is continuous and does not manufacture zeros", { set.seed(1804) time <- c(0.25, 0.5, 0.8, 1.1, 1.7, 2.3) status <- c(1, 1, 0, 1, 0, 1) for (model in c("ww", "gw", "ll")) { fit <- suppressWarnings(alpmixsurv( time, status, model = model, mcmc = list(nburn = 20, nsamp = 40, thin = 1) )) expect_true(all(is.finite(fit$posterior))) expect_false(any(fit$posterior[, "alpha"] == 0)) expect_equal(nrow(fit$posterior), 40) } }) test_that("thinning retains the requested number of posterior samples", { set.seed(91) fit <- suppressWarnings(alpmixsurv( c(0.4, 0.9, 1.4, 2), c(1, 1, 0, 1), model = "ww", mcmc = list(nburn = 5, nsamp = 12, thin = 3) )) expect_equal(nrow(fit$posterior), 12) expect_equal(fit$mcmc$n_iter, 41) }) test_that("alpha remains continuous and near-zero probability is reported", { set.seed(1842) fit <- suppressWarnings(alpmixsurv( c(0.25, 0.5, 0.8, 1.1, 1.7, 2.3), c(1, 1, 0, 1, 0, 1), model = "ww", mcmc = list(nburn = 20, nsamp = 40) )) expect_false("alpha_fixed" %in% names(formals(alpmixsurv))) expect_false("alpha_fixed" %in% names(fit)) expect_false(any(fit$posterior[, "alpha"] == 0)) expect_equal(fit$alpha_near_zero_band, 0.01) expect_equal( fit$alpha_near_zero_probability, mean(abs(fit$posterior[, "alpha"]) < 0.01) ) }) test_that("LL log-time standardization and parameter back-transform are exact", { time <- c(0.2, 0.7, 1.5, 4) transform <- ambs:::.ll_time_standardization(time) original <- c(alpha = 0.3, p = 0.4, mu1 = -0.2, mu2 = 0.8, sig1 = 0.5, sig2 = 0.15) working <- original working[c("mu1", "mu2")] <- (original[c("mu1", "mu2")] - transform$center) / transform$scale working[c("sig1", "sig2")] <- original[c("sig1", "sig2")] / transform$scale expect_equal( ambs:::.curve_ll(original, time, "s"), ambs:::.curve_ll(working, transform$time, "s"), tolerance = 1e-12 ) prior <- ambs:::.default_prior("ll") round_trip <- ambs:::.ll_prior_to_original( ambs:::.ll_prior_to_working(prior, transform), transform ) expect_equal(round_trip, prior, tolerance = 1e-12) }) test_that("LL fit reports original-scale draws and likelihood", { set.seed(901) time <- c(0.25, 0.4, 0.8, 1.2, 2, 3) status <- c(1, 1, 0, 1, 0, 1) fit <- suppressWarnings(alpmixsurv( time, status, model = "ll", mcmc = list(nburn = 100, nsamp = 20), ident = "none" )) draw <- fit$posterior[1, ] expected <- ambs:::.loglik_ll( draw["alpha"], draw["p"], 1 - draw["p"], draw["mu1"], draw["mu2"], draw["sig1"], draw["sig2"], time, status ) expect_equal(unname(draw["loglik"]), expected, tolerance = 1e-10) expect_equal(fit$prior_source, "fixed prior on standardized log-time") expect_true(all(c("center", "scale", "time") %in% names(fit$time_transform))) }) test_that("WW and GW median-time scaling preserves survival curves", { time <- c(20, 80, 150, 400) transform <- ambs:::.scale_time_standardization(time) for (model in c("ww", "gw")) { original <- c(alpha = 0.4, p = 0.35, k1 = 1.4, th1 = 90, k2 = 2.2, th2 = 260) working <- original working[c("th1", "th2")] <- original[c("th1", "th2")] / transform$scale curve_fun <- if (model == "ww") ambs:::.curve_ww else ambs:::.curve_gw expect_equal(curve_fun(original, time, "s"), curve_fun(working, transform$time, "s"), tolerance = 1e-11) prior <- ambs:::.default_prior(model) round_trip <- ambs:::.scale_prior_to_original( ambs:::.scale_prior_to_working(prior, transform), transform ) expect_equal(round_trip, prior, tolerance = 1e-12) } }) test_that("WW fit returns original-scale theta and likelihood", { set.seed(902) time <- c(25, 40, 80, 120, 200, 300) status <- c(1, 1, 0, 1, 0, 1) fit <- alpmixsurv(time, status, model = "ww", mcmc = list(nburn = 100, nsamp = 20), ident = "none") draw <- fit$posterior[1, ] expected <- ambs:::.loglik_ww( draw["alpha"], draw["p"], 1 - draw["p"], draw["th1"], draw["th2"], draw["k1"], draw["k2"], time, status ) expect_equal(unname(draw["loglik"]), expected, tolerance = 1e-10) expect_equal(fit$time_transform$scale, median(time)) expect_equal(fit$working_time, time / median(time)) expect_equal(fit$prior_source, "fixed prior on median-standardized time") expect_equal(fit$chain_inits[[1]]$th1, fit$chain_inits_working[[1]]$th1 * median(time)) })