#' @srrstats {G5.10} Extended tests can be switched on via setting the #' environment variable DYNAMITE_EXTENDED_TESTS to "true". run_extended_tests <- identical(Sys.getenv("DYNAMITE_EXTENDED_TESTS"), "true") data.table::setDTthreads(1) # For CRAN test_that("stanc_options argument works", { skip_if_not(run_extended_tests) set.seed(1) fit <- dynamite( dformula = obs(y ~ -1 + varying(~x), family = "gaussian") + lags(type = "varying") + splines(df = 20), gaussian_example, "time", "id", parallel_chains = 2, chains = 1, refresh = 0, backend = "cmdstanr", stanc_options = list("O0"), show_messages = FALSE, init = 0 ) expect_equal( summary(fit, parameter = "alpha_y")$mean[2], 1.5, tolerance = 0.1, ignore_attr = TRUE ) }) test_that("cmdstanr backend works for categorical model", { skip_if_not(run_extended_tests) set.seed(1) fit_dynamite <- update( categorical_example_fit, stanc_options = list("O0"), backend = "cmdstanr", show_messages = FALSE ) expect_equal( coef(fit_dynamite)$mean[1L], -0.5, tolerance = 0.1, ignore_attr = TRUE ) expect_error(get_code(fit_dynamite), NA) }) test_that("LOO and LFO works for AR(1) model estimated with cmdstanr", { skip_if_not(run_extended_tests) set.seed(1) fit <- dynamite( dformula = obs(LakeHuron ~ 1, "gaussian") + lags(), data = data.frame(LakeHuron, time = seq_len(length(LakeHuron)), id = 1), time = "time", group = "id", chains = 1, iter_sampling = 1000, iter_warmup = 1000, refresh = 0, backend = "cmdstanr", stanc_options = list("O0"), show_messages = FALSE, init = 0 ) l <- loo(fit) expect_equal( l$estimates, structure( c( -107.877842970846, 2.86041434691809, 215.755685941693, 7.36848739076899, 0.561813071004331, 14.736974781538 ), dim = 3:2, dimnames = list(c("elpd_loo", "p_loo", "looic"), c("Estimate", "SE")) ), tolerance = 1 ) expect_error(plot(l), NA) l <- lfo(fit, L = 20) expect_equal(l$ELPD, -91.5427292742266, tolerance = 1) expect_equal(l$ELPD_SE, 7.57777033647258, tolerance = 1) expect_error(plot(l), NA) }) test_that("within-chain parallelization with cmdstanr works", { skip_if_not(run_extended_tests) skip_on_os("mac") # Seems to segfault on MacOS set.seed(1) f <- obs(g ~ lag(g) + lag(logp), family = "gaussian") + obs(p ~ lag(g) + lag(logp) + lag(b), family = "poisson") + obs(b ~ lag(b) * lag(logp) + lag(b) * lag(g), family = "bernoulli") + aux(numeric(logp) ~ log(p + 1) | init(0)) fit_dynamite <- dynamite( dformula = f, data = multichannel_example, time = "time", group = "id", backend = "cmdstanr", show_messages = FALSE, threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) expect_equal( coef(fit_dynamite)$mean[1L], 0.003, tolerance = 0.1, ignore_attr = TRUE ) expect_error(get_code(fit_dynamite), NA) }) test_that("multivariate gaussian with threading produces a valid model", { skip_if_not(run_extended_tests) skip_on_os("mac") set.seed(1) N <- 100 T_ <- 50 S <- crossprod(matrix(rnorm(4), 2, 2)) L <- t(chol(S)) y1 <- matrix(0, N, T_) y2 <- matrix(0, N, T_) x <- matrix(0, N, T_) for (t in 2:T_) { for (i in 1:N){ mu <- c(0.7 * y1[i, t - 1], 0.4 * y2[i, t - 1] - 0.2 * y1[i, t - 1]) y <- mu + L %*% rnorm(2) y1[i, t] <- y[1L] y2[i, t] <- y[2L] x[i, t] <- rnorm(1, c(0.5 * y1[i, t - 1]), 0.5) } } d <- data.frame( y1 = c(y1), y2 = c(y2), x = c(x), t = rep(1:T_, each = N), id = 1:N ) f <- obs(c(y1, y2) ~ -1 + lag(y1) | -1 + lag(y1) + lag(y2), "mvgaussian") + obs(x ~ -1 + lag(y1), "gaussian") code <- get_code( x = f, data = d, time = "t", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) }) test_that("threading produces valid model code for other distributions", { skip_if_not(run_extended_tests) skip_on_os("mac") set.seed(1) n_id <- 50L n_time <- 20L n <- n_id * n_time d <- data.frame( g = rgamma(n, 3, 0.5), p = rpois(n, 5.0), b = rbinom(n, 10, 0.4), s = rt(n, 15), bb = rbeta(n, 3, 6), e = rexp(n, 2), time = rep(seq_len(n_time), each = n_id), id = rep(seq_len(n_id)), trials = rep(10, n) ) f <- obs(g ~ lag(g), family = "gamma") + obs(p ~ lag(g) + lag(b), family = "negbin") + obs(b ~ lag(b) + lag(b) * lag(g) + trials(trials), family = "binomial") + obs(s ~ lag(g), family = "student") + obs(bb ~ lag(b), family = "beta") + obs(e ~ lag(s), family = "exponential") code <- get_code( x = f, data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) }) test_that("threading produces a valid model for cumulative", { skip_if_not(run_extended_tests) skip_on_os("mac") set.seed(0) n <- 100 t <- 30 x <- matrix(0, n, t) y <- matrix(0, n, t) p <- matrix(0, n, 4) alpha <- c(-1, 0, 1) for (i in seq_len(t)) { x[, i] <- rnorm(n) eta <- 0.6 * x[, i] p[, 1] <- 1 - plogis(eta - alpha[1]) p[, 2] <- plogis(eta - alpha[1]) - plogis(eta - alpha[2]) p[, 3] <- plogis(eta - alpha[2]) - plogis(eta - alpha[3]) p[, 4] <- plogis(eta - alpha[3]) y[, i] <- apply(p, 1, sample, x = 1:4, size = 1, replace = FALSE) } d <- data.frame( y = factor(c(y), levels = 1:4), x = c(x), time = rep(seq_len(t), each = n), id = rep(seq_len(n), t) ) code <- get_code( x = obs(y ~ x, family = "cumulative", link = "logit"), data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) code <- get_code( x = obs(y ~ -1 + x + varying(~1), family = "cumulative", link = "logit") + splines(df = 10), data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) # no predictors code <- get_code( x = obs(y ~ -1 + varying(~1), family = "cumulative", link = "logit") + splines(df = 10), data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) code <- get_code( x = obs(y ~ 1, family = "cumulative", link = "logit") + splines(df = 10), data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) }) test_that("threading produces a valid model for multinomial", { skip_if_not(run_extended_tests) skip_on_os("mac") set.seed(1) n_id <- 100L n_time <- 20L d <- data.frame( y1 = sample(10, size = n_id * n_time, replace = TRUE), y2 = sample(15, size = n_id * n_time, replace = TRUE), y3 = sample(20, size = n_id * n_time, replace = TRUE), z = rnorm(n_id * n_time), time = seq_len(n_time), id = rep(seq_len(n_id), each = n_time) ) d$n <- d$y1 + d$y2 + d$y3 f <- obs( c(y1, y2, y3) ~ z + lag(y1) + lag(y2) + lag(y3) + trials(n), family = "multinomial" ) code <- get_code( x = f, data = d, time = "time", group = "id", backend = "cmdstanr", threads_per_chain = 2, grainsize = 10, chains = 2, parallel_chains = 1 ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) }) test_that("syntax is correct for various models", { skip_if_not(run_extended_tests) skip_on_os("mac") # categorical_example_fit code <- get_code( x = obs(x ~ z + lag(x) + lag(y), family = "categorical") + obs(y ~ z + lag(x) + lag(y), family = "categorical"), data = categorical_example, time = "time", group = "id", backend = "cmdstanr" ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) # gaussian_example_fit code <- get_code( x = obs( y ~ -1 + z + varying(~ x + lag(y)) + random(~1), family = "gaussian" ) + random_spec() + splines(df = 20), data = gaussian_example, time = "time", group = "id", backend = "cmdstanr" ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) # categorical_example_fit code <- get_code( x = obs(g ~ lag(g) + lag(logp), family = "gaussian") + obs(p ~ lag(g) + lag(logp) + lag(b), family = "poisson") + obs(b ~ lag(b) * lag(logp) + lag(b) * lag(g), family = "bernoulli") + aux(numeric(logp) ~ log(p + 1) | init(0)), data = multichannel_example, time = "time", group = "id", backend = "cmdstanr" ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) # ordered probit model set.seed(0) n <- 100 t <- 30 x <- matrix(0, n, t) y <- matrix(0, n, t) p <- matrix(0, n, 4) alpha <- c(-1, 0, 1) for (i in seq_len(t)) { x[, i] <- rnorm(n) eta <- 0.6 * x[, i] p[, 1] <- 1 - plogis(eta - alpha[1]) p[, 2] <- plogis(eta - alpha[1]) - plogis(eta - alpha[2]) p[, 3] <- plogis(eta - alpha[2]) - plogis(eta - alpha[3]) p[, 4] <- plogis(eta - alpha[3]) y[, i] <- apply(p, 1, sample, x = 1:4, size = 1, replace = FALSE) } d <- data.frame( y = factor(c(y), levels = 1:4), x = c(x), time = rep(seq_len(t), each = n), id = rep(seq_len(n), t) ) code <- get_code( x = obs(y ~ x, family = "cumulative", link = "logit"), data = d, time = "time", group = "id", backend = "cmdstanr" ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) }) test_that("latent factor syntax is correct", { skip_if_not(run_extended_tests) set.seed(123) N <- 40L T_ <- 20L D <- 10 B <- t(splines::bs(1:T_, df = D, intercept = TRUE)) z <- rnorm(D) a <- cumsum(z) + rnorm(D) b <- cumsum(z) + rnorm(D) psi <- matrix(0, 2, T_) lambda_yi <- rnorm(N, 1, 0.2) lambda_xi <- rnorm(N, 1, 0.2) for (t in 1:T_) { psi[1, t] <- B[, t] %*% a psi[2, t] <- B[, t] %*% b } y <- matrix(0, N, T_) x <- matrix(0, N, T_) for (t in 1:T_) { y[, t] <- rnorm(N, lambda_yi * psi[1, t], 0.2) x[, t] <- rnorm(N, lambda_xi * psi[2, t], 0.2) } latent_factor_example <- data.frame( y = c(y), x = c(y), id = seq_len(N), time = rep(seq_len(T_), each = N) ) lfactor_opts <- expand.grid( nonzero_lambda = c(FALSE, TRUE), noncentered_psi = c(FALSE, TRUE), correlated = c(FALSE, TRUE) ) for (i in seq_len(nrow(lfactor_opts))) { code <- get_code( x = obs(y ~ 1, family = "gaussian") + obs(x ~ 1, family = "gaussian") + lfactor( nonzero_lambda = lfactor_opts$nonzero_lambda[i], noncentered_psi = lfactor_opts$noncentered_psi[i], correlated = lfactor_opts$correlated[i] ) + splines(df = 10), data = latent_factor_example, group = "id", time = "time" ) e <- new.env() e$file <- cmdstanr::write_stan_file(code) model <- with(e, { cmdstanr::cmdstan_model(file, compile = FALSE) }) expect_true(model$check_syntax()) } }) test_that("dynamice with cmdstanr backend works", { skip_if_not(run_extended_tests) set.seed(1) n <- 50 p <- 1000 y <- replicate(p, stats::arima.sim(list(ar = 0.7), n, sd = 0.1)) d <- data.frame(y = c(y), time = 1:n, id = rep(seq_len(p), each = n)) dmiss <- d dmiss$y[sample(seq_len(nrow(d)), size = 0.2 * nrow(d))] <- NA # Long format imputation expect_error( fit_long <- dynamice( dformula = obs(y ~ lag(y), "gaussian"), time = "time", group = "id", data = dmiss, chains = 1, refresh = 0, backend = "cmdstanr", impute_format = "long", keep_imputed = FALSE, mice_args = list(m = 3, print = FALSE) ), NA ) expect_error( print(fit_long), NA ) }) test_that("get_parameter_dims() works for cmdnstar models", { skip_if_not(run_extended_tests) set.seed(1) f <- obs(g ~ lag(g) + lag(logp), family = "gaussian") + obs(p ~ lag(g) + lag(logp) + lag(b), family = "poisson") + obs(b ~ lag(b) * lag(logp) + lag(b) * lag(g), family = "bernoulli") + aux(numeric(logp) ~ log(p + 1) | init(0)) fit_dynamite <- suppressWarnings( dynamite( dformula = f, data = multichannel_example, time = "time", group = "id", backend = "cmdstanr", show_messages = FALSE, chains = 1, parallel_chains = 1, iter_sampling = 10, iter_warmup = 10 ) ) expect_equal( get_parameter_dims(fit_dynamite), list( beta_g = 2L, a_g = 1L, sigma_g = 1L, beta_p = 3L, a_p = 1L, beta_b = 5L, a_b = 1L ) ) }) test_that("diagnostics can be obtained for cmdstanr models", { skip_if_not(run_extended_tests) set.seed(1) gaussian_fit <- dynamite( dformula = obs( y ~ -1 + z + varying(~ x + lag(y)) + random(~1), family = "gaussian" ) + random_spec() + splines(df = 20), data = gaussian_example, time = "time", group = "id", refresh = 0, backend = "cmdstanr" ) expect_error(print(gaussian_fit), NA) expect_error(hmc_diagnostics(gaussian_fit), NA) expect_error(mcmc_diagnostics(gaussian_fit), NA) })