tests/testthat/test-update_method.R

context("Test various update methods")

if (isTRUE(as.logical(Sys.getenv("CI")))){
  # If on CI
  NITER <- 2
  env_test <- "CI"
}else if (!identical(Sys.getenv("NOT_CRAN"), "true")){
  # If on CRAN
  NITER <- 2
  env_test <- "CRAN"
  set.seed(191)
}else{
  # If on local machine
  NITER <- 2000
  env_test <- 'local'
}

test_that("Joint vs Cyclical Update", {
  
  skip_on_cran()
  
  N <- 1000
  G <- 20
  x <- rnorm(N)
  g <- sample(1:G, N, replace = T)
  g2 <- sample(1:10, N, replace = T)
  alpha <- rnorm(G)
  alpha2 <- rnorm(10)
  
  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[g] + alpha2[g2]))


  for (v in c("weak", "partial", "strong")) {
    
    ex_vglmer_cyclic <- vglmer(
      formula = y ~ x + (1 | g) + (1 | g2), family = "binomial",
      data = NULL, control = vglmer_control(factorization_method = v, linpred_method = "cyclical", init = "zero")
    )

    ex_vglmer_joint <- vglmer(
      formula = y ~ x + (1 | g) + (1 | g2), family = "binomial",
      data = NULL, control = vglmer_control(factorization_method = v, linpred_method = "joint", init = "zero")
    )

    fmt_vglmer_cyclic <- format_vglmer(ex_vglmer_cyclic)
    fmt_vglmer_joint <- format_vglmer(ex_vglmer_joint)

    expect_equivalent(fmt_vglmer_cyclic, fmt_vglmer_joint, tolerance = 1e-4, scale = 1)

    if (v == "strong") {

      ex_vglmer_normal <- vglmer(
        formula = y ~ x + (1 | g) + (1 | g2), family = "binomial",
        data = NULL, 
        control = vglmer_control(factorization_method = v, 
           do_SQUAREM = FALSE,
          linpred_method = "solve_normal", init = "zero")
      )

      fmt_vglmer_normal <- format_vglmer(ex_vglmer_normal)
      expect_equivalent(fmt_vglmer_normal, fmt_vglmer_joint, tolerance = 1e-4, scale = 1)
    }
  }
})


test_that("Joint vs Cyclical Update (Nested)", {
  
  skip_on_cran()

  N <- 1000
  G <- 20
  x <- rnorm(N)
  g <- sample(1:G, N, replace = T)
  g2 <- floor(g/3) + 1
  alpha <- rnorm(G)
  alpha2 <- rnorm(max(g2))
  
  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[g] + alpha2[g2]))
  
  
  fmla <- y ~ x + (1 | g2) + (1 | g)
  fmla_perm <- y ~ x + (1 | g) + (1 | g2)
  
  warning('This should work with *default* initialization...')
  
  ex_vglmer_cyclic_perm <- suppressMessages(vglmer(
    formula = fmla_perm, family = "binomial",
    data = NULL, control = vglmer_control(
      iterations = 100, print_prog = 500,
      init = 'EM', linpred_method = "cyclical")
  ))
  
  ex_vglmer_joint_perm <- vglmer(
    formula = fmla_perm, family = "binomial",
    control = vglmer_control(iterations = 100, print_prog = 500),
    data = NULL
  )
  
  ex_vglmer_cyclic <- suppressMessages(vglmer(
    formula = fmla, family = "binomial",
    data = NULL, control = vglmer_control(
      iterations = 100, print_prog = 500,
      init = 'EM', linpred_method = "cyclical")
  ))
  
  expect_equal(names(ranef(ex_vglmer_cyclic)), c('g2', 'g'))  
  expect_equal(names(ranef(ex_vglmer_cyclic_perm)), c('g', 'g2'))  
  
  ex_vglmer_joint <- vglmer(
    formula = fmla, family = "binomial",
    control = vglmer_control(iterations = 100,
                             print_prog = 500),
    data = NULL 
  )
  
  fmt_vglmer_cyclic_perm <- format_vglmer(ex_vglmer_cyclic_perm)
  fmt_vglmer_cyclic <- format_vglmer(ex_vglmer_cyclic)
  fmt_vglmer_joint <- format_vglmer(ex_vglmer_joint)
  
  expect_equivalent(fmt_vglmer_cyclic, fmt_vglmer_joint, tolerance = 1e-3, scale = 1)
  expect_equivalent(fmt_vglmer_cyclic, 
                    fmt_vglmer_cyclic_perm[
                      match(fmt_vglmer_cyclic$name, fmt_vglmer_cyclic_perm$name),
                    ], tolerance = 1e-2, scale = 1)
  
  expect_equivalent(ex_vglmer_joint$ELBO, ex_vglmer_joint_perm$ELBO,
                    tolerance = 1e-5, scale = 1)
  ex_vglmer_cyclic$ELBO$it <- NULL
  ex_vglmer_cyclic_perm$ELBO$it <- NULL
  expect_equivalent(ex_vglmer_cyclic$ELBO[,1], ex_vglmer_cyclic_perm$ELBO[,1],
                    tolerance = 1e-2, scale = 1)
  
})

test_that("Compare PX vs Non-PX", {
  
  skip_on_cran()
  
  N <- 1000
  G <- 20
  x <- rnorm(N)
  g <- sample(1:G, N, replace = T)
  alpha <- rnorm(G)

  y <- rbinom(n = N, size = 1, prob = plogis(-1 + x + alpha[g]))


  ex_vglmer_px_t <- vglmer(
    formula = y ~ x + (1 | g), family = "binomial",
    data = NULL, control = vglmer_control(
      factorization_method = "strong", debug_px = TRUE,
      parameter_expansion = "translation", debug_ELBO = T, init = "zero"
    )
  )
  
  ex_vglmer_px <- vglmer(
    formula = y ~ x + (1 | g), family = "binomial",
    data = NULL, control = vglmer_control(
      factorization_method = "strong", debug_px = TRUE,
      parameter_expansion = "mean", debug_ELBO = T, init = "zero"
    )
  )

  ex_vglmer_no <- vglmer(
    formula = y ~ x + (1 | g), family = "binomial",
    data = NULL, control = vglmer_control(
      factorization_method = "strong",
      parameter_expansion = "none", debug_ELBO = T, init = "zero"
    )
  )

  expect_gte(min(diff(ex_vglmer_no$ELBO_trajectory$ELBO)), -sqrt(.Machine$double.eps))
  expect_gte(min(diff(ex_vglmer_px$ELBO_trajectory$ELBO)), -sqrt(.Machine$double.eps))
  expect_gte(min(diff(ex_vglmer_px_t$ELBO_trajectory$ELBO)), -sqrt(.Machine$double.eps))
  
  fmt_vglmer_px <- format_vglmer(ex_vglmer_px)
  fmt_vglmer_no <- format_vglmer(ex_vglmer_no)
  fmt_vglmer_px_t <- format_vglmer(ex_vglmer_px_t)
  
  expect_equivalent(fmt_vglmer_px, fmt_vglmer_no, tolerance = 1e-4, scale = 1)
  expect_equivalent(fmt_vglmer_px, fmt_vglmer_px_t, tolerance = 1e-4, scale = 1)
})

test_that("Compare VI r methods", {
  
  skip_on_cran()

  N <- 1000
  G <- 50
  x <- rnorm(N)
  g <- sample(1:G, N, replace = T)
  alpha <- rnorm(G)

  y <- rnbinom(n = N, mu = exp(-1 + x + alpha[g]), size = 5)
  data <- data.frame(y = y, x = x, g = g)

  list_output <- list()
  list_r <- list()
  
  for (v in c("VEM", "fixed")) {
    
    if (v == 'fixed'){
      v <- 1
    }
    example_vglmer <- suppressWarnings(vglmer(
      formula = y ~ x + (1 | g), data = data,
      family = "negbin",
      control = vglmer_control(parameter_expansion = 'mean',
        factorization_method = "strong",
        iterations = ifelse(v != 'VEM', 2, 1000),
        vi_r_method = v, init = "random"
      )
    ))
    # Test whether it monotonically increases
    if (v == "VEM") {
      expect_gte(min(diff(example_vglmer$ELBO_trajectory$ELBO)), -sqrt(.Machine$double.eps))
    }
    
    fmt_vglmer <- format_vglmer(example_vglmer)
    names(fmt_vglmer)[-1] <- paste0(v, "_", names(fmt_vglmer)[-1])
  }

  # Disable negative binomial tests for now
  # list_output <- Reduce(function(a, b) {
  #   merge(a, b, by = "name")
  # }, list_output)
  # expect_gte(min(as.vector(cor(list_output[, c("Laplace_mean", "delta_mean", "VEM_mean")]))), 0.95)
  # expect_gte(min(as.vector(cor(list_output[, c("Laplace_var", "delta_var", "VEM_var")]))), 0.95)
  # 
  # all_r <- sapply(list_r, FUN = function(i) {
  #   i$mu
  # })
  # # Check that mu are quite close
  # expect_lte(diff(range(all_r)), 0.02)
  # # Check that the mu standard errors are close for Laplace/delta
  # expect_lte(diff(sqrt(sapply(list_r, FUN = function(i) {
  #   i$sigma
  # }))[-1]), 0.02)
})
mgoplerud/vglmer documentation built on Jan. 22, 2025, 6:43 p.m.