make_3state_t_fit <- function(seed = 11) { set.seed(seed) ret <- rnorm(150) fit <- fit_hmm_em(ret, K = 3L, model = "student", n_starts = 1L, max_iter = 20L, verbose = FALSE, nu_grid = c(4, 8, 15)) KRONX:::reorder_fit_by_scale(fit) } test_that("hazard_from_nu is monotone decreasing in nu", { nu <- c(3, 5, 10, 20) eps <- KRONX:::hazard_from_nu(nu, epsilon_min = 0.01, c_hazard = 0.05) expect_true(all(diff(eps) < 0)) expect_true(all(eps > 0)) }) test_that("build_Q row sums are <= 1 (sub-stochastic)", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) expect_true(all(rowSums(Q) <= 1 + 1e-12)) expect_true(all(Q >= 0)) }) test_that("build_K diagonal is negative", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) Kmat <- KRONX:::build_K(Q) expect_true(all(diag(Kmat) < 0)) }) test_that("fundamental_matrix N has positive entries", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) Kmat <- KRONX:::build_K(Q) Nmat <- KRONX:::fundamental_matrix(Kmat) expect_true(all(Nmat > 0)) }) test_that("quasi_stationary_distribution sums to one", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) pi <- KRONX:::quasi_stationary_distribution(Q) expect_equal(sum(pi), 1, tolerance = 1e-10) expect_true(all(pi > 0)) }) test_that("residence_weight_vector sums to one", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) Kmat <- KRONX:::build_K(Q) Nmat <- KRONX:::fundamental_matrix(Kmat) pi <- KRONX:::quasi_stationary_distribution(Q) w <- KRONX:::residence_weight_vector(Nmat, pi) expect_equal(sum(w), 1, tolerance = 1e-10) }) test_that("left_tail_state_probs are in (0, 1)", { fit <- make_3state_t_fit() ret <- rnorm(150) thr <- as.numeric(stats::quantile(ret, 0.01)) q <- KRONX:::left_tail_state_probs(fit, thr) expect_length(q, 3L) expect_true(all(q > 0 & q < 1)) }) test_that("ruin_bound is in (0, 1)", { fit <- make_3state_t_fit() eps <- KRONX:::hazard_from_nu(fit$params$nu) Q <- KRONX:::build_Q(fit$A, eps) Kmat <- KRONX:::build_K(Q) Nmat <- KRONX:::fundamental_matrix(Kmat) pi_qs <- KRONX:::quasi_stationary_distribution(Q) pi_r <- KRONX:::residence_weight_vector(Nmat, pi_qs) ret <- rnorm(150) thr <- as.numeric(stats::quantile(ret, 0.01)) q <- KRONX:::left_tail_state_probs(fit, thr) bar_q <- sum(pi_r * q) bar_l <- sum(pi_r * eps) rb <- 1 - exp(-bar_l * bar_q * 250) expect_true(rb > 0 && rb < 1) })