test_that("softmax_weights sum to 1", { w <- softmax_weights(c(0.8, 0.5, 0.3)) expect_equal(sum(w), 1.0, tolerance = 1e-10) }) test_that("softmax_weights preserve names", { w <- softmax_weights(c(MSG = 0.8, MRG = 0.5, cMTG = 0.3)) expect_equal(names(w), c("MSG", "MRG", "cMTG")) }) test_that("softmax_weights handle zero components", { w <- softmax_weights(c(0.8, 0.0, 0.7), temperature = 0.13) expect_true(w[2] < 0.01) # near-zero weight for zero component expect_equal(sum(w), 1.0, tolerance = 1e-10) }) test_that("softmax_weights respect temperature", { # Lower T = sharper weights w_sharp <- softmax_weights(c(0.8, 0.3, 0.5), temperature = 0.05) w_soft <- softmax_weights(c(0.8, 0.3, 0.5), temperature = 1.0) expect_true(max(w_sharp) > max(w_soft)) }) test_that("softmax_weights reject bad inputs", { expect_error(softmax_weights(c(0.5))) # too few expect_error(softmax_weights(c(0.5, 0.3), temperature = 0)) expect_error(softmax_weights(c(0.5, 0.3), temperature = -1)) }) test_that("compute_psri_sm returns numeric value", { val <- compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 25) expect_true(is.numeric(val)) expect_true(length(val) == 1) expect_true(val >= 0) }) test_that("compute_psri_sm return_components works", { result <- compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 25, return_components = TRUE) expect_true(is.list(result)) expect_true("psri_sm" %in% names(result)) expect_true("components" %in% names(result)) expect_true("weights" %in% names(result)) expect_equal(result$n_components, 3) expect_equal(names(result$components), c("MSG", "MRG", "cMTG")) }) test_that("compute_psri_sm includes RVS with radicle_count", { result <- compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 25, radicle_count = 18, return_components = TRUE) expect_equal(result$n_components, 4) expect_true("RVS" %in% names(result$components)) expect_equal(result$components[["RVS"]], 0.72) }) test_that("compute_psri_sm does NOT collapse to zero with zero component", { # MRG will be ~0 for late-only germination result <- compute_psri_sm(c(0, 0, 10), c(3, 5, 7), 25, return_components = TRUE) # Softmax should NOT produce zero — this is the key advantage expect_true(result$psri_sm > 0) }) test_that("compute_psri_sm validates inputs", { expect_error(compute_psri_sm(c(5, 15), c(3, 5, 7), 25)) # length mismatch expect_error(compute_psri_sm(c(-1, 5, 10), c(3, 5, 7), 25)) # negative expect_error(compute_psri_sm(c(5, 15, 30), c(3, 5, 7), 25)) # exceeds seeds expect_error(compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 0)) # zero seeds }) test_that("compute_psri_sm respects temperature parameter", { val_sharp <- compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 25, temperature = 0.05) val_soft <- compute_psri_sm(c(5, 15, 20), c(3, 5, 7), 25, temperature = 1.0) # Both should be valid numbers; exact relationship depends on components expect_true(is.numeric(val_sharp) && is.numeric(val_soft)) })