lineage/population
Represents a collection of individuals organised across generations.
A Population tracks individuals by the generation they were born into,
starting at generation 0. The simulation advances the population one
generation at a time by selecting mating candidates, producing offspring,
and adding them to the next generation.
The behaviour of each step is controlled by a PopulationRules value,
which lets the caller customise mate selection, population alignment, and
how many offspring are produced per generation.
Types
A function that transforms a population before the next generation is produced — for example, to cull individuals, enforce carrying capacity, or apply selection pressure
pub type Align =
fn(Population) -> Population
The two pools of individuals from which mating partners are drawn.
For species without genetic sex both males and females may contain
the same individuals, as in select_all.
Fields
males- Candidate paternal parentsfemales- Candidate maternal parents
pub type MatingCandidates {
MatingCandidates(
males: List(individual.Individual),
females: List(individual.Individual),
)
}
Constructors
-
MatingCandidates( males: List(individual.Individual), females: List(individual.Individual), )
A function that determines how many offspring to produce in the next generation, based on the current state of the population
pub type OffspringCount =
fn(Population) -> Int
A collection of individuals grouped by the generation they belong to.
Generation numbers start at 0. Each generation maps to a list of individuals born in that step.
Fields
generations- A dict mapping generation number to the list of individuals born in that generation
pub type Population {
Population(
generations: dict.Dict(Int, List(individual.Individual)),
)
}
Constructors
-
Population( generations: dict.Dict(Int, List(individual.Individual)), )
The complete set of rules governing how a population reproduces and changes over time.
Fields
select- Chooses which individuals are eligible to matealign- Transforms the population before reproduction (e.g. culling)amount- Determines how many offspring to produce per generation
pub type PopulationRules {
PopulationRules(
select: fn(Population) -> MatingCandidates,
align: fn(Population) -> Population,
amount: fn(Population) -> Int,
)
}
Constructors
-
PopulationRules( select: fn(Population) -> MatingCandidates, align: fn(Population) -> Population, amount: fn(Population) -> Int, )
A function that inspects a population and returns the candidates eligible for mating in the next generation
pub type SelectMates =
fn(Population) -> MatingCandidates
Values
pub fn advance(
population: Population,
rules: PopulationRules,
) -> Population
Advances the population by one generation.
First applies rules.align to the current population, then determines the
next generation number as one greater than the current highest. Finally,
produces as many offspring as rules.amount specifies, each added to the
new generation.
Examples
let next = advance(population, rules)
dict.size(next.generations) == dict.size(population.generations) + 1
// -> True
pub fn from_list(
members: List(individual.Individual),
) -> Population
Creates a population from an existing list of individuals, placing them all in generation 0.
Examples
pea.create_multiple(10) |> from_list
// -> Population with 10 individuals in generation 0
pub fn new() -> Population
Creates an empty population with no individuals and no generations
pub fn select_all(population: Population) -> MatingCandidates
A SelectMates implementation that places every individual in both the
male and female candidate pools.
Suitable for hermaphroditic species such as the garden pea. Note that because both pools contain all individuals, self-fertilisation is possible.
Examples
let candidates = select_all(population)
list.length(candidates.males) == list.length(candidates.females)
// -> True
pub fn simulate(
population: Population,
cycles: Int,
rules: PopulationRules,
) -> Population
Runs the simulation for a given number of cycles by calling advance
repeatedly.
Each cycle produces one new generation. When cycles reaches 0 the
population is returned as-is.
Examples
let result = simulate(population, 10, rules)
dict.size(result.generations)
// -> 11 (generation 0 plus 10 new generations)
pub fn to_json(
population: Population,
rules: individual.SpeciesRules,
) -> json.Json
Encodes a population as a JSON object.
Keys are generation numbers (as strings) and values are arrays of
individuals encoded via individual.to_json. Species rules are required
to resolve sex, fertility, and phenotype for each individual.
Examples
population |> to_json(pea.rules()) |> json.to_string
// -> "{\"0\":[{...},{...}],\"1\":[{...}]}"