tests/testthat/test-tolerances.R

library(testthat)
library(mrgsolve)
library(dplyr)

Sys.setenv(R_TESTS="")
options("mrgsolve_mread_quiet"=TRUE)

test_that("Switch to custom tolerances", {
  modx <- house()
  n <- mrgsolve:::neq(modx)
  expect_length(modx@vec_rtol, 0)
  expect_length(modx@vec_atol, 0)
  mod <- use_custom_tol(modx)
  expect_length(mod@vec_rtol, n)
  expect_length(mod@vec_atol, n)
  expect_equal(mod@itol, 4)
  
  expect_error(
    use_custom_tol(letters), 
    "the first argument to use_custom_tol"
  )
})

test_that("Switch to scalar tolerances", {
  modx <- use_custom_tol(house())
  n <- mrgsolve:::neq(modx)
  expect_equal(modx@itol, 4)
  mod <- use_scalar_tol(modx)
  expect_equal(mod@itol, 1)
  expect_length(mod@vec_rtol, n)
  expect_length(mod@vec_atol, n)
  
  expect_error(
    use_scalar_tol(letters), 
    "the first argument to use_scalar_tol"
  )
})

test_that("Extract tolerances", {
  mod <- house()
  
  # data frame
  data <- get_tol(mod)
  expect_equal(ncol(data), 5)
  
  # prior to initializing
  expect_true(all(is.na(data$custom_rtol)))
  expect_true(all(is.na(data$custom_atol)))
  
  # after initializing
  data <- get_tol(use_custom_tol(mod))
  expect_true(all(data$custom_rtol==mod@rtol))
  expect_true(all(data$custom_atol==mod@atol))
  
  # list
  data2 <- get_tol_list(use_custom_tol(mod))
  expect_is(data2, "list")
  expect_equal(names(data2), c("custom_rtol", "custom_atol", "scalar_rtol", "scalar_atol"))
  expect_equal(as.list(data$custom_rtol), unname(data2$custom_rtol))
  expect_equal(as.list(data$custom_atol), unname(data2$custom_atol))
  
  # names
  expect_identical(mrgsolve:::Cmt(mod), names(data2$custom_rtol))
  expect_identical(mrgsolve:::Cmt(mod), names(data2$custom_atol))
  expect_identical(mrgsolve:::Cmt(mod), names(data2$scalar_rtol))
  expect_identical(mrgsolve:::Cmt(mod), names(data2$scalar_atol))
  
  expect_error(
    get_tol(letters), 
    "the first argument to get_tol"
  )
  expect_error(
    get_tol_list(letters), 
    "the first argument to get_tol_list"
  )
})

test_that("Customize tolerances - dots", {
  mod <- use_custom_tol(house(rtol = 1e-4, atol = 1e-5))
  data <- get_tol_list(mod)
  expect_equal(data$custom_rtol, list(GUT = 1e-4, CENT = 1e-4, RESP = 1e-4))
  expect_equal(data$custom_atol, list(GUT = 1e-5, CENT = 1e-5, RESP = 1e-5))
  
  mod <- custom_rtol(mod, CENT = 1e-3, RESP = 1e-9)
  data <- get_tol_list(mod)
  expect_equal(data$custom_rtol, list(GUT = 1e-4, CENT = 1e-3, RESP = 1e-9))
  mod <- custom_atol(mod, GUT = 1e-10, RESP = 1e-12)
  data <- get_tol_list(mod)
  expect_equal(data$custom_atol, list(GUT = 1e-10, CENT = 1e-5, RESP = 1e-12))
  
  expect_error(custom_rtol(mod, kyle = 10), "Invalid compartment names")
  expect_error(custom_atol(mod, metrum = 10), "Invalid compartment names")
  
  expect_error(
    custom_rtol(letters), 
    "the first argument to custom_rtol"
  )
  expect_error(
    custom_atol(letters), 
    "the first argument to custom_atol"
  )
})

test_that("Customize tolerances - object", {
  mod <- use_custom_tol(house(rtol = 1e-4, atol = 1e-5))
  data <- get_tol_list(mod)
  
  up <- c(CENT = 1e-3, RESP = 1e-9)
  mod <- custom_rtol(mod, up)
  data <- get_tol_list(mod)
  expect_equal(data$custom_rtol, list(GUT = 1e-4, CENT = 1e-3, RESP = 1e-9))
  
  up <- c(GUT = 1e-10, RESP = 1e-12)
  mod <- custom_atol(mod, up)
  data <- get_tol_list(mod)
  expect_equal(data$custom_atol, list(GUT = 1e-10, CENT = 1e-5, RESP = 1e-12))
  
  up <- list(GUT = 1e-2, RESP = 1e-3)
  mod <- custom_atol(mod, up)
  data <- get_tol_list(mod)
  expect_equal(data$custom_atol, list(GUT = 1e-2, CENT = 1e-5, RESP = 1e-3))
  
  up <- list(GUT = 1e-3, RESP = 1e-9)
  mod <- custom_atol(mod, up)
  data <- get_tol_list(mod)
  expect_equal(data$custom_atol, list(GUT = 1e-3, CENT = 1e-5, RESP = 1e-9))
  
  up <- c(GUT = 1e-2, kyle = 10)
  expect_error(custom_rtol(mod, up), "Invalid compartment names")
  expect_error(custom_atol(mod, up), "Invalid compartment names")
})

test_that("Customize tolerances - both", {
  mod <- use_custom_tol(house(rtol = 1e-12, atol = 1e-13))
  data <- get_tol_list(mod)
  
  up <- c(GUT = 1e-3, RESP = 1e-9)
  mod <- custom_rtol(mod, up, CENT = 1e-1)
  data <- get_tol_list(mod)
  expect_equal(data$custom_rtol, list(GUT = 1e-3, CENT = 1e-1, RESP = 1e-9))
  
  up <- c(GUT = 1e-2, RESP = 1e-7)
  mod <- custom_atol(mod, up, CENT = 1e-3)
  data <- get_tol_list(mod)
  expect_equal(data$custom_atol, list(GUT = 1e-2, CENT = 1e-3, RESP = 1e-7))
  
  up <- c(GUT = 1, CENT = 2, RESP = 3)
  mod <- custom_rtol(mod, up, RESP = 4, GUT = 5, CENT = 6)
  data <- get_tol_list(mod)
  expect_equal(data$custom_rtol, list(GUT = 5, CENT = 6, RESP = 4))
  
})

test_that("Customize tolerances via custom_tol", {
  code <- "$cmt a b c\n$env xrtol=c(a=1,b=2,c=3); xatol=c(a=4,b=5,c=6); xx=letters"  
  modx <- mcode("customize-via-custom-tol", code, compile = FALSE)
  
  mod <- custom_tol(modx, .rtol = c(a = 2, b = 3, c = 4))
  data <- get_tol(mod)  
  expect_equal(data$custom_rtol, c(2,3,4))
  
  mod <- custom_tol(modx, .atol = c(a = 2, b = 3, c = 4))
  data <- get_tol(mod)  
  expect_equal(data$custom_atol, c(2,3,4))
  
  # Accepts character
  mod <- custom_tol(modx, .rtol = "xrtol")
  data <- get_tol(mod)  
  expect_equal(data$custom_rtol, c(1,2,3))
  
  mod <- custom_tol(modx, .atol = "xatol")
  data <- get_tol(mod)  
  expect_equal(data$custom_atol, c(4,5,6))
  
  expect_error(
    custom_tol(modx, .rtol = "foo"), 
    "object 'foo' of mode 'numeric' was not found"
  )
  expect_error(
    custom_tol(modx, .atol = "bar"), 
    "object 'bar' of mode 'numeric' was not found"
  )
  expect_error(
    custom_tol(modx, .rtol = "xx"), 
    "object 'xx' of mode 'numeric' was not found"
  )
})

test_that("Reset tolerances", {
  mod <- custom_rtol(house(), .default = 1e-2)   
  n <- mrgsolve:::neq(mod)
  
  data <- get_tol(mod)
  expect_equal(data$custom_rtol, rep(1e-2, n))
  
  # Just reset rtol
  mod <- reset_rtol(mod, rtol = 1e-7)
  expect_equal(mod@rtol, 1e-7)
  data <- get_tol(mod)
  expect_equal(data$custom_rtol, rep(1e-7, n))
  
  # Just reset atol
  mod <- reset_atol(mod, atol = 1e-9)
  expect_equal(mod@atol, 1e-9)
  data <- get_tol(mod)
  expect_equal(data$custom_atol, rep(1e-9, n))
  
  # Reset both rtol and atol
  mod <- reset_tol(mod, rtol = 1e-2, atol = 1e-6)
  
  expect_equal(mod@rtol, 1e-2)
  data <- get_tol(mod)
  expect_equal(data$custom_rtol, rep(1e-2, n))
  
  expect_equal(mod@atol, 1e-6)
  data <- get_tol(mod)
  expect_equal(data$custom_atol, rep(1e-6, n))
  
  # itol is preserved
  mod <- house()
  expect_equal(mod@itol, 1)
  mod <- reset_tol(mod, rtol = 1e-5, atol = 1e-8)
  expect_equal(mod@itol, 1)
  
  mod <- house(rtol = 1, atol = 2)
  mod <- custom_rtol(mod, .default = 3)
  mod <- custom_atol(mod, .default = 4)
  mod <- use_scalar_tol(mod)
  mod <- reset_tol(mod)
  expect_equal(mod@itol, 1)
  expect_equal(mod@rtol, 1)
  expect_equal(mod@atol, 2)
  
  mod <- house(rtol = 1)
  mod <- custom_rtol(mod, .default = 3)
  mod <- use_scalar_tol(mod)
  mod <- reset_rtol(mod)
  expect_equal(mod@itol, 1)
  expect_equal(mod@rtol, 1)
  expect_equal(mod@atol, 1e-8, tolerance=1e-5)
  
  mod <- house(atol = 5)
  mod <- custom_atol(mod, .default = 5)
  mod <- use_scalar_tol(mod)
  mod <- reset_atol(mod)
  expect_equal(mod@itol, 1)
  expect_equal(mod@rtol, 1e-8, tolerance=1e-5)
  expect_equal(mod@atol, 5)
  
  expect_error(
    reset_tol(letters), 
    "the first argument to reset_tol"
  )
  expect_error(
    reset_tol(letters), 
    "the first argument to reset_tol"
  )
  expect_error(
    reset_rtol(letters), 
    "the first argument to reset_rtol"
  )
  expect_error(
    reset_atol(letters), 
    "the first argument to reset_atol"
  )
})

test_that("Detect corrupted vec_rtol and vec_atol data", {
  h <- house()
  
  mod <- use_custom_tol(h)
  mod@vec_rtol <- mod@vec_rtol[1:2]
  expect_error(get_tol(mod), "custom rtol vector is not the correct size")
  
  mod <- use_custom_tol(h)
  set.seed(1023)
  names(mod@vec_rtol) <- sample(mod@vec_rtol)
  expect_error(get_tol(mod), "custom rtol vector is in the wrong order")
  
  mod <- use_custom_tol(h)
  mod@vec_atol <- mod@vec_atol[1:2]
  expect_error(get_tol(mod), "custom atol vector is not the correct size")
  
  mod <- use_custom_tol(h)
  set.seed(1023)
  names(mod@vec_atol) <- sample(mod@vec_atol)
  expect_error(get_tol(mod), "custom atol vector is in the wrong order")
})

test_that("Bad customize input", {
  mod <- use_custom_tol(house())
  expect_error(
    custom_rtol(mod, CENT = "ABC"), 
    "tolerance data must be a named numeric list or vector"
  )
  expect_error(
    custom_atol(mod, atol = c(1,2,3)), 
    "tolerance data must be a named numeric list or vector"
  )
  expect_error(
    custom_rtol(mod, mtcars), 
    "tolerance data must be a named numeric list or vector"
  )
})

test_that("Error when either atol or rtol not customized", {
  mod <- use_custom_tol(house())
  mod@vec_atol <- numeric(0)
  suppressMessages(
    expect_error(
      mrgsim(mod), 
      "itol is > 1, but atol/rtol vector(s) are not the expected size.", 
      fixed = TRUE
    )
  )
  expect_message(
    try(mrgsim(mod), silent = TRUE), 
    "[lsoda] expecting atol vector with size 3, but got size 0.", 
    fixed = TRUE
  )
  mod <- use_custom_tol(house())
  mod@vec_rtol <- c(a = 1e-3)
  suppressMessages(
    expect_error(
      mrgsim(mod), 
      "itol is > 1, but atol/rtol vector(s) are not the expected size.",
      fixed = TRUE
    )
  )
  expect_message(
    try(mrgsim(mod), silent = TRUE), 
    "[lsoda] expecting rtol vector with size 3, but got size 1.", 
    fixed = TRUE
  )
})

test_that("Detect numerical differences using custom tol", {
  
  # atol
  mod1 <- house(atol = 1e-3)
  mod2 <- custom_atol(house(), GUT = 1e-20)
  dose <- ev(amt = 1)
  
  set.seed(12345)
  out0 <- mrgsim_e(mod1, dose, end = 480)
  set.seed(12345)
  out1 <- mrgsim_e(mod1, dose, end = 480)
  set.seed(12345)
  out2 <- mrgsim_e(mod2, dose, end = 480)
  
  expect_equal(out0$CENT, out1$CENT, tolerance = 1e-7)
  expect_true(nrow(filter_sims(out1, CENT < 0)) > 0)
  expect_true(nrow(filter_sims(out2, CENT < 0)) == 0)
  
  diff1 <- abs(out1$CENT - out0$CENT)
  diff2 <- abs(out2$CENT - out1$CENT)
  expect_true(sum(diff2)/100 > sum(diff1))
  
  # rtol 
  mod1 <- house(rtol = 1e-12)
  mod2 <- custom_rtol(house(), CENT = 1e-2)
  
  set.seed(12345)
  out0 <- mrgsim_e(mod1, dose, end = 480)
  set.seed(12345)
  out1 <- mrgsim_e(mod1, dose, end = 480)
  set.seed(12345)
  out2 <- mrgsim_e(mod2, dose, end = 480)
  
  diff1 <- abs(out1$CENT - out0$CENT)
  diff2 <- abs(out2$CENT - out1$CENT)
  
  expect_true(sum(diff2)/100 > sum(diff1))
})

Try the mrgsolve package in your browser

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

mrgsolve documentation built on Aug. 22, 2025, 9:12 a.m.