aegis_sim.submodels.reproduction.reproduction

 1from aegis_sim.dataclasses.genomes import Genomes
 2from aegis_sim.submodels.reproduction.pairing import pairing
 3from aegis_sim.submodels.reproduction.recombination import recombination, recombination_via_pairs
 4
 5
 6class Reproducer:
 7    """
 8    GUI
 9    Individuals are fertile starting with [[MATURATION_AGE]] (can be 0) until [[REPRODUCTION_ENDPOINT]] (if 0, no REPRODUCTION_ENDPOINT occurs).
10    Reproduction can be sexual (with diploid genomes) or asexual (with diploid or haploid genomes).
11    When reproduction is sexual, recombination occurs in gametes at a rate of [[RECOMBINATION_RATE]]
12    and gametes will inherit mutations at an age-independent rate
13    which can be parameterized (genetics-independent) or set to evolve (genetics-dependent).
14    Mutations cause the offspring genome bit states to flip from 0-to-1 or 1-to-0.
15    The ratio of 0-to-1 and 1-to-0 can be modified using the [[MUTATION_RATIO]].
16
17    If the population is oviparous, [[INCUBATION_PERIOD]] should be set to -1, 1 or greater.
18    When it is set to -1, all laid eggs hatch only once all living individuals die.
19    When it is set to 0 or greater, eggs hatch after that specified time.
20    Thus, when 0, individuals do not go through an egg stage during their life cycle.
21    """
22
23    # TODO INCUBATION_PERIOD set to -1 or 1 or greater is stupid
24    # TODO INCUBATION_PERIOD is not really a part of this submodel, but it is in the documentation. it should be though.
25
26    # TODO probably better to split mutation logic into another domain and cluster together with genetic architecture stuff
27
28    def __init__(self, RECOMBINATION_RATE, REPRODUCTION_MODE, mutator):
29        self.RECOMBINATION_RATE = RECOMBINATION_RATE
30        self.REPRODUCTION_MODE = REPRODUCTION_MODE
31        self.mutator = mutator
32
33    def generate_offspring_genomes(self, genomes, muta_prob, ages, parental_sexes,
34                                   ancestry=None, parent_positions=None,
35                                   max_search_radius=0):
36        """Generate offspring genomes from parental input.
37
38        Asexual: each parent yields one offspring (same chromatids as parent).
39        Sexual: pairing pairs males and females. When `parent_positions` is given
40        (LATTICE_MODE), pairing is lattice-aware — see matingmanager. When None,
41        classical well-mixed pairing.
42
43        Returns `(genomes, ancestry, mother_slots)`. `mother_slots` is the slot
44        indices of the paired mothers (into the parental_genomes array). For
45        sexual, callers use this to look up mother positions for offspring
46        placement on the lattice. For asexual, mother_slots is None — the
47        caller already knows the parent->offspring mapping via `who`.
48        """
49        mother_slots = None
50        if self.REPRODUCTION_MODE == "sexual":
51            if ancestry is not None:
52                genomes, ancestry = recombination_via_pairs(genomes, self.RECOMBINATION_RATE, ancestry=ancestry)
53                genomes, ages, muta_prob, ancestry, mother_slots = pairing(
54                    Genomes(genomes), parental_sexes, ages, muta_prob,
55                    ancestry=ancestry,
56                    parent_positions=parent_positions,
57                    max_search_radius=max_search_radius,
58                )
59            else:
60                genomes = recombination_via_pairs(genomes, self.RECOMBINATION_RATE)
61                genomes, ages, muta_prob, mother_slots = pairing(
62                    Genomes(genomes), parental_sexes, ages, muta_prob,
63                    parent_positions=parent_positions,
64                    max_search_radius=max_search_radius,
65                )
66
67        # Mutation flips genome bits — ancestry labels are not mutated
68        genomes = self.mutator._mutate(genomes, muta_prob, ages)
69        genomes = Genomes(genomes)
70        return genomes, ancestry, mother_slots
class Reproducer:
 7class Reproducer:
 8    """
 9    GUI
10    Individuals are fertile starting with [[MATURATION_AGE]] (can be 0) until [[REPRODUCTION_ENDPOINT]] (if 0, no REPRODUCTION_ENDPOINT occurs).
11    Reproduction can be sexual (with diploid genomes) or asexual (with diploid or haploid genomes).
12    When reproduction is sexual, recombination occurs in gametes at a rate of [[RECOMBINATION_RATE]]
13    and gametes will inherit mutations at an age-independent rate
14    which can be parameterized (genetics-independent) or set to evolve (genetics-dependent).
15    Mutations cause the offspring genome bit states to flip from 0-to-1 or 1-to-0.
16    The ratio of 0-to-1 and 1-to-0 can be modified using the [[MUTATION_RATIO]].
17
18    If the population is oviparous, [[INCUBATION_PERIOD]] should be set to -1, 1 or greater.
19    When it is set to -1, all laid eggs hatch only once all living individuals die.
20    When it is set to 0 or greater, eggs hatch after that specified time.
21    Thus, when 0, individuals do not go through an egg stage during their life cycle.
22    """
23
24    # TODO INCUBATION_PERIOD set to -1 or 1 or greater is stupid
25    # TODO INCUBATION_PERIOD is not really a part of this submodel, but it is in the documentation. it should be though.
26
27    # TODO probably better to split mutation logic into another domain and cluster together with genetic architecture stuff
28
29    def __init__(self, RECOMBINATION_RATE, REPRODUCTION_MODE, mutator):
30        self.RECOMBINATION_RATE = RECOMBINATION_RATE
31        self.REPRODUCTION_MODE = REPRODUCTION_MODE
32        self.mutator = mutator
33
34    def generate_offspring_genomes(self, genomes, muta_prob, ages, parental_sexes,
35                                   ancestry=None, parent_positions=None,
36                                   max_search_radius=0):
37        """Generate offspring genomes from parental input.
38
39        Asexual: each parent yields one offspring (same chromatids as parent).
40        Sexual: pairing pairs males and females. When `parent_positions` is given
41        (LATTICE_MODE), pairing is lattice-aware — see matingmanager. When None,
42        classical well-mixed pairing.
43
44        Returns `(genomes, ancestry, mother_slots)`. `mother_slots` is the slot
45        indices of the paired mothers (into the parental_genomes array). For
46        sexual, callers use this to look up mother positions for offspring
47        placement on the lattice. For asexual, mother_slots is None — the
48        caller already knows the parent->offspring mapping via `who`.
49        """
50        mother_slots = None
51        if self.REPRODUCTION_MODE == "sexual":
52            if ancestry is not None:
53                genomes, ancestry = recombination_via_pairs(genomes, self.RECOMBINATION_RATE, ancestry=ancestry)
54                genomes, ages, muta_prob, ancestry, mother_slots = pairing(
55                    Genomes(genomes), parental_sexes, ages, muta_prob,
56                    ancestry=ancestry,
57                    parent_positions=parent_positions,
58                    max_search_radius=max_search_radius,
59                )
60            else:
61                genomes = recombination_via_pairs(genomes, self.RECOMBINATION_RATE)
62                genomes, ages, muta_prob, mother_slots = pairing(
63                    Genomes(genomes), parental_sexes, ages, muta_prob,
64                    parent_positions=parent_positions,
65                    max_search_radius=max_search_radius,
66                )
67
68        # Mutation flips genome bits — ancestry labels are not mutated
69        genomes = self.mutator._mutate(genomes, muta_prob, ages)
70        genomes = Genomes(genomes)
71        return genomes, ancestry, mother_slots

GUI Individuals are fertile starting with [[MATURATION_AGE]] (can be 0) until [[REPRODUCTION_ENDPOINT]] (if 0, no REPRODUCTION_ENDPOINT occurs). Reproduction can be sexual (with diploid genomes) or asexual (with diploid or haploid genomes). When reproduction is sexual, recombination occurs in gametes at a rate of [[RECOMBINATION_RATE]] and gametes will inherit mutations at an age-independent rate which can be parameterized (genetics-independent) or set to evolve (genetics-dependent). Mutations cause the offspring genome bit states to flip from 0-to-1 or 1-to-0. The ratio of 0-to-1 and 1-to-0 can be modified using the [[MUTATION_RATIO]].

If the population is oviparous, [[INCUBATION_PERIOD]] should be set to -1, 1 or greater. When it is set to -1, all laid eggs hatch only once all living individuals die. When it is set to 0 or greater, eggs hatch after that specified time. Thus, when 0, individuals do not go through an egg stage during their life cycle.

Reproducer(RECOMBINATION_RATE, REPRODUCTION_MODE, mutator)
29    def __init__(self, RECOMBINATION_RATE, REPRODUCTION_MODE, mutator):
30        self.RECOMBINATION_RATE = RECOMBINATION_RATE
31        self.REPRODUCTION_MODE = REPRODUCTION_MODE
32        self.mutator = mutator
RECOMBINATION_RATE
REPRODUCTION_MODE
mutator
def generate_offspring_genomes( self, genomes, muta_prob, ages, parental_sexes, ancestry=None, parent_positions=None, max_search_radius=0):
34    def generate_offspring_genomes(self, genomes, muta_prob, ages, parental_sexes,
35                                   ancestry=None, parent_positions=None,
36                                   max_search_radius=0):
37        """Generate offspring genomes from parental input.
38
39        Asexual: each parent yields one offspring (same chromatids as parent).
40        Sexual: pairing pairs males and females. When `parent_positions` is given
41        (LATTICE_MODE), pairing is lattice-aware — see matingmanager. When None,
42        classical well-mixed pairing.
43
44        Returns `(genomes, ancestry, mother_slots)`. `mother_slots` is the slot
45        indices of the paired mothers (into the parental_genomes array). For
46        sexual, callers use this to look up mother positions for offspring
47        placement on the lattice. For asexual, mother_slots is None — the
48        caller already knows the parent->offspring mapping via `who`.
49        """
50        mother_slots = None
51        if self.REPRODUCTION_MODE == "sexual":
52            if ancestry is not None:
53                genomes, ancestry = recombination_via_pairs(genomes, self.RECOMBINATION_RATE, ancestry=ancestry)
54                genomes, ages, muta_prob, ancestry, mother_slots = pairing(
55                    Genomes(genomes), parental_sexes, ages, muta_prob,
56                    ancestry=ancestry,
57                    parent_positions=parent_positions,
58                    max_search_radius=max_search_radius,
59                )
60            else:
61                genomes = recombination_via_pairs(genomes, self.RECOMBINATION_RATE)
62                genomes, ages, muta_prob, mother_slots = pairing(
63                    Genomes(genomes), parental_sexes, ages, muta_prob,
64                    parent_positions=parent_positions,
65                    max_search_radius=max_search_radius,
66                )
67
68        # Mutation flips genome bits — ancestry labels are not mutated
69        genomes = self.mutator._mutate(genomes, muta_prob, ages)
70        genomes = Genomes(genomes)
71        return genomes, ancestry, mother_slots

Generate offspring genomes from parental input.

Asexual: each parent yields one offspring (same chromatids as parent). Sexual: pairing pairs males and females. When parent_positions is given (LATTICE_MODE), pairing is lattice-aware — see matingmanager. When None, classical well-mixed pairing.

Returns (genomes, ancestry, mother_slots). mother_slots is the slot indices of the paired mothers (into the parental_genomes array). For sexual, callers use this to look up mother positions for offspring placement on the lattice. For asexual, mother_slots is None — the caller already knows the parent->offspring mapping via who.