Evolving populations
Routines to advance a population state for a number of time steps, or until a pre-specified condition is met.
Available dynamics
All routines take a Population as their first and only positional argument, and further parameters like mutation rate as keyword arguments.
Spatial
An Eden-like model
GrowthDynamics.LatticeDynamics.eden_with_density! — Functioneden_with_density!(state::Population)Cells proliferate to neighboring lattice sites with a rate proportional to the number of free neighboring sites. A time step is either a birth, death, or mutation-only event.
A custom label function must be provided if genotypes are not integers.
See the 'extended help' for signatures of the various callbacks.
Keyword arguments
T::Int: number of steps to advance.d=0.0: death rate. Zero halts the dynamics after carrying capacity is reached.fitness=(population, old_genotype, new_genotype)->1.0: function that assigns a fitness value (default1.0) to a new genotype.mu=0.0: mutation rate; either a function or number.label=(population, old_genotype) -> lastgenotype(population)+1: function that assigns the new genotype upon mutation.makesnps=true: generate SNPs during the simulation?mu_type=[:poisson, :fixed]: number of mutations is fixed, or Poisson distributed.genome_length=10^9: length of the genome.baserate=1.0: progressing real time is measured in1/baserate.prune_period=0: prune the phylogeny periodically after no. of steps, set to0to disable.prune_on_exit=true: prune before leaving the simulation.onstep: callback that is executed at the very beginning of every time step.onprebirth: callback that runs if a birth event is about to occur. Return value determines whether cell actually proliferates.onpostbirth: callback that runs at the very end of a proliferation event.ondeath: callback that runs at the very end of a death event.
Extended help
Mutations
The rate of mutations is steered by the keyword argument mu, which is either a number or a function.
A number is automatically converted to an appropriate function. As a function it must have signature mu(population, old_genotype, I_old, I_new)->(rate, p_mutate)::Tuple{Float64, Float64} where
old_genotypeis the genotype of the parent cellI_old,I_neware the linear lattice indices of the parent and daughter cell.
This way the mutation rate can depend on position as well as on mutations present in the parental genome.
The output must be a tuple, where the first entry rate determines the number of mutations if mutations happen; the probability of which is given by the second entry p_mutate. The main reason for keeping these quantities separate is that one will often not generate SNPs during the simulation, because that process is rather costly, but generate them later on the basis of the final phylogeny.
If mu is a number, it is implicitely wrapped in a function that returns (mu, 1-exp(-mu)), i.e. the mutation probability is the probability of at least one event under a Poisson distribution with rate mu.
Setting makesnps to true/false determines whether SNPs are generated during the simulation. If it is set to false, the first value returned by mu is inconsequential.
Finally, label(population, old_genotype)->new_genotype assigns the designation to a newly entering genotype. It defaults to numbering genotypes consecutively. See also rename!.
Fitness
When a new genotype enters due to a mutation event, it is assigned a fitness value given by a user-provides function (population, old_genotype, new_genotype)->Float64 passed as keyword argument fitness. For example, to inherit the fitness, one would provide  fitness=(p,og,ng)->p.meta[g=og; :fitness].
Callbacks
Callbacks are triggered at certain stages of the simulation loop:
onstep(population)::Boolis executed at the beginning of each time step. Iftrueis returned, the simulation ends.onprebirth(population, Iold)::Boolis executed when a birth event is triggered, but before it is executed. Iffalseis returned, the cell will not proliferate, but might still mutate.onpostbirth(population, Iold, Inew)is executed after proliferation and mutation are completed. Useful for collecting observables.ondeath(population, Idead)is executed when a death event has finished.
Non-spatial
GrowthDynamics.LatticeDynamics.moran! — Functionmoran!(state::NoLattice{Int}; <keyword arguments>)(Generalized) Moran dynamics on an unstructured population. Birth and death events are independent until the carrying capacity (keyword argument K) is reached.  After that individuals begin replacing each other like in the classic Moran model.
Arguments
T::Int: the number of steps to advance.d=0.0: death rate.K=0: Carrying capacity. Set to0for unlimited.fitness=(population, old_genotype, new_genotype)->1.0: function that assigns a fitness value (default1.0) to a new genotype.mu=0.0: mutation rate; either a function or number.label=(population, old_genotype) -> lastgenotype(population)+1: function that assigns the new genotype upon mutation.makesnps=true: generate SNPs during the simulation?mu_type=[:poisson, :fixed]: number of mutations is fixed, or Poisson distributed.genome_length=10^9: length of the genome.baserate=1.0: progressing real time is measured in1/baserate.prune_period=0: prune the phylogeny periodically after no. of steps, set to0to disable.prune_on_exit=true: prune before leaving the simulation.onstep: callback that is executed at the very beginning of every time step.onprebirth: callback that runs if a birth event is about to occur. Return value determines whether cell actually proliferates.onpostbirth: callback that runs at the very end of a proliferation event.ondeath: callback that runs at the very end of a death event.
GrowthDynamics.LatticeDynamics.exponential! — Functionexponential!(state::NoLattice{Int}; <keyword arguments>)Run exponential growth on an unstructered population until carrying capacity is reached. No death.
Each generation consists of the following actions for every genotype:
- The number of decendents is drawn from a binomial distribution with parameters 
n, the population number of that genotype, andpgiven by1-exp(-f/<f>)with fitness valuef. That number as capped so as not to exceed the defined carrying capacity. - Of those decendents the number of mutants is drawn from a binomial distribution according to the mutation rate 
mu. 
Arguments
T::Int: number of steps to advance.fitness=(population, old_genotype, new_genotype)->1.0: function that assigns a fitness value (default1.0) to a new genotype.mu=0.0: mutation rate; either a function or number.label=(population, old_genotype) -> lastgenotype(population)+1: function that assigns the new genotype upon mutation.makesnps=true: generate SNPs during the simulation?mu_type=[:poisson, :fixed]: number of mutations is fixed, or Poisson distributed.genome_length=10^9: length of the genome.baserate=1.0: progressing real time is measured in1/baserate.prune_period=0: prune the phylogeny periodically after no. of steps, set to0to disable.prune_on_exit=true: prune before leaving the simulation.onstep: callback that is executed at the very beginning of every time step.ondeath: callback that runs at the very end of a death event.
Adding your own
There is no particular interface or signature an evolution routine must adhere to.
However, to avoid putting the lattice, metadata and phylogeny in an inconsistent state, a few tips should be followed
- Use the getter and setter methods for 
MetaDatainstead of manipulating the fields directly. - Using 
metadata[g=genotype; Val(:field)]is more performant thanmetadata[g=genotype; :field]if:fieldis constant, because the former avoids dynamic dispatch. - Use 
population[index] (= genotype)to get/set the genotype of a cell at a position. Do not manipulatepopulation.lattice.datadirectly. - Prefer 
add_genotype!overpush!. - Use 
remove_genotype!. - For performance reasons SNPs are either 
Vector{Int}ornothing. Check for the latter withhassnpsbefore adding new ones. - Not a must, but don't forget to advance the step/real time counters 
population.tandpopulation.trealafter each simulation step.