Produces offspring by simulating chromosomal crossovers from explicit parent
pairs. Each row of matings defines exactly one offspring, giving full
control over the mating design.
Usage
add_offspring(
pop,
matings,
seed = NULL,
store_crossovers = FALSE,
batch_size = NULL,
max_batch_mem = NULL
)Arguments
- pop
A
tidybreed_popobject- matings
A tibble or data.frame where each row = one offspring.
Required columns (canonical names match
ind_meta):id_parent_1— sire / parent 1 ID; must exist inind_meta. Alias:id_sireid_parent_2— dam / parent 2 ID; must exist inind_meta. Alias:id_damsex—"M"or"F"line_name— line name for offspring IDs (same format asadd_founders())
Optional extra columns (e.g.
gen = 2L,farm = "Iowa") are validated and written directly toind_meta. If a column does not yet exist inind_metait is added automatically. Scalar values in a tibble are recycled to all rows before being passed to this function.- seed
Optional integer base seed for the per-gamete recombination streams. Each gamete draws from its own deterministic
dqrngsub-stream keyed on its global offspring index, so output is independent ofbatch_sizeand (later) thread count.seed = NULL(default) draws one base seed from base-R's RNG, so an upstreamset.seed()still fully reproduces the call; pass an integer to fix the gamete streams independently of surrounding base-R state. The resolved base seed is reported in the message and attached to the returned object asattr(pop, "base_seed"); it is not written to any table.- store_crossovers
Logical (default
FALSE). WhenTRUE, every crossover drawn during meiosis is recorded as one row inind_crossover(id_ind,parent_origin,chr,chr_name,pos_cM). Absence of a row for a(id_ind, parent_origin, chr)means that gamete's chromosome did not recombine. Includes crossovers on recombining special chromosomes.FALSEcosts nothing (no buffer emitted, noind_crossoverwrite).- batch_size
Optional integer. Offspring generated + written per batch. Bounds peak memory at roughly
batch_size x n_locilong rows regardless of the total number of offspring (a single huge mating streams to disk batch by batch). Any value — includingbatch_size = nrow(matings)— produces byte-identical output for a fixed seed; it only trades peak memory against per-batch overhead. Overridesmax_batch_memwhen both are set.- max_batch_mem
Optional per-batch memory budget as bytes (e.g.
512e6) or a string (e.g."512MB","2GB"). Used to derivebatch_sizewhenbatch_sizeisNULL. When both areNULL(the default), the batch size is auto-picked from detected available system memory (falling back to a conservative fixed budget if detection is unavailable).
Value
The modified tidybreed_pop object (invisibly).
Assign the result back: pop <- add_offspring(pop, matings)
Details
Mating design flexibility:
Multiple offspring per pair: repeat the pair row in
matingsMultiple sires per dam (pooled semen / polyspermy): include each sire as a separate row with the same
id_parent_2Multiple dams per sire: include each dam as a separate row with the same
id_parent_1Cross-line matings: use any
line_namevalue in theline_namecolumn
Column aliases:
id_sire is accepted as an alias for id_parent_1, and id_dam for
id_parent_2. Both naming styles produce identical results.
Recombination model:
Gametes are simulated using the Haldane map function. Each gamete draws from
its own deterministic dqrng sub-stream keyed on (base seed, offspring index, parent role), so output is identical for any batch_size (and, in
later versions, any thread count) and is set by seed (see above). The
crossover count per chromosome ~ Poisson(chr_len_cM[i] / 100), i.e.
Poisson(genetic length in Morgans) since 1 Morgan = 100 cM, drawn by a
uniform-only inversion sampler. Genetic positions come from the resolved
genetic map (genome_map, per the gamete-producing parent's sex/line);
crossover positions are uniform in cM within each chromosome, and the starting
haplotype is chosen at random. This
applies to plain autosomes (the seeded default: chr_inheritance
from_parent_1 = from_parent_2 = 1 for both sexes and chr_recombination
recombines = TRUE for both parent sexes).
Chromosomes with rules: a chromosome counts as a plain autosome only if
it is plain for every line that has a rule in define_chromosome(). Any
other chromosome (sex chromosomes, organelles, or one that a single line's
rule changes) is drawn for every offspring on a slower per-offspring path
that applies the rules. The copies an offspring inherits follow the rule for
the offspring's sex and line. Whether a parent's contribution recombines
follows the rule for that parent's sex and line:
A parent with two copies whose rule says it recombines passes on a recombined copy (same Haldane model).
A parent with two copies whose rule says it does not recombine passes on one of its two copies unchanged, chosen at random (one draw).
A parent with one copy passes it on unchanged (no draw).
Because the path is decided from the stored rules, adding a line rule that makes a chromosome special moves it to the slower path for every line, and so changes later seeded offspring of every line, not only that line's.
Offspring IDs:
IDs follow the same "{line_name}_{n}" format as add_founders(), which
they share a counter with: n continues from the largest n the line
currently holds in ind_meta, and within one call each line's rows are
numbered in matings row order. The offspring's line is whatever
line_name its row gives; nothing is derived from the parents' lines, so a
cross is named by you ("AB", "F1", ...). See add_founders() for the
supported range and what happens after deletions.
Parent ids must be character (a factor is converted), with no NA or
empty string. They are looked up as data, never pasted into SQL, so an id
of any shape that is not in ind_meta is reported as not found.
Examples
if (FALSE) { # \dontrun{
pop <- open_pop(pop_name = "test", db_name = ":memory:") |>
define_genome(n_loci = 1000, n_chr = 5, chr_len_Mb = 100) |>
define_founder_haplotypes(n_haplotypes = 200, line_name = "A")
pop <- pop |>
get_table("founder_haplotypes") |>
add_founders(n_males = 5, n_females = 5, line_name = "A", gen = 1L)
# One offspring per mating, extra metadata column (gen)
matings <- tibble::tibble(
id_parent_1 = rep("A_1", 5),
id_parent_2 = paste0("A_", 6:10),
sex = c("M", "F", "M", "F", "M"),
line_name = "A",
gen = 2L
)
pop <- pop |> add_offspring(matings)
# Animal-breeder-style aliases
matings2 <- tibble::tibble(
id_sire = rep("A_1", 3),
id_dam = paste0("A_", 6:8),
sex = c("M", "F", "M"),
line_name = "A",
gen = 2L
)
pop <- pop |> add_offspring(matings2)
} # }
