Metadata
Every Population has MetaData attached. They store for every genotype
- population count. While in principle redundant, it is significantly cheaper to keep a tally during a simulation than to iterate the entire lattice.
 - fitness value
 - TODO: death rate
 - mutations: either 
nothing, or a vector ofIntif mutations are present - the time when the genotype entered the population
 
Additionally, the field misc is a dictionary for storing arbitrary user-defined key-value pairs.
Aside from using the value of genotype to index into metadata, one can use the linear index of the underlying array in which they are stored. The function index queries the linear index of a given genotype. An example is given below.
While insertions only happen at the end and leave linear indices unchanged, deletions will shift them. You should not rely on a particular index mapping to a given genotype when deletions are performed.
Convenient getter and setter are provided. Let's demonstrate on an example similar to the one from the Quick Start section, but label genotypes with strings of fixed length (from InlineStrings.jl)
julia> using InlineStringsjulia> import Base: zerojulia> zero(::Type{String7}) = String7("00-00")zero (generic function with 83 methods)julia> state, _ = spheref(HexagonalLattice, 128, f=1/10, g1=String7("00-00"), g2=String7("00-AA"))(HexagonalLattice{InlineStrings.String7, Matrix{InlineStrings.String7}} 1 genotypes 1789 population, CartesianIndex{2}[CartesianIndex(60, 43), CartesianIndex(62, 43), CartesianIndex(63, 43), CartesianIndex(64, 43), CartesianIndex(65, 43), CartesianIndex(66, 43), CartesianIndex(67, 43), CartesianIndex(68, 43), CartesianIndex(70, 43), CartesianIndex(56, 44) … CartesianIndex(66, 86), CartesianIndex(67, 86), CartesianIndex(68, 86), CartesianIndex(69, 86), CartesianIndex(70, 86), CartesianIndex(71, 86), CartesianIndex(73, 86), CartesianIndex(63, 87), CartesianIndex(65, 87), CartesianIndex(67, 87)])julia> function newlabel(state,g) l = String7( join(rand('0':'9',2))*"-"*join(rand('A':'Z',2)) ) while l in state.meta[:, :genotype] l = String7( join(rand('0':'9',2))*"-"*join(rand('A':'Z',2)) ) end return l endnewlabel (generic function with 1 method)julia> eden_with_density!(state; label=newlabel, T=1024, # timesteps mu=1e0, # mutation rate per genome (not site!) d=1/100, # death rate fitness=(s,g_old,g_new)->1.0 + 0.1*randn() # function to assign fitness to new genotypes )julia> state.meta524-element MetaData{InlineStrings.String7}: (genotype = "00-AA", npop = 1752, fitness = 1.0, snps = nothing, age = (0, 0.0)) (genotype = "46-YQ", npop = 2, fitness = 0.8026922162866656, snps = [110084262, 253811787, 491383196, 565145360, 749194060], age = (0, 0.0)) (genotype = "96-XJ", npop = 1, fitness = 0.869823904746547, snps = [314518933, 400909946], age = (5, 0.10023335135522374)) (genotype = "17-SF", npop = 1, fitness = 0.9834021327434191, snps = [435520975], age = (6, 0.10185923369599444)) (genotype = "71-ST", npop = 1, fitness = 0.9218723537854466, snps = [479112480, 517628781, 943209862, 954631554], age = (14, 0.17555368249455572)) (genotype = "54-ZW", npop = 1, fitness = 0.8723606893869594, snps = [238562147], age = (18, 0.2019425087395775)) (genotype = "91-BB", npop = 4, fitness = 1.1097052357136297, snps = [205402397, 520761909, 569074237, 874639413], age = (23, 0.24336823090956902)) (genotype = "73-LC", npop = 1, fitness = 0.9429685908671765, snps = [360126036], age = (26, 0.2748001729518197)) (genotype = "35-UW", npop = 2, fitness = 1.0978922556179902, snps = [672492348], age = (27, 0.29377129088071935)) (genotype = "22-CM", npop = 3, fitness = 0.9108345792617524, snps = [435219219], age = (28, 0.30010234602896746)) ⋮ (genotype = "79-NI", npop = 1, fitness = 0.9196751074441047, snps = [776304358, 862648082], age = (1010, 7.599596469291105)) (genotype = "36-HN", npop = 1, fitness = 0.9471067845342037, snps = [266825313, 363001712, 506492735, 706422805, 767131980], age = (1011, 7.617345626199722)) (genotype = "66-ZQ", npop = 1, fitness = 0.876064872204234, snps = [377471091, 706940438, 857078752], age = (1012, 7.6278173847330795)) (genotype = "19-QF", npop = 1, fitness = 0.9579578044887453, snps = [108939159, 193120121, 222302266, 294783611, 498556788, 666825034, 871535173], age = (1013, 7.633293548955527)) (genotype = "26-HW", npop = 1, fitness = 1.0985091020078388, snps = [243554223, 603146202, 752612901, 839893191, 945853422, 974079460], age = (1017, 7.659144400146129)) (genotype = "04-EZ", npop = 1, fitness = 1.1177314304176673, snps = [19027929, 521511857], age = (1018, 7.6616833761353655)) (genotype = "07-YI", npop = 1, fitness = 0.9599311049567875, snps = [182584633, 642509645, 754868161, 909947272, 921455577, 989635317], age = (1019, 7.663833254109903)) (genotype = "42-PD", npop = 1, fitness = 0.9871456754594935, snps = [501650746, 647956475, 831204264], age = (1020, 7.673283160925875)) (genotype = "26-SD", npop = 1, fitness = 1.144614231051611, snps = [45842811, 61818289, 100338695, 293302154, 302462774, 345865540, 398472030, 585210393, 621587900, 744600308, 989340177], age = (1021, 7.675782629293754))
Query according to index
julia> state.meta[2, :genotype]"46-YQ"julia> state.meta[5:10, :npop]6-element Vector{Int64}: 1 1 4 1 2 3
or genotype
julia> state.meta[g="46-YQ"](genotype = InlineStrings.String7("46-YQ"), npop = 2, fitness = 0.8026922162866656, snps = [110084262, 253811787, 491383196, 565145360, 749194060], age = (0, 0.0))julia> state.meta[g="46-YQ", :fitness] = 1.11.1
In places where performance is paramount, getter and setter should be called like state.meta[2, Val(:genotype)] to circumvent dynamic dispatch.
Alternatively, getgenotype(state, id), setgenotype!(state, id), etc. are provided.
Because MetaData implements Julia's array interface, iterating is supported, and because each MetaDatum is a NamedTuple, conversion to e.g. a DataFrame is straightforward
julia> using DataFramesjulia> filter(v->v.npop>10, state.meta) |> DataFrame1×5 DataFrame Row │ genotype npop fitness snps age │ String7 Int64 Float64 Union… Tuple… ─────┼──────────────────────────────────────────── 1 │ 00-AA 1752 1.0 (0, 0.0)
API
GrowthDynamics.Populations.MetaData — TypeMetaData{T}Indexable structure to store information about a population.
Access
Use meta[id, :field] or meta[g=G, :field] to access and set information for genotype G, or the genotype with id id respectively.
id is also the number of the vertex in the phylogeny corresponding to a given genotype.
:field is one of
:npop- population size:fitness- fitness value:genotype- genotype corresponding to a given id:snps: - vector of integers representing mutations a genotype carries.nothingif no mutations are present.:age: tuple of(timestep, realtime)of when the genotype was first instantiated.
Useful for putting lengths on the branches of a phylogeny.
Additionally, a global field meta.misc::Dict{Any,Any} exists to store arbitrary, user-defined information.
See also MetaDatum
GrowthDynamics.Populations.MetaDatum — TypeMetaDatum{T,S}NamedTuple to store information about a single genotype of type T that carries mutations of type S (default Int).
Dictionaries.index — Methodindex(M, g)Return the index of genotype g, or nothing if g is not in meta data M.
GrowthDynamics.Populations.index! — Functionindex!(::MetaData)Reindex the metadata.
DataFrames.rename! — Functionrename!(S, g1 => g2)Rename genotype g1 to g2.
GrowthDynamics.Populations.hassnps — Functionhassnps(M::MetaData; g)Return true if genotype contains mutations.
GrowthDynamics.Populations.lastgenotype — Functionlastgenotype(M::MetaData)Return genotype that was added last.
Genotype that was last added to the population.
Base.length — Functionlength(L::RealLattice)Number of elements of L.data.