tests/testthat/test-link-sampling.R

# Tests for link function sampling
#
# These tests verify that all 11 link functions can run MCMC sampling
# without errors. They use minimal iterations for speed.
#
# NOTE: These tests require compiled Stan models.
# - Skipped in devtools::test() (development mode)
# - Run in R CMD check (after package build/install)

# Helper to check if Stan models are compiled
stan_models_compiled <- function() {
  model_path <- system.file("bin", "stan", "clm_base", package = "clmstan")
  nzchar(model_path) && file.exists(model_path)
}

# Helper function to generate simple ordinal test data
# This is a simplified version that doesn't depend on clm_cdf
generate_test_data <- function(n = 50, K = 3, link = "logit",
                                link_param = list(), seed = NULL) {
  if (!is.null(seed)) set.seed(seed)

  # Generate simple data using logistic latent variable
  x1 <- rnorm(n)
  latent <- 0.5 * x1 + rlogis(n)

  # Create thresholds
  thresholds <- seq(-1, 1, length.out = K - 1)

  # Create ordinal response
  y <- cut(latent, breaks = c(-Inf, thresholds, Inf), labels = 1:K)

  data <- data.frame(y = y, x1 = x1)

  list(
    data = data,
    true_params = list(
      beta = c(0.5),
      thresholds = c(0, thresholds[-1] - thresholds[1]),
      link = link,
      link_param = link_param
    )
  )
}

# =============================================================================
# Standard Link Functions (5)
# =============================================================================

test_that("logit link runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 3, link = "logit", seed = 101)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "logit",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("probit link runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 3, link = "probit", seed = 102)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "probit",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("cloglog link runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 3, link = "cloglog", seed = 103)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "cloglog",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("loglog link runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 3, link = "loglog", seed = 104)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "loglog",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("cauchit link runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 3, link = "cauchit", seed = 105)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "cauchit",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

# =============================================================================
# Flexible Link Functions (6) - Fixed Parameters
# =============================================================================

test_that("tlink with fixed df runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "tlink",
    link_param = list(df = 8),
    seed = 106
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "tlink",
      link_param = list(df = 8),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("aranda_ordaz with fixed lambda runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "aranda_ordaz",
    link_param = list(lambda = 1),
    seed = 107
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "aranda_ordaz",
      link_param = list(lambda = 1),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("sp with fixed r runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "sp",
    link_param = list(r = 1, base = "logit"),
    seed = 108
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "sp",
      link_param = list(r = 1),
      base = "logit",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("log_gamma with fixed lambda runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "log_gamma",
    link_param = list(lambda = 0.5),
    seed = 109
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "log_gamma",
      link_param = list(lambda = 0.5),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("gev with fixed xi runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "gev",
    link_param = list(xi = 0.2),
    seed = 110
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "gev",
      link_param = list(xi = 0.2),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("aep with fixed theta1, theta2 runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "aep",
    link_param = list(theta1 = 2, theta2 = 2),
    seed = 111
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "aep",
      link_param = list(theta1 = 2, theta2 = 2),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

# =============================================================================
# Flexible Link Functions - Estimated Parameters (Full Model)
# =============================================================================

test_that("gev with estimated xi runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  # Use data generated with xi = 0.2
  sim <- generate_test_data(
    n = 50, K = 3,
    link = "gev",
    link_param = list(xi = 0.2),
    seed = 120
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "gev",
      link_param = list(xi = "estimate"),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("aep with estimated theta1, theta2 runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(
    n = 50, K = 3,
    link = "aep",
    link_param = list(theta1 = 2, theta2 = 2),
    seed = 121
  )

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "aep",
      link_param = list(theta1 = "estimate", theta2 = "estimate"),
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

# =============================================================================
# Different Threshold Structures
# =============================================================================

test_that("logit with equidistant thresholds runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  sim <- generate_test_data(n = 50, K = 4, link = "logit", seed = 130)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "logit",
      threshold = "equidistant",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

test_that("logit with symmetric thresholds runs without error", {
  skip_on_cran()
  skip_if_not(instantiate::stan_cmdstan_exists(), "CmdStan not available")
  skip_if_not(stan_models_compiled(), "Stan models not compiled")

  # Symmetric thresholds work best with odd K
  sim <- generate_test_data(n = 50, K = 5, link = "logit", seed = 131)

  expect_no_error({
    fit <- clm_stan(
      y ~ x1,
      data = sim$data,
      link = "logit",
      threshold = "symmetric",
      chains = 1,
      iter = 100,
      warmup = 50,
      refresh = 0
    )
  })
})

Try the clmstan package in your browser

Any scripts or data that you put into this service are public.

clmstan documentation built on June 29, 2026, 5:06 p.m.