tests/testthat/test-breedtools_poly.R

#context("BreedTools - solve_composition_poly")

#  Shared fixtures 
ref_file      <- system.file("test_ref.txt",  package = "BIGpopA")
val_file      <- system.file("test_test.txt", package = "BIGpopA")
ref_ids_file  <- system.file("ref_ids.txt",   package = "BIGpopA")

reference     <- read.table(ref_file,     header = TRUE, row.names = 1, sep = "\t")
validation    <- read.table(val_file,     header = TRUE, row.names = 1, sep = "\t")
reference_ids <- read.table(ref_ids_file, header = TRUE, sep = "\t")

ref_ids <- lapply(as.list(reference_ids), as.character)
freq    <- allele_freq_poly(reference, ref_ids, ploidy = 4)

#  1. Baseline (tetraploid, no ped/groups) 
test_that("baseline tetraploid composition returns expected values", {
  prediction <- as.data.frame(solve_composition_poly(validation, freq, ploidy = 4))
  
  expect_equal(round(mean(as.numeric(freq)),          6), 0.888889, tolerance = 0.01)
  expect_equal(round(mean(as.numeric(prediction$R2)), 6), 0.841454, tolerance = 0.01)
  expect_true(nrow(prediction) == 175)
})

#  2. Default ploidy (diploid) 
test_that("default ploidy = 2 runs without error and returns a data.frame", {
  freq_dip   <- allele_freq_poly(reference, ref_ids, ploidy = 2)
  prediction <- solve_composition_poly(validation, freq_dip)  # ploidy defaults to 2
  
  expect_s3_class(as.data.frame(prediction), "data.frame")
  expect_true(nrow(prediction) > 0)
})

#  3. Ploidy parameter changes output 
test_that("ploidy = 4 scales dosage differently than ploidy = 2", {
  # Use the same freq matrix for both — only ploidy differs
  # Source shows Y is divided by ploidy: Y <- Y / ploidy
  # So with identical input, a higher ploidy should yield different compositions
  pred_2 <- as.data.frame(solve_composition_poly(validation, freq, ploidy = 2))
  pred_4 <- as.data.frame(solve_composition_poly(validation, freq, ploidy = 4))
  
  breed_cols <- intersect(colnames(freq), colnames(pred_2))
  skip_if(length(breed_cols) < 1, "Could not identify breed proportion columns.")
  
  expect_false(isTRUE(all.equal(
    pred_2[, breed_cols, drop = FALSE],
    pred_4[, breed_cols, drop = FALSE]
  )))
})

#  4. Small inline example (tetraploid) 
test_that("inline tetraploid example from documentation runs correctly", {
  allele_freqs_matrix <- matrix(
    c(0.625, 0.500,
      0.500, 0.500,
      0.500, 0.500,
      0.750, 0.500,
      0.625, 0.625),
    nrow = 5, ncol = 2, byrow = TRUE,
    dimnames = list(paste0("SNP", 1:5), c("VarA", "VarB"))
  )
  
  val_geno_matrix <- matrix(
    c(2, 1, 2, 3, 4,
      3, 4, 2, 3, 0),
    nrow = 2, ncol = 5, byrow = TRUE,
    dimnames = list(paste0("Test", 1:2), paste0("SNP", 1:5))
  )
  
  composition <- solve_composition_poly(Y = val_geno_matrix,
                                        X = allele_freqs_matrix,
                                        ploidy = 4)
  
  expect_s3_class(as.data.frame(composition), "data.frame")
  expect_equal(nrow(composition), 2)
})

#  5. Compositions sum to 1 (full dataset) 
test_that("breed composition proportions sum to 1 for each animal", {
  prediction <- as.data.frame(solve_composition_poly(validation, freq, ploidy = 4))
  breed_cols <- colnames(freq)  # reference population names come directly from X
  
  available <- intersect(breed_cols, colnames(prediction))
  skip_if(length(available) < 2, "Could not identify breed proportion columns.")
  
  row_sums <- rowSums(prediction[, available, drop = FALSE])
  expect_true(all(abs(row_sums - 1) < 1e-6))
})

#  6. groups argument returns a named list 
test_that("groups argument returns a named list of data.frames", {
  skip_if_not(existsFunction("QPseparate", where = asNamespace("BIGpopA")),
              "QPseparate not available — skipping groups test.")
  
  all_ids <- rownames(validation)
  half    <- floor(length(all_ids) / 2)
  groups  <- list(GroupA = all_ids[1:half],
                  GroupB = all_ids[(half + 1):length(all_ids)])
  
  result <- solve_composition_poly(validation, freq, groups = groups, ploidy = 4)
  
  expect_type(result, "list")
  expect_named(result, c("GroupA", "GroupB"))
})

#  7. ped argument: basic composition with pedigree 
test_that("ped argument runs and returns a data.frame", {
  ped_file <- system.file("test_ped.txt", package = "BIGpopA")
  skip_if_not(file.exists(ped_file), "Pedigree test file not available.")
  
  ped    <- read.table(ped_file, header = TRUE, sep = "\t")
  result <- solve_composition_poly(validation, freq, ped = ped, ploidy = 4)
  
  expect_s3_class(as.data.frame(result), "data.frame")
  expect_true(nrow(result) > 0)
})

#  8. mia flag returns MIA data.frame 
test_that("mia = TRUE returns maternally inherited allele data.frame", {
  ped_file <- system.file("test_ped.txt", package = "BIGpopA")
  skip_if_not(file.exists(ped_file), "Pedigree test file not available.")
  
  ped    <- read.table(ped_file, header = TRUE, sep = "\t")
  result <- solve_composition_poly(validation, freq, ped = ped,
                                   mia = TRUE, ploidy = 4)
  
  expect_s3_class(as.data.frame(result), "data.frame")
  expect_true(ncol(result) > 0)
})

#  9. sire flag returns sire genotype data.frame 
test_that("sire = TRUE returns sire genotype data.frame", {
  ped_file <- system.file("test_ped.txt", package = "BIGpopA")
  skip_if_not(file.exists(ped_file), "Pedigree test file not available.")
  
  ped    <- read.table(ped_file, header = TRUE, sep = "\t")
  result <- solve_composition_poly(validation, freq, ped = ped,
                                   sire = TRUE, ploidy = 4)
  
  expect_s3_class(as.data.frame(result), "data.frame")
})

#  10. dam flag returns dam genotype data.frame 
test_that("dam = TRUE returns dam genotype data.frame", {
  ped_file <- system.file("test_ped.txt", package = "BIGpopA")
  skip_if_not(file.exists(ped_file), "Pedigree test file not available.")
  
  ped    <- read.table(ped_file, header = TRUE, sep = "\t")
  result <- solve_composition_poly(validation, freq, ped = ped,
                                   dam = TRUE, ploidy = 4)
  
  expect_s3_class(as.data.frame(result), "data.frame")
})

#  11. Extra SNPs in Y not in X are silently dropped 
test_that("extra SNPs in Y not present in X are ignored without error", {
  extra_snps <- matrix(
    rbinom(nrow(validation) * 3, 4, 0.5),
    nrow = nrow(validation),
    dimnames = list(rownames(validation), c("FAKE1", "FAKE2", "FAKE3"))
  )
  validation_extra <- cbind(validation, extra_snps)
  
  expect_no_error(
    solve_composition_poly(validation_extra, freq, ploidy = 4)
  )
})

#  12. Output row count matches number of animals in Y 
test_that("number of rows in output matches number of animals in Y", {
  prediction <- as.data.frame(solve_composition_poly(validation, freq, ploidy = 4))
  expect_equal(nrow(prediction), nrow(validation))
})

Try the BIGpopA package in your browser

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

BIGpopA documentation built on July 17, 2026, 1:07 a.m.