test_that("WW relabeling orders component medians and preserves likelihood", { draws <- rbind( c(alpha = 0.4, p = 0.2, k1 = 2, th1 = 3, k2 = 1.5, th2 = 0.7, loglik = -4), c(alpha = -0.3, p = 0.7, k1 = 1.2, th1 = 0.5, k2 = 2.5, th2 = 2, loglik = -5) ) result <- ambs:::.relabel_posterior(draws, "ww") medians <- ambs:::.component_medians(result$draws, "ww") expect_true(all(medians[, "m1"] <= medians[, "m2"])) expect_equal(result$draws[, "loglik"], draws[, "loglik"]) time <- c(0.2, 0.9, 1.8) status <- c(1, 0, 1) for (i in seq_len(nrow(draws))) { before <- draws[i, ] after <- result$draws[i, ] ll_before <- ambs:::.loglik_ww( before["alpha"], before["p"], 1 - before["p"], before["th1"], before["th2"], before["k1"], before["k2"], time, status ) ll_after <- ambs:::.loglik_ww( after["alpha"], after["p"], 1 - after["p"], after["th1"], after["th2"], after["k1"], after["k2"], time, status ) expect_equal(ll_after, ll_before, tolerance = 1e-12) for (type in c("s", "h", "d")) { expect_equal( ambs:::.curve_ww(before, time, type), ambs:::.curve_ww(after, time, type), tolerance = 1e-12 ) } } }) test_that("LL relabeling orders locations and preserves likelihood", { draws <- rbind( c(alpha = 0.5, p = 0.25, mu1 = 2, mu2 = 0, sig1 = 0.2, sig2 = 0.8, loglik = -3), c(alpha = -0.4, p = 0.6, mu1 = -1, mu2 = 1, sig1 = 0.5, sig2 = 0.3, loglik = -6) ) result <- ambs:::.relabel_posterior(draws, "ll") expect_true(all(result$draws[, "mu1"] <= result$draws[, "mu2"])) time <- c(0.3, 1, 3) status <- c(1, 1, 0) for (i in seq_len(nrow(draws))) { before <- draws[i, ] after <- result$draws[i, ] ll_before <- ambs:::.loglik_ll( before["alpha"], before["p"], 1 - before["p"], before["mu1"], before["mu2"], before["sig1"], before["sig2"], time, status ) ll_after <- ambs:::.loglik_ll( after["alpha"], after["p"], 1 - after["p"], after["mu1"], after["mu2"], after["sig1"], after["sig2"], time, status ) expect_equal(ll_after, ll_before, tolerance = 1e-12) for (type in c("s", "h", "d")) { expect_equal( ambs:::.curve_ll(before, time, type), ambs:::.curve_ll(after, time, type), tolerance = 1e-12 ) } } }) test_that("relabeling requires exchangeable same-family priors", { symmetric <- ambs:::.default_prior("ww") asymmetric <- symmetric asymmetric$component2$k$meanlog <- asymmetric$component1$k$meanlog + 0.2 expect_true(ambs:::.component_priors_exchangeable("ww", symmetric)) expect_false(ambs:::.component_priors_exchangeable("ww", asymmetric)) expect_error( ambs:::.validate_ident_request("ww", asymmetric, "relabel"), "exchangeable" ) expect_error( ambs:::.validate_ident_request("gw", ambs:::.default_prior("gw"), "relabel"), "distribution family" ) }) test_that("known exact degeneracies are detected", { ww <- matrix(rep(c(alpha = 0, p = 0.5, k1 = 2, th1 = 1, k2 = 2, th2 = 2, loglik = -1), 20), nrow = 20, byrow = TRUE) colnames(ww) <- c("alpha", "p", "k1", "th1", "k2", "th2", "loglik") gw <- matrix(rep(c(alpha = 0.4, p = 0.5, k1 = 1, th1 = 1, k2 = 1, th2 = 1, loglik = -1), 20), nrow = 20, byrow = TRUE) colnames(gw) <- colnames(ww) ll <- matrix(rep(c(alpha = 0.4, p = 0.5, mu1 = 1, mu2 = 1, sig1 = 0.5, sig2 = 0.5, loglik = -1), 20), nrow = 20, byrow = TRUE) colnames(ll) <- c("alpha", "p", "mu1", "mu2", "sig1", "sig2", "loglik") for (case in list(list(ww, "ww"), list(gw, "gw"), list(ll, "ll"))) { diagnosed <- ambs:::.diagnose_identifiability(case[[1]], case[[2]], c(0.2, 1, 2)) expect_equal(diagnosed$known_degeneracy$probability, 1) expect_equal(diagnosed$status, "non_identifiable") } }) test_that("overlap and boundary diagnostics flag weak identification", { draws <- matrix(rep(c(alpha = 0.5, p = 0.99, mu1 = 1, mu2 = 1.01, sig1 = 0.5, sig2 = 0.6, loglik = -1), 20), nrow = 20, byrow = TRUE) colnames(draws) <- c("alpha", "p", "mu1", "mu2", "sig1", "sig2", "loglik") diagnosed <- ambs:::.diagnose_identifiability(draws, "ll", c(0.2, 1, 2)) expect_equal(diagnosed$weight_boundary$probability, 1) expect_true(diagnosed$component_overlap$probability > 0) expect_equal(diagnosed$status, "weak") }) test_that("public ident interface records raw and relabeled draws", { set.seed(27) fit <- suppressWarnings(alpmixsurv( c(0.4, 0.8, 1.2, 2), c(1, 1, 0, 1), model = "ll", mcmc = list(nburn = 10, nsamp = 20), ident = "auto" )) expect_equal(nrow(fit$posterior_raw), 20) expect_equal(fit$identifiability$method, "median-based posterior relabeling") expect_true(all(fit$posterior[, "mu1"] <= fit$posterior[, "mu2"])) fit_none <- suppressWarnings(alpmixsurv( c(0.4, 0.8, 1.2, 2), c(1, 1, 0, 1), model = "ll", mcmc = list(nburn = 2, nsamp = 4), ident = "none" )) expect_equal(fit_none$posterior, fit_none$posterior_raw) expect_equal(fit_none$identifiability$status, "not_assessed") })