tests/testthat/test-time-direction.R

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

test_that("forward and backward time model objects are equivalent", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")

  # forward direction
  p1 <- population(name = "p1",              time = 1, N = 1, center = c(1, 1), radius = 1, map = map)
  p2 <- population(name = "p2", parent = p1, time = 2, N = 1, center = c(1, 1), radius = 1)
  p3 <- population(name = "p3", parent = p1, time = 3, N = 1, center = c(1, 1), radius = 1)
  p4 <- population(name = "p4", parent = p3, time = 4, N = 1, center = c(1, 1), radius = 1)
  p5 <- population(name = "p5", parent = p2, time = 5, N = 1, center = c(1, 1), radius = 1)

  geneflows <- gene_flow(p1, p2, start = 3, end = 4, rate = 0.5, overlap = FALSE)

  forward <- compile_model(
    path = file.path(tempdir(), "tmp-forward"),
    populations = list(p1, p2, p3, p4, p5), gene_flow = geneflows,
    generation_time = 1, resolution = 1,
    overwrite = TRUE, force = TRUE,
    competition = 1, mating = 1, dispersal = 1,
    simulation_length = 5
  )

  # backward direction
  p1 <- population(name = "p1",              time = 5, N = 1, center = c(1, 1), radius = 1, map = map)
  p2 <- population(name = "p2", parent = p1, time = 4, N = 1, center = c(1, 1), radius = 1)
  p3 <- population(name = "p3", parent = p1, time = 3, N = 1, center = c(1, 1), radius = 1)
  p4 <- population(name = "p4", parent = p3, time = 2, N = 1, center = c(1, 1), radius = 1)
  p5 <- population(name = "p5", parent = p2, time = 1, N = 1, center = c(1, 1), radius = 1)

  geneflows <- gene_flow(p1, p2, start = 3, end = 2, rate = 0.5, overlap = FALSE)

  backward <- compile_model(
    path = file.path(tempdir(), "tmp-backward"),
    populations = list(p1, p2, p3, p4, p5), gene_flow = geneflows,
    generation_time = 1, resolution = 1,
    overwrite = TRUE, force = TRUE,
    competition = 1, mating = 1, dispersal = 1
  )

  expect_true(all.equal(forward$splits[, grep("_orig", colnames(forward$splits), value = TRUE, invert = TRUE)],
                        backward$splits[, grep("_orig", colnames(backward$splits), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)],
                        backward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$maps[, c("pop", "pop_id", "time_gen")],
                        backward$maps[, c("pop", "pop_id","time_gen")]))

  components <- c("generation_time", "resolution", "world")
  expect_true(all(sapply(components, function(i) all.equal(forward[[i]], backward[[i]]))))
})

test_that("forward and backward models yield the same simulation result", {
  map <- readRDS("map.rds")

  # forward simulation ------------------------------------------------------
  p1 <- population(name = "pop1", N = 100, time = 1, radius = 600000, center = c(10, 25), map = map)
  p2 <- population(name = "pop2", parent = p1, time = 10, N = 100, center = c(10, 25), radius = 300000) %>%
    move(trajectory = list(c(25, 25), c(40, 30), c(40, 40), c(50, 50)), start = 11, end = 200, snapshots = 30)
  p3 <- population(name = "pop3", parent = p2, time = 200, N = 100, polygon = list(c(-10, 50), c(10, 50), c(20, 53), c(40, 60), c(40, 70), c(-10, 65)))
  p4 <- population(name = "pop4", parent = p2, time = 200, N = 100, polygon = list(c(-10, 35), c(20, 37), c(25, 40), c(30, 45), c(10, 50), c(-10, 45)))
  p5 <- population(name = "pop5", parent = p1, time = 300, N = 100, center = c(10, 25), radius = 300000) %>%
    move(trajectory = list(c(-5, 33), c(-5, 40)), start = 301, end = 400, snapshots = 20)

  geneflow <- list(
    gene_flow(from = p5, to = p4, rate = 0.2, start = 310, end = 350, overlap = F),
    gene_flow(from = p5, to = p3, rate = 0.3, start = 340, end = 480, overlap = F)
  )

  forward <- compile_model(
    path = file.path(tempdir(), "tmp-forward"),
    populations = list(p1, p2, p3, p4, p5),
    gene_flow = geneflow,
    generation_time = 1,
    resolution = 10000,
    overwrite = TRUE, force = TRUE,
    competition = 100e3, mating = 100e3, dispersal = 100e3,
    simulation_length = 480
  )

  # backward simulation -----------------------------------------------------
  p1 <- population(name = "pop1", N = 100, time = 480, radius = 600000, center = c(10, 25), map = map)
  p2 <- population(name = "pop2", parent = p1, time = 471, N = 100, center = c(10, 25), radius = 300000) %>%
    move(trajectory = list(c(25, 25), c(40, 30), c(40, 40), c(50, 50)), start = 470, end = 281, snapshots = 30)
  p3 <- population(name = "pop3", parent = p2, time = 281, N = 100, polygon = list(c(-10, 50), c(10, 50), c(20, 53), c(40, 60), c(40, 70), c(-10, 65)))
  p4 <- population(name = "pop4", parent = p2, time = 281, N = 100, polygon = list(c(-10, 35), c(20, 37), c(25, 40), c(30, 45), c(10, 50), c(-10, 45)))
  p5 <- population(name = "pop5", parent = p1, time = 181, N = 100, center = c(10, 25), radius = 300000) %>%
    move(trajectory = list(c(-5, 33), c(-5, 40)), start = 180, end = 81, snapshots = 20)

  geneflow <- list(
    gene_flow(from = p5, to = p4, rate = 0.2, start = 171, end = 131, overlap = F),
    gene_flow(from = p5, to = p3, rate = 0.3, start = 141, end = 1, overlap = F)
  )

  backward <- compile_model(
    path = file.path(tempdir(), "tmp-backward"),
    populations = list(p1, p2, p3, p4, p5),
    gene_flow = geneflow,
    generation_time = 1,
    resolution = 10000,
    overwrite = TRUE, force = TRUE,
    competition = 100e3, mating = 100e3, dispersal = 100e3
  )

  expect_true(all.equal(forward$splits[, grep("_orig", colnames(forward$splits), value = TRUE, invert = TRUE)],
                        backward$splits[, grep("_orig", colnames(backward$splits), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)],
                        backward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$maps[, c("pop", "pop_id", "time_gen")],
                        backward$maps[, c("pop", "pop_id","time_gen")]))

  components <- c("generation_time", "resolution", "world")
  expect_true(all(sapply(components, function(i) all.equal(forward[[i]], backward[[i]]))))

  locations_forward <- normalizePath(tempfile(fileext = ".gz"), winslash = "/", mustWork = FALSE)
  locations_backward <- normalizePath(tempfile(fileext = ".gz"), winslash = "/", mustWork = FALSE)

  # simulation runs are the same
  slim(forward, sequence_length = 1, recombination_rate = 0, locations = locations_forward, method = "batch", random_seed = 123, verbose = FALSE)
  slim(backward, sequence_length = 1, recombination_rate = 0, locations = locations_backward, method = "batch", random_seed = 123, verbose = FALSE)

  # make sure the scripts are the same
  f_script <- file.path(forward$path, "script.slim") %>% readLines
  b_script <- file.path(backward$path, "script.slim") %>%  readLines
  expect_equal(f_script, b_script)

  # make sure that the simulated location data is the same
  f_loc <- suppressMessages(readr::read_tsv(locations_forward))
  b_loc <- suppressMessages(readr::read_tsv(locations_backward))

  expect_equal(f_loc, b_loc)
})

test_that("forward and backward models yield the same simulation result (nonspatial)", {
  # forward simulation ------------------------------------------------------
  p1 <- population(name = "pop1", N = 100, time = 1)
  p2 <- population(name = "pop2", parent = p1, time = 10, N = 100)
  p3 <- population(name = "pop3", parent = p2, time = 200, N = 100)
  p4 <- population(name = "pop4", parent = p2, time = 200, N = 100)
  p5 <- population(name = "pop5", parent = p1, time = 300, N = 100)

  geneflow <- list(
    gene_flow(from = p5, to = p4, rate = 0.2, start = 310, end = 350, overlap = F),
    gene_flow(from = p5, to = p3, rate = 0.3, start = 340, end = 480, overlap = F)
  )

  forward <- compile_model(
    path = file.path(tempdir(), "tmp-forward"),
    populations = list(p1, p2, p3, p4, p5),
    gene_flow = geneflow,
    generation_time = 1,
    overwrite = TRUE, force = TRUE,
    simulation_length = 480
  )

  # backward simulation -----------------------------------------------------
  p1 <- population(name = "pop1", N = 100, time = 480)
  p2 <- population(name = "pop2", parent = p1, time = 471, N = 100)
  p3 <- population(name = "pop3", parent = p2, time = 281, N = 100)
  p4 <- population(name = "pop4", parent = p2, time = 281, N = 100)
  p5 <- population(name = "pop5", parent = p1, time = 181, N = 100)

  geneflow <- list(
    gene_flow(from = p5, to = p4, rate = 0.2, start = 171, end = 131, overlap = F),
    gene_flow(from = p5, to = p3, rate = 0.3, start = 141, end = 1, overlap = F)
  )

  backward <- compile_model(
    path = file.path(tempdir(), "tmp-backward"),
    populations = list(p1, p2, p3, p4, p5),
    gene_flow = geneflow,
    generation_time = 1,
    overwrite = TRUE, force = TRUE,
  )

  expect_true(all.equal(forward$splits[, grep("_orig", colnames(forward$splits), value = TRUE, invert = TRUE)],
                        backward$splits[, grep("_orig", colnames(backward$splits), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)],
                        backward$geneflow[, grep("_orig", colnames(forward$geneflow), value = TRUE, invert = TRUE)]))
  expect_true(all.equal(forward$maps[, c("pop", "pop_id", "time_gen")],
                        backward$maps[, c("pop", "pop_id","time_gen")]))

  components <- c("generation_time", "resolution", "world")
  expect_true(all(sapply(components, function(i) all.equal(forward[[i]], backward[[i]]))))

  # simulation runs are the same
  slim(forward, sequence_length = 1, recombination_rate = 0, method = "batch", random_seed = 123, verbose = FALSE)
  slim(backward, sequence_length = 1, recombination_rate = 0, method = "batch", random_seed = 123, verbose = FALSE)

  # make sure the scripts are the same
  f_script <- file.path(forward$path, "script.slim") %>% readLines
  b_script <- file.path(backward$path, "script.slim") %>% readLines
  expect_equal(f_script, b_script)

  # TODO after some output generation is implemented, test equivalence here
})

test_that("move preceding population split results in an error", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  p1 <- population(name = "p1", time = 1, N = 1, center = c(1, 1), radius = 10, map = map)
  p2 <- population(name = "p2", parent = p1, time = 10, N = 1, center = c(20, 50), radius = 20)
  expect_error(move(p2, trajectory = c(100, 100), start = 3, end = 20, snapshots = 5),
               "The new event (.*) pre-dates the last specified active event")
})

test_that("overlaps in time result in an error (forward time)", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  p1 <- population(name = "p1", time = 1, N = 1, center = c(1, 1), radius = 10, map = map)
  p2 <- population(name = "p2", parent = p1, time = 10, N = 1, center = c(20, 50), radius = 20) %>%
    move(trajectory = c(100, 100), start = 11, end = 20, snapshots = 5)
  msg <- "The new event (.*) pre-dates the last specified active event"
  # repeating the same move
  expect_error(move(p2, trajectory = c(100, 100), start = 10, end = 20, snapshots = 5), msg)
  # overlapping time window of the second move
  expect_error(move(p2, trajectory = c(100, 100), start = 5, end = 30, snapshots = 5), msg)
  # overlapping time window of the following range expansion
  expect_error(expand_range(p2, by = 20, start = 5, end = 30, snapshots = 5), msg)
  # overlapping time window of the following range change
  expect_error(set_range(p2, time = 8, center = c(50, 10), radius = 8), msg)
})

test_that("overlaps in time result in an error (backward time)", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  p1 <- population(name = "p1", time = 100, N = 1, center = c(1, 1), radius = 10, map = map)
  p2 <- population(name = "p2", parent = p1, time = 40, N = 1, center = c(20, 50), radius = 20) %>%
    move(trajectory = c(100, 100), start = 30, end = 10, snapshots = 5)
  msg <- "The new event (.*) pre-dates the last specified active event"
  # repeating the same move
  expect_error(move(p2, trajectory = c(100, 100), start = 30, end = 10, snapshots = 5), msg)
  # overlapping time window of the second move
  expect_error(move(p2, trajectory = c(100, 100), start = 25, end = 5, snapshots = 5), msg)
  # overlapping time window of the following range expansion
  expect_error(expand_range(p2, by = 20, start = 25, end = 5, snapshots = 5), msg)
  # overlapping time window of the following range change
  expect_error(set_range(p2, time = 15, center = c(50, 10), radius = 8), msg)
})

test_that("resizing of populations is consistent with established population dynamics (forward time)", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  p1 <- population(name = "pop1", map = map, time = 1, N = 500, center = c(10, 25), radius = 300000)
  p2 <- population(name = "pop2", parent = p1, time = 10, N = 500, center = c(10, 25), radius = 300000)
  msg <- "The new event (.*) pre-dates the last specified active event"
  expect_error(resize(p2, N = 10, how = "step", time = 5), msg)
})

test_that("resizing of populations is consistent with established population dynamics (backward time)", {
  map <- world(xrange = c(0, 100), yrange = c(0, 100), landscape = "blank")
  p1 <- population(name = "pop1", map = map, time = 30000, N = 500, center = c(10, 25), radius = 300000)
  p2 <- population(name = "pop2", parent = p1, time = 25000, N = 500, center = c(10, 25), radius = 300000)
  msg <- "The new event (.*) pre-dates the last specified active event"
  expect_error(resize(p2, N = 10, how = "step", time = 28000), msg)
})


# New tests for time consistency added after re-factoring of event time checks

map <- readRDS("map.rds")

p1 <- population(name = "p1", map = map, time = 100, N = 1, center = c(20, 50), radius = 20)

# ancestral populations

test_that("Overlapping time-windows for ancestral populations are caught", {
  expect_error(expand_range(p1, by = 20, start = 120, end = 20, snapshots = 5),
               "The new event (.*) falls both before and after")
})

test_that("Older forward event can't follow ancestral population with a higher 'split time'", {
  msg <- "The new event (.*) implies a forward time direction but"
  expect_error(move(p1, trajectory = c(10, 10), start = 80, end = 90, snapshots = 5), msg)
  expect_error(expand_range(p1, by = 1000, start = 80, end = 90, snapshots = 5), msg)
})

test_that("Older backward event can't follow ancestral population with a lower 'split time'", {
  msg <- "The new event (.*) implies a backward time direction but"
  expect_error(move(p1, trajectory = c(10, 10), start = 120, end = 110, snapshots = 5), msg)
  expect_error(expand_range(p1, by = 1000, start = 120, end = 110, snapshots = 5), msg)
})


# daughter populations

# backward model

test_that("Populations can only be created after their parents (backward model)", {
  expect_error(population(name = "p2", parent = p1, time = 100, N = 1, center = c(20, 50), radius = 20),
               "Population can be only created after its parent")
  expect_silent(population(name = "p2", parent = p1, time = 90, N = 1, center = c(20, 50), radius = 20))
})

p2 <- population(name = "p2", parent = p1, time = 98, N = 1, center = c(20, 50), radius = 20)

test_that("Forward time events not allowed for backward models", {
  msg <- "The new forward event (.*) is inconsistent with the backward time"
  expect_error(move(p2, trajectory = c(10, 10), start = 50, end = 80, snapshots = 5), msg)
  expect_error(expand_range(p2, by = 1000, start = 50, end = 80, snapshots = 5), msg)
})

test_that("Backward time events can't predate previous active event for backward models", {
  msg <- "The new event (.*) pre-dates"
  expect_error(move(p2, trajectory = c(10, 10), start = 150, end = 140, snapshots = 5), msg)
  expect_error(expand_range(p2, by = 1000, start = 150, end = 140, snapshots = 5), msg)
})

# forward model

test_that("Populations can only be created after their parents (forward model)", {
  expect_error(population(name = "p2", parent = p1, time = 100, N = 1, center = c(20, 50), radius = 20),
               "Population can be only created after its parent")
  expect_silent(population(name = "p2", parent = p1, time = 90, N = 1, center = c(20, 50), radius = 20))
})

p2 <- population(name = "p2", parent = p1, time = 120, N = 1, center = c(20, 50), radius = 20)

test_that("Backward time events not allowed for forward models", {
  msg <- "The new backward event (.*) is inconsistent with the forward time"
  expect_error(move(p2, trajectory = c(10, 10), start = 80, end = 50, snapshots = 5), msg)
  expect_error(expand_range(p2, by = 1000, start = 80, end = 50, snapshots = 5), msg)
})

test_that("Forward time events can't predate previous active event for forward models", {
  msg <- "The new event (.*) pre-dates"
  expect_error(move(p2, trajectory = c(10, 10), start = 40, end = 50, snapshots = 5), msg)
  expect_error(expand_range(p2, by = 1000, start = 40, end = 50, snapshots = 5), msg)
})

# Test consistency with scheduled removal times

test_that("Events can't be scheduled after population removal", {
  msg <- "The specified event time (.*) is not consistent with the scheduled removal .*"

  forward <- population(name = "forward", map = map, time = 1, N = 1, center = c(20, 50), radius = 20, remove = 50)
  expect_error(move(forward, trajectory = c(5, 5), start = 5, end = 80, snapshots = 3), paste0(msg, "given the assumed forward time"))
  expect_error(move(forward, trajectory = c(5, 5), start = 60, end = 80, snapshots = 3), paste0(msg, "given the assumed forward time"))
  expect_error(expand_range(forward, by = 100, start = 5, end = 80, snapshots = 3), paste0(msg, "given the assumed forward time"))
  expect_error(expand_range(forward, by = 100, start = 60, end = 80, snapshots = 3), paste0(msg, "given the assumed forward time"))
  expect_silent(move(forward, trajectory = c(5, 5), start = 5, end = 40, snapshots = 3))

  backward <- population(name = "backward", map = map, time = 100, N = 1, center = c(20, 50), radius = 20, remove = 10)
  expect_error(move(backward, trajectory = c(5, 5), start = 80, end = 5, snapshots = 3), paste0(msg, "given the assumed backward time"))
  expect_error(move(backward, trajectory = c(5, 5), start = 8, end = 5, snapshots = 3),paste0(msg, "given the assumed backward time"))
  expect_error(expand_range(backward, by = 10, start = 80, end = 5, snapshots = 3), paste0(msg, "given the assumed backward time"))
  expect_error(expand_range(backward, by = 10, start = 8, end = 5, snapshots = 3),paste0(msg, "given the assumed backward time"))
  expect_silent(move(backward, trajectory = c(5, 5), start = 80, end = 50, snapshots = 3))
})

test_that("Explicitly given direction must agree with the implied direction", {
  map <- readRDS("map.rds")

  msg <- "The direction that was explicitly specified contradicts the direction implied by the model"

  pop <- population("pop", time = 500, N = 100, map = map, center = c(20, 50), radius = 500e3) %>%
    resize(N = 1000, time = 900, how = "step")

  model_dir <- file.path(tempdir(), "direction_conflict")
  expect_error(compile_model(populations = list(pop), generation_time = 1,
                       resolution = 10e3, competition = 130e3, mating = 100e3, dispersal = 70e3, # how far will offspring end up from their parents
                       path = model_dir, direction = "backward", overwrite = TRUE, force = TRUE), msg)

  pop <- population("pop", time = 500, N = 100, map = map, center = c(20, 50), radius = 500e3) %>%
    resize(N = 1000, time = 300, how = "step")

  model_dir <- file.path(tempdir(), "direction_conflict")
  expect_error(compile_model(populations = list(pop), generation_time = 1,
                       resolution = 10e3, competition = 130e3, mating = 100e3, dispersal = 70e3, # how far will offspring end up from their parents
                       path = model_dir, direction = "forward", overwrite = TRUE, force = TRUE), msg)

})
bodkan/slendr documentation built on Dec. 19, 2024, 11:41 p.m.