aegis_sim.submodels.reproduction.pairing

 1import numpy as np
 2from numba import njit, prange
 3from aegis_sim import variables
 4from aegis_sim.dataclasses.genomes import Genomes
 5from aegis_sim import submodels
 6
 7
 8@njit(parallel=True)
 9def _assemble_children(genome_array, males, females, male_gamete_idx, female_gamete_idx):
10    """Assemble children genomes directly from parent genome array.
11
12    Reads each parent's selected chromatid and writes it into the children array
13    in one pass, parallelized over pairs. No intermediate arrays allocated.
14    """
15    n_pairs = len(males)
16    # genome_array shape: (n_individuals, ploidy, loci, bpl)
17    n_loci = genome_array.shape[2]
18    n_bpl = genome_array.shape[3]
19    children = np.empty((n_pairs, 2, n_loci, n_bpl), dtype=genome_array.dtype)
20
21    for p in prange(n_pairs):
22        m = males[p]
23        f = females[p]
24        mg = male_gamete_idx[p]
25        fg = female_gamete_idx[p]
26        for i in range(n_loci):
27            for j in range(n_bpl):
28                children[p, 0, i, j] = genome_array[m, mg, i, j]
29                children[p, 1, i, j] = genome_array[f, fg, i, j]
30
31    return children
32
33
34def pairing(genomes: Genomes, parental_sexes, ages, muta_prob, ancestry=None):
35    """Return assorted chromatids."""
36
37    # Get pairs
38    males, females = submodels.matingmanager.pair_up_polygamously(parental_sexes)
39    assert len(males) == len(females)
40    n_pairs = len(males)
41
42    if n_pairs == 0:
43        gshape = genomes.shape()
44        children = np.empty(shape=(0, *gshape[1:]), dtype=np.bool_)
45        if ancestry is not None:
46            empty_ancestry = np.empty(shape=(0, *ancestry.shape[1:]), dtype=ancestry.dtype)
47            return children, ages[females], muta_prob[females], empty_ancestry
48        return children, ages[females], muta_prob[females]
49
50    # Random gamete selection (chromatid 0 or 1 per parent)
51    male_gamete_idx = (variables.rng.random(n_pairs) < 0.5).astype(np.int32)
52    female_gamete_idx = (variables.rng.random(n_pairs) < 0.5).astype(np.int32)
53
54    # Assemble children directly from genome array — no intermediate copies
55    children = _assemble_children(
56        genomes.array, males, females, male_gamete_idx, female_gamete_idx,
57    )
58
59    if ancestry is not None:
60        # Assemble offspring ancestry using same parent/gamete selections
61        offspring_ancestry = np.empty((n_pairs, *ancestry.shape[1:]), dtype=ancestry.dtype)
62        offspring_ancestry[:, 0] = ancestry[males, male_gamete_idx]
63        offspring_ancestry[:, 1] = ancestry[females, female_gamete_idx]
64        # TODO fix splitting of ages and muta_prob
65        return children, ages[females], muta_prob[females], offspring_ancestry
66
67    # TODO fix splitting of ages and muta_prob
68    return children, ages[females], muta_prob[females]
def pairing( genomes: aegis_sim.dataclasses.genomes.Genomes, parental_sexes, ages, muta_prob, ancestry=None):
35def pairing(genomes: Genomes, parental_sexes, ages, muta_prob, ancestry=None):
36    """Return assorted chromatids."""
37
38    # Get pairs
39    males, females = submodels.matingmanager.pair_up_polygamously(parental_sexes)
40    assert len(males) == len(females)
41    n_pairs = len(males)
42
43    if n_pairs == 0:
44        gshape = genomes.shape()
45        children = np.empty(shape=(0, *gshape[1:]), dtype=np.bool_)
46        if ancestry is not None:
47            empty_ancestry = np.empty(shape=(0, *ancestry.shape[1:]), dtype=ancestry.dtype)
48            return children, ages[females], muta_prob[females], empty_ancestry
49        return children, ages[females], muta_prob[females]
50
51    # Random gamete selection (chromatid 0 or 1 per parent)
52    male_gamete_idx = (variables.rng.random(n_pairs) < 0.5).astype(np.int32)
53    female_gamete_idx = (variables.rng.random(n_pairs) < 0.5).astype(np.int32)
54
55    # Assemble children directly from genome array — no intermediate copies
56    children = _assemble_children(
57        genomes.array, males, females, male_gamete_idx, female_gamete_idx,
58    )
59
60    if ancestry is not None:
61        # Assemble offspring ancestry using same parent/gamete selections
62        offspring_ancestry = np.empty((n_pairs, *ancestry.shape[1:]), dtype=ancestry.dtype)
63        offspring_ancestry[:, 0] = ancestry[males, male_gamete_idx]
64        offspring_ancestry[:, 1] = ancestry[females, female_gamete_idx]
65        # TODO fix splitting of ages and muta_prob
66        return children, ages[females], muta_prob[females], offspring_ancestry
67
68    # TODO fix splitting of ages and muta_prob
69    return children, ages[females], muta_prob[females]

Return assorted chromatids.