tests/testthat/test-compilation.R

test_that("name of a population can only be a scalar character value", {
  error_msg <- "A population name must be a character scalar value"
  expect_error(population(name = c("asd", "xyz"), time = 1, N = 100), error_msg)
  expect_s3_class(population(name = "asd", time = 1, N = 100), "slendr_pop")
})

test_that("'competition' must be specified in compile_model() if missing", {
  map <- readRDS("map.rds")
  p <- population(mating = 10, dispersal = 10, name = "pop1", N = 700, time = 40000, radius = 600000, center = c(10, 25), map = map)
  expect_error(compile_model(populations = list(p), generation_time = 30, resolution = 11e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"),
               "Parameter 'competition' missing", fixed = TRUE)
  skip_if(Sys.info()[["sysname"]] == "Windows") # new meaningless CRAN warnings
  expect_silent(compile_model(competition = 50e3, populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"))
})

test_that("'mating' must be specified in compile_model() if missing", {
  map <- readRDS("map.rds")
  p <- population(competition = 10, dispersal = 10, name = "pop1", N = 700, time = 40000, radius = 600000, center = c(10, 25), map = map)
  skip_if(Sys.info()[["sysname"]] == "Windows") # new meaningless CRAN warnings
  expect_error(compile_model(populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"),
               "Parameter 'mating' missing", fixed = TRUE)
  expect_silent(compile_model(mating = 50e3, populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"))
})

test_that("'dispersal' must be specified in compile_model() if missing", {
  map <- readRDS("map.rds")
  p <- population(competition = 10, mating = 10, name = "pop1", N = 700, time = 40000, radius = 600000, center = c(10, 25), map = map)
  skip_if(Sys.info()[["sysname"]] == "Windows") # new meaningless CRAN warnings
  expect_error(compile_model(populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"),
               "Parameter 'dispersal' missing", fixed = TRUE)
  expect_silent(compile_model(dispersal = 50e3, populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"))
})

test_that("'competition', 'mating', and 'dispersal' do not have to be specified in compile_model() if already present", {
  map <- readRDS("map.rds")
  p <- population(competition = 10, mating = 10, dispersal = 10, name = "pop1", N = 700, time = 40000, radius = 600000, center = c(10, 25), map = map)
  skip_if(Sys.info()[["sysname"]] == "Windows") # new meaningless CRAN warnings
  expect_silent(compile_model(populations = list(p), generation_time = 30, resolution = 10e3, path = tempfile(), overwrite = TRUE, force = TRUE, direction = "backward"))
})

test_that("presence of all parents is enforced", {
  p1 <- population(name = "pop1", N = 700, time = 40000)
  p2 <- population(name = "pop2", parent = p1, N = 700, time = 4000)
  expect_error(compile_model(populations = p2, path = file.path(tempdir(), "missing-parent"), generation_time = 30),
               "The following parent populations are missing: pop1")
})

test_that("invalid blank maps are prevented", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  pop <- population("pop", time = 1, N = 100, map = map, center = c(50, 50), radius = 0.5)

  expect_error(compile_model(pop, generation_time = 1, competition = 1,
                       mating = 50, dispersal = 1, simulation_length = 300,
                       resolution = 1),
               "No occupiable pixel on a rasterized map")
})

test_that("deletion in non-interactive mode must be forced", {
  skip_if(interactive())
  p <- population(name = "pop", N = 700, time = 100) %>% resize(N = 100, time = 50, how = "step")
  directory <- file.path(tempdir(), "dir-forced")
  dir.create(directory)
  expect_error(model <- compile_model(p, path = directory, generation_time = 30, overwrite = TRUE),
               "Compilation aborted")
  model <- compile_model(p, path = directory, generation_time = 30, overwrite = TRUE, force = TRUE)
  expect_true(grepl("dir-forced$", model$path))
})

skip_if(!is_slendr_env_present())
init_env(quiet = TRUE)

test_that("sequence length can only be an integer number (SLiM)", {
  p <- population(name = "pop1", N = 700, time = 1)
  model <- compile_model(populations = p, generation_time = 30, simulation_length = 1000)
  error_msg <- "Sequence length must be a non-negative integer number"
  expect_error(slim(model, sequence_length = 0.1, recombination_rate = 0), error_msg)
  expect_error(slim(model, sequence_length = 0.1, recombination_rate = 0), error_msg)
  expect_silent(slim(model, sequence_length = 1e6, recombination_rate = 0))
})

test_that("sequence length can only be an integer number (msprime)", {
  p <- population(name = "pop1", N = 700, time = 1)
  model <- compile_model(populations = p, generation_time = 30, simulation_length = 1000)
  error_msg <- "Sequence length must be a non-negative integer number"
  expect_error(msprime(model, sequence_length = 0.1, recombination_rate = 0), error_msg)
  expect_error(msprime(model, sequence_length = 0.1, recombination_rate = 0), error_msg)
  expect_silent(msprime(model, sequence_length = 1e6, recombination_rate = 0))
})

test_that("recombination rate can only be an integer number (SLiM)", {
  p <- population(name = "pop1", N = 700, time = 1)
  model <- compile_model(populations = p, generation_time = 30, simulation_length = 1000)
  error_msg <- "Recombination rate must be a numeric value"
  expect_error(slim(model, sequence_length = 100, recombination_rate = "asdf"), error_msg)
  expect_error(slim(model, sequence_length = 100, recombination_rate = -1), error_msg)
  expect_s3_class(slim(model, sequence_length = 100, recombination_rate = 1e-8), "slendr_ts")
})

test_that("recombination rate can only be an integer number (msprime)", {
  p <- population(name = "pop1", N = 700, time = 1)
  model <- compile_model(populations = p, generation_time = 30, simulation_length = 1000)
  error_msg <- "Recombination rate must be a numeric value"
  expect_error(msprime(model, sequence_length = 100, recombination_rate = "asdf", random_seed = 42), error_msg)
  expect_error(msprime(model, sequence_length = 100, recombination_rate = -1, random_seed = 42), error_msg)
  expect_silent(msprime(model, sequence_length = 100, recombination_rate = 1e-8, random_seed = 42))
})

test_that("mix of spatial and non-spatial populations gives a warning", {
  map <- readRDS("map.rds")
  p1 <- population(name = "pop1", N = 700, map = map, time = 40000)
  p2 <- population(name = "pop2", N = 700, time = 4000)
  expect_warning(
    compile_model(populations = list(p1, p2), generation_time = 30, direction = "backward",
                  resolution = 10e3, competition = 10e3, mating = 10e3, dispersal = 10e3),
    "Model containing a mix of spatial and non-spatial populations"
  )
})

test_that("purely spatial populations compile in silence", {
  map <- readRDS("map.rds")
  p1 <- population(name = "pop1", N = 700, map = map, time = 40000)
  p2 <- population(name = "pop2", N = 700, map = map, time = 4000)
  expect_s3_class(
    compile_model(populations = list(p1, p2), generation_time = 30, direction = "backward",
                  resolution = 10e3, competition = 10e3, mating = 10e3, dispersal = 10e3),
    "slendr_model"
  )
})

test_that("purely non-spatial populations compile in silence", {
  p1 <- population(name = "pop1", N = 700, time = 40000)
  p2 <- population(name = "pop2", N = 700, time = 4000)
  expect_s3_class(
    compile_model(populations = list(p1, p2), generation_time = 30, direction = "backward"),
    "slendr_model"
  )
})

# tests of the extract_parameters function --------------------------------------

# first compile a model (taken from vignette number 4)

afr <- population("AFR", time = 52000, N = 3000)
ooa <- population("OOA", parent = afr, time = 51000, N = 500, remove = 25000) %>%
  resize(N = 2000, time = 40000, how = "step")
ehg <- population("EHG", parent = ooa, time = 28000, N = 1000, remove = 6000)
eur <- population("EUR", parent = ehg, time = 25000, N = 2000) %>%
  resize(N = 10000, how = "exponential", time = 5000, end = 0)
ana <- population("ANA", time = 28000, N = 3000, parent = ooa, remove = 4000)
yam <- population("YAM", time = 7000, N = 500, parent = ehg, remove = 2500)

gf <- list(
  gene_flow(from = ana, to = yam, rate = 0.5, start = 6500, end = 6400),
  gene_flow(from = ana, to = eur, rate = 0.5, start = 8000, end = 6000),
  gene_flow(from = yam, to = eur, rate = 0.75, start = 4000, end = 3000)
)

# full model
model <- compile_model(populations = list(afr, ooa, ehg, eur, ana, yam),
                       gene_flow = gf, generation_time = 30)

# no-gene-flow model
model_nogf <- compile_model(populations = list(afr, ooa, ehg, eur, ana, yam),
                            generation_time = 30)

# no-resizes model
ooa <- population("OOA", parent = afr, time = 51000, N = 500, remove = 25000)
eur <- population("EUR", parent = ehg, time = 25000, N = 2000)
model_noresizes <- compile_model(populations = list(afr, ooa, ehg, eur, ana, yam),
                                 gene_flow = gf, generation_time = 30)

# model with no dynamics
model_base <- compile_model(populations = list(afr, ooa, ehg, eur, ana, yam),
                            generation_time = 30)

test_that("parameters of the full model are extracted properly (from model)", {
  pars <- extract_parameters(model)
  expect_length(pars, 3)
  expect_equal(names(pars), c("splits", "gene_flows", "resizes"))
})

test_that("parameters of the no-gf model are extracted properly (from model)", {
  pars <- extract_parameters(model_nogf)
  expect_length(pars, 2)
  expect_equal(names(pars), c("splits", "resizes"))
})

test_that("parameters of the no-resize model are extracted properly (from model)", {
  pars <- extract_parameters(model_noresizes)
  expect_length(pars, 2)
  expect_equal(names(pars), c("splits", "gene_flows"))
})

test_that("parameters of the base model are extracted properly (from model)", {
  pars <- extract_parameters(model_base)
  expect_length(pars, 1)
  expect_equal(names(pars), "splits")
})

skip_if(!is_slendr_env_present())
init_env(quiet = TRUE)

ts <- msprime(model, sequence_length = 1, recombination_rate = 0)
ts_nogf <- msprime(model_nogf, sequence_length = 1, recombination_rate = 0)
ts_noresizes <- msprime(model_noresizes, sequence_length = 1, recombination_rate = 0)
ts_base <- msprime(model_base, sequence_length = 1, recombination_rate = 0)

test_that("parameters of the full model are extracted properly (from t.s.)", {
  pars <- extract_parameters(ts)
  expect_length(pars, 3)
  expect_equal(names(pars), c("splits", "gene_flows", "resizes"))
})

test_that("parameters of the no-gf model are extracted properly (from t.s.)", {
  pars <- extract_parameters(ts_nogf)
  expect_length(pars, 2)
  expect_equal(names(pars), c("splits", "resizes"))
})

test_that("parameters of the no-resize model are extracted properly (from t.s.)", {
  pars <- extract_parameters(ts_noresizes)
  expect_length(pars, 2)
  expect_equal(names(pars), c("splits", "gene_flows"))
})

test_that("parameters of the base model are extracted properly (from t.s.)", {
  pars <- extract_parameters(ts_base)
  expect_length(pars, 1)
  expect_equal(names(pars), "splits")
})

test_that("extract_parameters fails gracefully when non-slendr tree sequence is used", {
  output <- normalizePath(tempfile(), winslash = "/", mustWork = FALSE)
  py_cmd <- sprintf("import msprime; msprime.sim_ancestry(%d, random_seed=42, population_size=%d).dump('%s')", 10, 10, output)
  reticulate::py_run_string(py_cmd)
  ts <- ts_read(output)
  expect_error(extract_parameters(ts), "No slendr model configuration present in the tree sequence.")
})
bodkan/slendr documentation built on Dec. 19, 2024, 11:41 p.m.