# ============================================================================== # Tests for eigen_indices.R — Chapter 7 Eigen Selection Index Methods # # Functions covered: esim(), resim(), ppg_esim() # Helpers tested indirectly: .esim_solve_sym_multi(), .eigen_index_metrics(), # .leading_eigenvector() # ============================================================================== # ------------------------------------------------------------------------------ # Shared fixtures (2-trait synthetic + real seldata matrices) # ------------------------------------------------------------------------------ # Small 2×2 system with known spectral properties: # P^{-1}G = [5 1; 1 4] [10 2; 2 8]^{-1} — exact eigenvalues computable by hand P_2 <- matrix(c(10, 2, 2, 8), nrow = 2) G_2 <- matrix(c(5, 1, 1, 4), nrow = 2) # 3×3 system used for restriction tests P_3 <- matrix(c( 10, 2, 1, 2, 8, 1, 1, 1, 6 ), nrow = 3, byrow = TRUE) G_3 <- matrix(c( 5, 1, 0.5, 1, 4, 0.5, 0.5, 0.5, 3 ), nrow = 3, byrow = TRUE) colnames(P_3) <- colnames(G_3) <- c("T1", "T2", "T3") # ============================================================================== # ===== ESIM ================================================================= # ============================================================================== test_that("esim: output structure is correct", { r <- esim(P_2, G_2) expect_s3_class(r, "esim") expect_s3_class(r, "eigen_index") expected_names <- c( "summary", "b", "Delta_G", "sigma_I", "hI2", "rHI", "lambda2", "implied_w", "all_eigenvalues", "selection_intensity", "trait_names", "pmat", "gmat" ) expect_true(all(expected_names %in% names(r))) # summary is a data frame expect_s3_class(r$summary, "data.frame") expect_equal(nrow(r$summary), 1L) # default n_indices = 1 # b and Delta_G are numeric vectors of correct length expect_true(is.numeric(r$b)) expect_false(is.matrix(r$b)) expect_equal(length(r$b), 2L) expect_equal(length(r$Delta_G), 2L) # scalar outputs are single finite numbers for (field in c("sigma_I", "hI2", "rHI", "lambda2")) { expect_true(is.numeric(r[[field]]) && length(r[[field]]) == 1L, label = paste("scalar:", field) ) expect_true(is.finite(r[[field]]), label = paste("finite:", field)) } }) test_that("esim: leading eigenvalue equals index heritability", { r <- esim(P_2, G_2) # The leading eigenvalue of P^{-1}G IS h^2_I by definition P_inv_G <- solve(P_2) %*% G_2 ev <- eigen(P_inv_G) lambda2_ref <- max(Re(ev$values)) expect_equal(r$lambda2, lambda2_ref, tolerance = 1e-6) expect_equal(r$hI2, lambda2_ref, tolerance = 1e-6) expect_equal(r$rHI, sqrt(lambda2_ref), tolerance = 1e-6) }) test_that("esim: b_E is the eigenvector of P^{-1}G for the leading eigenvalue", { r <- esim(P_2, G_2) P_inv_G <- solve(P_2) %*% G_2 # (P^{-1}G) b = lambda^2 b => residual P^{-1}G b - lambda^2 b ≈ 0 residual <- as.numeric(P_inv_G %*% r$b - r$lambda2 * r$b) expect_true(all(abs(residual) < 1e-8), label = "b_E satisfies (P^{-1}G - lambda^2 I) b = 0" ) }) test_that("esim: metric formulas are internally consistent", { r <- esim(P_3, G_3) b <- as.numeric(r$b) # sigma_I = sqrt(b'Pb) bPb <- as.numeric(t(b) %*% P_3 %*% b) expect_equal(r$sigma_I, sqrt(bPb), tolerance = 1e-8) # hI2 = b'Gb / b'Pb (also == lambda2) bGb <- as.numeric(t(b) %*% G_3 %*% b) expect_equal(r$hI2, bGb / bPb, tolerance = 1e-8) expect_equal(r$rHI, sqrt(r$hI2), tolerance = 1e-8) k_I <- r$selection_intensity expect_equal(r$summary$Delta_G[1], k_I * r$sigma_I, tolerance = 1e-6) # Delta_G vector = (k_I / sigma_I) * G b DG_expected <- as.numeric(k_I * (G_3 %*% b) / r$sigma_I) expect_equal(as.numeric(r$Delta_G), DG_expected, tolerance = 1e-8) }) test_that("esim: n_indices > 1 returns multiple rows in summary", { r <- esim(P_3, G_3, n_indices = 3L) # P_3 is 3×3, so at most 3 positive eigenvalues expect_true(nrow(r$summary) >= 1L) expect_true(nrow(r$summary) <= 3L) # Eigenvalues in summary should be in descending order lambdas <- r$summary$lambda2 expect_true(all(diff(lambdas) <= 0 + 1e-10)) }) test_that("esim: returns correct trait names from column names", { r <- esim(P_3, G_3) expect_equal(r$trait_names, c("T1", "T2", "T3")) expect_equal(names(r$b), c("T1", "T2", "T3")) expect_equal(names(r$Delta_G), c("T1", "T2", "T3")) }) test_that("esim: fallback trait names when matrices have no colnames", { P_nc <- P_2 colnames(P_nc) <- NULL G_nc <- G_2 colnames(G_nc) <- NULL r <- esim(P_nc, G_nc) expect_equal(r$trait_names, c("Trait_1", "Trait_2")) }) test_that("esim: input validation catches bad inputs", { expect_error(esim(P_2, G_3), regexp = "same dimensions") expect_error(esim(matrix(1), matrix(1)), regexp = "At least 2 traits") non_sym <- P_2 non_sym[1, 2] <- 999 expect_error(esim(non_sym, G_2), regexp = "symmetric") }) test_that("esim: works on real seldata matrices", { gmat <- gen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) pmat <- phen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) r <- esim(pmat, gmat) expect_s3_class(r, "esim") expect_equal(length(r$b), 7L) expect_true(r$hI2 > 0 && r$hI2 < 1) expect_true(r$rHI > 0 && r$rHI < 1) expect_true(r$sigma_I > 0) }) test_that("esim: print and summary return invisibly without error", { r <- esim(P_3, G_3) capture.output(expect_invisible(print(r))) capture.output(expect_invisible(summary(r))) }) test_that("esim: results are deterministic across repeated calls", { r1 <- esim(P_3, G_3) r2 <- esim(P_3, G_3) expect_equal(r1$b, r2$b) expect_equal(r1$lambda2, r2$lambda2) expect_equal(r1$Delta_G, r2$Delta_G) }) test_that("esim: custom selection_intensity scales sigma_I correctly", { k1 <- 2.063 k2 <- 1.755 # 20 % selection r1 <- esim(P_3, G_3, selection_intensity = k1) r2 <- esim(P_3, G_3, selection_intensity = k2) # b and lambda2 are independent of k_I (purely from eigenproblem) expect_equal(r1$b, r2$b, tolerance = 1e-10) expect_equal(r1$lambda2, r2$lambda2, tolerance = 1e-10) # Delta_G (scalar) scales with k_I expect_equal(r1$summary$Delta_G[1] / r2$summary$Delta_G[1], k1 / k2, tolerance = 1e-6 ) }) # ============================================================================== # ===== RESIM ================================================================ # ============================================================================== test_that("resim: output structure is correct", { r <- resim(P_3, G_3, restricted_traits = 1L) expect_s3_class(r, "resim") expect_s3_class(r, "eigen_index") expected_names <- c( "summary", "b", "Delta_G", "sigma_I", "hI2", "rHI", "lambda2", "K", "Q_R", "U_mat", "restricted_traits", "implied_w", "selection_intensity", "trait_names", "pmat", "gmat" ) expect_true(all(expected_names %in% names(r))) expect_s3_class(r$summary, "data.frame") expect_equal(nrow(r$summary), 1L) expect_equal(length(r$b), 3L) expect_equal(length(r$Delta_G), 3L) for (field in c("sigma_I", "hI2", "rHI", "lambda2")) { expect_true(is.finite(r[[field]]), label = paste("finite:", field)) } }) test_that("resim: single restricted trait has near-zero genetic gain", { r <- resim(P_3, G_3, restricted_traits = 1L) expect_true(abs(r$Delta_G["T1"]) < 1e-6, label = "Restricted trait T1 has Delta_G ≈ 0" ) }) test_that("resim: two restricted traits both have near-zero gain", { r <- resim(P_3, G_3, restricted_traits = c(1L, 2L)) expect_true(abs(r$Delta_G["T1"]) < 1e-6) expect_true(abs(r$Delta_G["T2"]) < 1e-6) }) test_that("resim: unrestricted traits are free to respond", { r <- resim(P_3, G_3, restricted_traits = 1L) # T2 and T3 are unrestricted — their gains should generally be non-zero expect_true(abs(r$Delta_G["T2"]) > 1e-8 || abs(r$Delta_G["T3"]) > 1e-8) }) test_that("resim: restriction satisfied via formula U' G b ≈ 0", { r <- resim(P_3, G_3, restricted_traits = c(1L, 3L)) U <- r$U_mat b <- as.numeric(r$b) # The formal RESIM constraint is U' C b = 0 (C = gmat) constraint <- as.numeric(t(U) %*% G_3 %*% b) expect_true(all(abs(constraint) < 1e-6), label = "U' G b = 0 for all restricted traits" ) }) test_that("resim: accepts custom U_mat and matches restricted_traits version", { r1 <- resim(P_3, G_3, restricted_traits = c(1L, 2L)) U <- diag(3)[, c(1L, 2L), drop = FALSE] r2 <- resim(P_3, G_3, U_mat = U) expect_equal(r1$b, r2$b, tolerance = 1e-8) expect_equal(r1$lambda2, r2$lambda2, tolerance = 1e-8) }) test_that("resim: K is a projection matrix (K^2 = K)", { r <- resim(P_3, G_3, restricted_traits = 1L) K <- r$K expect_equal(K %*% K, K, tolerance = 1e-8) }) test_that("resim: lambda2 < esim lambda2 (restriction reduces heritability)", { re <- esim(P_3, G_3) rr <- resim(P_3, G_3, restricted_traits = 1L) expect_true(rr$lambda2 <= re$lambda2 + 1e-10, label = "Restriction cannot improve maximum index heritability" ) }) test_that("resim: input validation catches bad inputs", { expect_error(resim(P_3, G_3), regexp = "restricted_traits.*U_mat" ) expect_error(resim(P_3, G_3, restricted_traits = c(1L, 2L, 3L)), regexp = "less than number of traits" ) expect_error(resim(P_3, G_3, restricted_traits = 5L), regexp = "valid trait indices" ) }) test_that("resim: works on real seldata matrices", { gmat <- gen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) pmat <- phen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) r <- resim(pmat, gmat, restricted_traits = c(1L, 3L)) expect_s3_class(r, "resim") expect_true(abs(r$Delta_G[1]) < 1e-6) # trait 1 restricted expect_true(abs(r$Delta_G[3]) < 1e-6) # trait 3 restricted }) test_that("resim: print and summary return invisibly without error", { r <- resim(P_3, G_3, restricted_traits = 1L) capture.output(expect_invisible(print(r))) capture.output(expect_invisible(summary(r))) }) # ============================================================================== # ===== PPG-ESIM ============================================================= # ============================================================================== test_that("ppg_esim: output structure is correct", { d <- c(1, 1) r <- ppg_esim(P_2, G_2, d) expect_s3_class(r, "ppg_esim") expect_s3_class(r, "eigen_index") expected_names <- c( "summary", "beta", "b", "Delta_G", "sigma_I", "hI2", "rHI", "lambda2", "F_mat", "K_P", "Q_P", "D_M", "desired_gains", "selection_intensity", "trait_names", "pmat", "gmat" ) expect_true(all(expected_names %in% names(r))) expect_s3_class(r$summary, "data.frame") expect_equal(length(r$beta), 2L) expect_equal(length(r$b), 2L) expect_equal(length(r$Delta_G), 2L) for (field in c("sigma_I", "hI2", "rHI", "lambda2")) { expect_true(is.finite(r[[field]]), label = paste("finite:", field)) } }) test_that("ppg_esim: Mallard matrix D_M is orthogonal to d", { d <- c(2, 1, -1) r <- ppg_esim(P_3, G_3, d) d_unit <- d / sqrt(sum(d^2)) # Every column of D_M must be orthogonal to d_unit inner_products <- as.numeric(t(r$D_M) %*% d_unit) expect_true(all(abs(inner_products) < 1e-10), label = "D_M columns are orthogonal to d" ) # D_M columns should be orthonormal among themselves DtD <- t(r$D_M) %*% r$D_M expect_equal(DtD, diag(ncol(r$D_M)), tolerance = 1e-10) }) test_that("ppg_esim: K_P is a rank-1 projection matrix", { d <- c(1, 2, 1) r <- ppg_esim(P_3, G_3, d) K <- r$K_P expect_equal(K %*% K, K, tolerance = 1e-8) # Rank 1 rank_K <- sum(eigen(K, only.values = TRUE)$values > 0.5) expect_equal(rank_K, 1L) }) test_that("ppg_esim: gains are exactly proportional to d (equal d)", { d <- rep(1, 3) r <- ppg_esim(P_3, G_3, d) dg <- as.numeric(r$Delta_G) ratios <- dg / d cv <- sd(ratios) / abs(mean(ratios)) expect_true(cv < 1e-8, label = paste("CV of gain ratios:", cv) ) }) test_that("ppg_esim: gains are exactly proportional to d (asymmetric d)", { d <- c(3, 1, 2) r <- ppg_esim(P_3, G_3, d) dg <- as.numeric(r$Delta_G) ratios <- dg / d cv <- sd(ratios) / abs(mean(ratios)) expect_true(cv < 1e-8, label = paste("CV of gain ratios:", cv) ) }) test_that("ppg_esim: gains are exactly proportional to d (mixed sign d)", { d <- c(1, -1, 1) r <- ppg_esim(P_3, G_3, d) dg <- as.numeric(r$Delta_G) ratios <- dg / d cv <- sd(ratios) / abs(mean(ratios)) expect_true(cv < 1e-8, label = paste("CV of gain ratios:", cv) ) }) test_that("ppg_esim: gains are exactly proportional on real seldata (7 traits)", { gmat <- gen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) pmat <- phen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) for (dtest in list(rep(1, 7), c(2, 1, 1, 1, 1, 1, 1), c(1, 1, -1, 1, -1, 1, 1))) { r <- ppg_esim(pmat, gmat, dtest) dg <- as.numeric(r$Delta_G) ratios <- dg / dtest cv <- sd(ratios) / abs(mean(ratios)) expect_true(cv < 1e-6, label = paste("CV:", cv, "for d:", paste(dtest, collapse = ",")) ) } }) test_that("ppg_esim: phi (mean ratio) is positive — gains align with d", { d1 <- c(1, 1, 1) d2 <- c(2, 1, -1) for (d in list(d1, d2)) { r <- ppg_esim(P_3, G_3, d) dg <- as.numeric(r$Delta_G) phi <- mean(dg / d) expect_true(phi > 0, label = paste("phi > 0 for d:", paste(d, collapse = ",")) ) } }) test_that("ppg_esim: beta = F b and F is scalar ±I", { d <- c(1, 2, 1) r <- ppg_esim(P_3, G_3, d) beta_reconstructed <- as.numeric(r$F_mat %*% r$b) expect_equal(as.numeric(r$beta), beta_reconstructed, tolerance = 1e-10) # F_mat must be a scalar multiple of identity (±I) diag_vals <- diag(r$F_mat) expect_true(length(unique(diag_vals)) == 1L, label = "F_mat is scalar ±I" ) expect_true(abs(abs(diag_vals[1]) - 1) < 1e-10, label = "diagonal of F_mat is ±1" ) # Off-diagonal elements are zero off_diag <- r$F_mat - diag(diag_vals) expect_true(all(abs(off_diag) < 1e-10)) }) test_that("ppg_esim: metric formulas are internally consistent", { d <- c(2, 1, 1) r <- ppg_esim(P_3, G_3, d) beta <- as.numeric(r$beta) bPb <- as.numeric(t(beta) %*% P_3 %*% beta) bGb <- as.numeric(t(beta) %*% G_3 %*% beta) k_I <- r$selection_intensity expect_equal(r$sigma_I, sqrt(bPb), tolerance = 1e-8) expect_equal(r$hI2, bGb / bPb, tolerance = 1e-8) expect_equal(r$rHI, sqrt(r$hI2), tolerance = 1e-8) DG_expected <- as.numeric(k_I * (G_3 %*% beta) / r$sigma_I) expect_equal(as.numeric(r$Delta_G), DG_expected, tolerance = 1e-8) }) test_that("ppg_esim: D_M has correct dimensions (t x t-1)", { d <- c(1, 2, 3) r <- ppg_esim(P_3, G_3, d) expect_equal(dim(r$D_M), c(3L, 2L)) }) test_that("ppg_esim: scaling d does not change the gain direction", { d1 <- c(1, 2, 1) d2 <- d1 * 5 # scaled version r1 <- ppg_esim(P_3, G_3, d1) r2 <- ppg_esim(P_3, G_3, d2) # Gains should be proportional (same direction, possibly different scale) ratio1 <- as.numeric(r1$Delta_G) / as.numeric(r2$Delta_G) cv <- sd(ratio1) / abs(mean(ratio1)) expect_true(cv < 1e-8, label = "Scaling d does not change the gain ratio pattern" ) }) test_that("ppg_esim: lambda2 <= esim lambda2 (PPG constraint reduces heritability)", { d <- c(2, 1, 1) re <- esim(P_3, G_3) rp <- ppg_esim(P_3, G_3, d) expect_true(rp$lambda2 <= re$lambda2 + 1e-10, label = "PPG constraint cannot improve free-optimum heritability" ) }) test_that("ppg_esim: input validation catches bad inputs", { expect_error(ppg_esim(P_3, G_3, c(1, 1)), regexp = "same length" ) expect_error(ppg_esim(P_3, G_3, c(0, 0, 0)), regexp = "non-zero" ) non_sym <- P_3 non_sym[1, 2] <- 999 expect_error(ppg_esim(non_sym, G_3, c(1, 1, 1)), regexp = "symmetric" ) }) test_that("ppg_esim: print and summary return invisibly without error", { d <- c(2, 1, 1) r <- ppg_esim(P_3, G_3, d) capture.output(expect_invisible(print(r))) capture.output(expect_invisible(summary(r))) }) test_that("ppg_esim: results are deterministic across repeated calls", { d <- c(1, 2, 1) r1 <- ppg_esim(P_3, G_3, d) r2 <- ppg_esim(P_3, G_3, d) expect_equal(r1$beta, r2$beta) expect_equal(r1$lambda2, r2$lambda2) expect_equal(r1$Delta_G, r2$Delta_G) }) # ============================================================================== # ===== Cross-method consistency ============================================= # ============================================================================== test_that("esim >= resim >= ppg_esim in heritability (hierarchy of constraints)", { gmat <- gen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) pmat <- phen_varcov(seldata[, 3:9], seldata[, 2], seldata[, 1]) re <- esim(pmat, gmat) rr <- resim(pmat, gmat, restricted_traits = 1L) rp <- ppg_esim(pmat, gmat, rep(1, 7)) # Free ESIM achieves the maximum; restrictions can only reduce it expect_true(re$lambda2 >= rr$lambda2 - 1e-8) expect_true(re$lambda2 >= rp$lambda2 - 1e-8) }) test_that("esim without restrictions equals resim with zero restrictions (edge case)", { # RESIM with restriction on 0 traits is ill-formed — verify proper error # (not a valid call — at least 1 restriction required) expect_error(resim(P_3, G_3, restricted_traits = integer(0)), regexp = "restricted_traits" ) }) # ============================================================================== # NEW COVERAGE TESTS — targeting previously uncovered lines # ============================================================================== # Helper: small valid 2x2 matrices with known properties .P2 <- matrix(c(10, 2, 2, 8), nrow = 2) .G2 <- matrix(c(5, 1, 1, 4), nrow = 2) # --- .eigen_index_metrics: line 87 – Delta_G_vec = rep(NA, nrow(G)) -------- # Triggered when bPb <= 0 (sigma_I is NA), so the Delta_G vector branch # at line 84 goes to the else: rep(NA_real_, nrow(G)). test_that(".eigen_index_metrics returns NA Delta_G_vec when bPb <= 0 (line 87)", { # Build b perpendicular to P's positive-definite direction by giving a # degenerate b = 0 so b'Pb = 0. b_zero <- c(0, 0) m <- selection.index:::.eigen_index_metrics(b_zero, .P2, .G2, lambda2 = NULL) expect_true(all(is.na(m$Delta_G_vec))) expect_equal(length(m$Delta_G_vec), nrow(.G2)) }) # --- .eigen_index_metrics: line 94 – bGb/bPb fallback when lambda2=NULL --- # When lambda2 is NULL and bPb > 0 (valid b), hI2 = bGb/bPb (line 94). test_that(".eigen_index_metrics uses bGb/bPb when lambda2=NULL (line 94)", { # Use a non-zero b for meaningful bPb b <- c(1, 0) m <- selection.index:::.eigen_index_metrics(b, .P2, .G2, lambda2 = NULL) # bPb = b'Pb = [1,0] [10,2;2,8] [1,0]' = 10 # bGb = b'Gb = [1,0] [5,1;1,4] [1,0]' = 5 expect_equal(m$hI2, 5 / 10, tolerance = 1e-10) expect_equal(m$hI2, m$bGb / m$bPb, tolerance = 1e-10) }) # --- .leading_eigenvector: lines 133-134 – no positive eigenvalues ---------- # Supply a negative-definite matrix (negated identity) so all eigenvalues <= 0. test_that(".leading_eigenvector stops when no positive eigenvalues (lines 133-134)", { neg_mat <- -diag(3) # all eigenvalues = -1 <= 0 expect_error( selection.index:::.leading_eigenvector(neg_mat), "No positive eigenvalues found" ) }) # --- esim: line 228 – non-symmetric gmat ----------------------------------- test_that("esim errors on non-symmetric gmat (line 228)", { bad_g <- .G2 bad_g[1, 2] <- 999 expect_error(esim(.P2, bad_g), regexp = "symmetric") }) # --- esim: lines 280-281 – implied_w tryCatch fallback ---------------------- # Mock ginv in selection.index namespace to throw; implied_w tryCatch catches # and returns rep(NA_real_, n_traits) + emits a warning. test_that("esim implied_w tryCatch returns NA and warns when ginv fails (lines 280-281)", { expect_warning( { r <- with_mocked_bindings( ginv = function(...) stop("mocked ginv failure"), .package = "selection.index", esim(.P2, .G2) ) }, "Could not compute implied economic weights" ) expect_true(all(is.na(r$implied_w))) expect_equal(length(r$implied_w), 2L) }) # --- resim: line 423 – non-symmetric pmat ---------------------------------- test_that("resim errors on non-symmetric pmat (line 423)", { bad_p <- P_3 bad_p[1, 2] <- 999 expect_error(resim(bad_p, G_3, restricted_traits = 1L), regexp = "pmat must be a symmetric matrix" ) }) # --- resim: line 425 – non-symmetric gmat ---------------------------------- test_that("resim errors on non-symmetric gmat (line 425)", { bad_g <- G_3 bad_g[1, 2] <- 999 expect_error(resim(P_3, bad_g, restricted_traits = 1L), regexp = "gmat must be a symmetric matrix" ) }) # --- resim: line 427 – dimension mismatch (nrow(pmat) != nrow(gmat)) ------- test_that("resim errors when pmat and gmat have different dims (line 427)", { expect_error( resim(P_3, .G2, restricted_traits = 1L), regexp = "same dimensions" ) }) # --- resim: line 431 – auto-generate trait names when no colnames ---------- test_that("resim auto-generates trait names when pmat has no colnames (line 431)", { P_nc <- P_3 colnames(P_nc) <- NULL G_nc <- G_3 colnames(G_nc) <- NULL r <- resim(P_nc, G_nc, restricted_traits = 1L) expect_equal(r$trait_names, paste0("Trait_", 1:3)) }) # --- resim: line 447 – U_mat with wrong nrow -------------------------------- test_that("resim errors when U_mat nrow != n_traits (line 447)", { U_wrong <- matrix(c(1, 0), nrow = 2, ncol = 1) # nrow=2 but n_traits=3 expect_error( resim(P_3, G_3, U_mat = U_wrong), regexp = "U_mat must have nrow equal to n_traits" ) }) # --- resim: lines 498-499 – implied_w tryCatch fallback ------------------- # resim calls ginv() once in its own computation (mid_inv, line 467) and once # inside the implied_w tryCatch (G_inv, line 495). A stateful counter mock # lets the first call succeed and fails only the second, so we reach line 498-499. test_that("resim implied_w tryCatch returns NA and warns when ginv fails (lines 498-499)", { call_count <- 0L real_ginv <- MASS::ginv r <- NULL expect_warning( { r <- with_mocked_bindings( ginv = function(X, ...) { call_count <<- call_count + 1L if (call_count < 2L) real_ginv(X, ...) else stop("mocked ginv failure") }, .package = "selection.index", resim(P_3, G_3, restricted_traits = 1L) ) }, "Could not compute implied weights" ) expect_true(all(is.na(r$implied_w))) expect_equal(length(r$implied_w), nrow(P_3)) }) # --- ppg_esim: line 653 – non-symmetric gmat -------------------------------- test_that("ppg_esim errors on non-symmetric gmat (line 653)", { bad_g <- G_3 bad_g[1, 2] <- 999 expect_error(ppg_esim(P_3, bad_g, d = c(1, 1, 1)), regexp = "symmetric" ) }) # --- ppg_esim: line 655 – dimension mismatch -------------------------------- test_that("ppg_esim errors when pmat and gmat have different dims (line 655)", { expect_error( ppg_esim(P_3, .G2, d = c(1, 1, 1)), regexp = "same dimensions" ) }) # --- ppg_esim: lines 699-701 – solve(mid) fails, falls back to ginv -------- # With G_zero (all-zero), Psi_DM = 0, so mid = 0 -> solve(0) fails and the # warning at line 699-700 fires. The function then continues with ginv(0) = 0, # leading to K_P = I, then v_norm < 1e-12 -> stop at line 728-729. # Wrap in tryCatch to swallow the downstream stop so we can verify the warning. test_that("ppg_esim warning when middle matrix is singular, falls back to ginv (lines 699-701)", { G_zero <- matrix(0, 3, 3) # Psi_DM = G*D_M = 0 -> mid = 0 -> solve fails # The function emits the singularity warning first, then stops due to v_norm=0. # Capture the warning before the subsequent error propagates. expect_warning( tryCatch( ppg_esim(P_3, G_zero, d = c(1, 0.01, 0.01)), error = function(e) NULL # swallow downstream v_norm stop ), "Middle matrix in Q_P is singular" ) }) # --- ppg_esim: lines 728-729 – v_norm < 1e-12 → stop ----------------------- # When P_inv_G = 0 (G=0) and K_P * 0 = 0, v_raw = 0 → v_norm = 0 → stop. # Actually with G=0, K_P * P^{-1}*G * d_unit = K_P * 0 = 0, so v_norm < 1e-12. # This test requires ginv fallback path. After ginv of a zero mid gives 0 K_P, # then v_raw = K_P * P^{-1}*0 * d = 0, so v_norm = 0. test_that("ppg_esim stops when v_norm is numerically zero (lines 728-729)", { G_zero <- matrix(0, 3, 3) # makes P^{-1}G = 0, so K_P*(P^{-1}G)*d = 0 expect_error( suppressWarnings( ppg_esim(P_3, G_zero, d = c(1, 1, 1)) ), "K_P.*P.*{-1}G.*d is numerically zero|numerically zero" ) }) # --- ppg_esim: line 749 – sign_corr == 0 → sign_corr <- 1L ----------------- # sign_corr = sign(sum(tentative_gain * d)) = 0 when G*b_P is perpendicular to d. # With an all-zero G, G*b_P = 0, so sum(0 * d) = 0 → sign_corr = 0 → line 749. # We need G that makes G*b_P orthogonal to d but still has P^{-1}G != 0. # Construct G_skew: symmetric, with G*b ⊥ d for the specific b that arises. # Easier: use a G where G*b_P happens to be exactly orthogonal to d. # This is very hard to engineer analytically. Instead, mock the internal # tentative_gain calculation by providing a custom G where the gain is # numerically zero — same approach as G_zero above, but bypass the v_norm stop. # We can achieve line 749 by using a G that is not zero (so v_norm > 0) but # produces G*b_P exactly orthogonal to d. Since b_P ∝ K_P * P^{-1}G * d, # and gmat*b_P ≡ 0 iff G*b_P = 0, we need G to map b_P to zero. # The simplest synthetic case: G singular with null space containing b_P. # For the 2-trait system, d = c(1,1): if G * b_P = 0 and b_P ≠ 0: # pick G = outer(c(-1,1), c(1,1)) (rank 1, maps b_P to 0 if b_P ∝ c(1,1)) # Let's verify: b_P = K_P * P^{-1}G * d/||d||. With G = [-1 -1; 1 1], K_P at # rank 1 projects onto d=c(1,1) direction, b_P ∝ c(1,1). # G * c(1,1) = c(-2, 2) ≠ 0, so this doesn't work directly. # Use G = c(1,-1)c(1,1)': G * c(1,-1) = c(1-1, -1+1) = 0 after normalization. # This is very hard to guarantee analytically, so use the mock-based approach. test_that("ppg_esim handles sign_corr = 0 by defaulting to 1 (line 749)", { # It is mathematically impossible for G*b_P = 0 AND v_norm > 0 in exact arithmetic. # We mock the internal solver to decouple the two checks. P_test <- diag(2) G_test <- matrix(c(0, 0, 0, 1), 2, 2) d_test <- c(1, 0) mock_solve <- function(pmat, mat) { if (ncol(mat) == nrow(pmat)) { # For P_inv_G calculation, return Identity so v_raw = K_P * I * d = c(1,0) -> v_norm = 1 diag(2) } else { # For P_inv_PsiDM calculation, return normal matrix multiplication mat } } testthat::with_mocked_bindings( .esim_solve_sym_multi = mock_solve, .package = "selection.index", code = { # This forces b_P = c(1, 0). # Then tentative_gain = G_test %*% c(1, 0) = c(0, 0). # sum(c(0,0) * d_test) == 0, triggering the fallback. expect_no_error( res <- ppg_esim(P_test, G_test, d = d_test) ) expect_equal(as.numeric(res$b), c(1, 0), tolerance = 1e-5) } ) }) # --- print.resim: line 954 – rep(FALSE, ...) when restricted_traits is NULL -- # When resim is called with U_mat (not restricted_traits), result$restricted_traits # is NULL. The print method at line 951-954 hits the else: rep(FALSE, ...). test_that("print.resim reaches rep(FALSE,...) when restricted_traits is NULL (line 954)", { U_mat <- diag(3)[, 1L, drop = FALSE] # U_mat provided, not restricted_traits r <- resim(P_3, G_3, U_mat = U_mat) expect_null(r$restricted_traits) # Print should run without error and reach the else branch at line 954 out <- capture.output(print(r)) # The Restricted column should be present and all FALSE expect_true(any(grepl("Restricted", out)) || is.character(out)) }) # --- print.ppg_esim: lines 1062-1066 – high CV → "[!]" branch --------------- # The "[OK]" branch at line 1059 requires cv_ratio < 0.02. # The "[!]" branch at line 1062 fires when cv_ratio >= 0.02. # We can force this by constructing a result object with inconsistent # desired_gains and Delta_G (high CV of ratios). test_that("print.ppg_esim prints '[!]' message when CV >= 0.02 (lines 1062-1066)", { # Build a minimal valid ppg_esim result with manually forced high CV ratios. r <- ppg_esim(P_3, G_3, d = c(1, 1, 1)) # Manually overwrite desired_gains to create high CV: # ratios = Delta_G / d; if d is very non-uniform and Delta_G is uniform, # CV will be high. r$desired_gains <- c(0.001, 1000, 0.001) # wildly non-proportional to Delta_G out <- capture.output(print(r)) expect_true(any(grepl("\\[!\\]", out)), info = "Expected '[!]' line in print output when CV >= 0.02" ) })