Tutorial

Installation

Install using the following command in a Julia REPL.

]add EvoDynamics

Basic usage

Parameters of a model should be put in a julia file (.jl format) with the structure below.

See Simple Wright-Fisher and Predator prey for complete examples of parameter files.

## 1. Functions
...

## 2. Species parameters. A dictionary for each species

## 3. Model parameters as a dictionary.

The order of these sections is important because each section relies on objects from its preceding sections.

Functions are used to create parameters that may change temporally and spatially. The following parameters are functions: bottleneck function, which kills specific agents at certain times and locations; optimal phenotype values, which returns the optimal phenotype for a species at a given time and location; and environmental resources that may change over time.

You may create as many species as you want. Parameters of each species are stored in a dictionary.

Model parameters is a dictionary that stores general parameters of the model, such as the number of generations, space size, and species interaction parameters.

First, define your model parameters (here, we call it parameters.jl) Simple Wright-Fisher and Predator prey have examples of initiation parameters. See Model Parameters and Simulation Outline for a description of each parameter.

You can use the runmodel function to create a model from these parameters and run the simulation.

EvoDynamics.runmodelFunction
runmodel(param_file::AbstractString; kwargs)

Creates and runs a model given parameters. Returns a DataFrame of collected data, which are specified by kwargs.

Keywords

  • adata=[] agent data to be collected. Either agent fields or functions that accept an agent as input can be put in the array. To aggregate collected data, provide tuples inside the array. For example, to collect mean and median fitness of individuals which is in field W, your array will be [(:W,mean), (:W,median)].
  • mdata=[meanfitnessperspecies, speciesN] model data to be collected. By default, collects mean population fitness per species. Each row of the output DataFrame corresponds to all agents and each column is the value function applied to a field. The functions in the array are applied to the model object.
  • when=nothing The generations from which data are collected. By default collect at all steps.
  • replicates::Int = 0 Number of replicates per simulation.
  • parallel::Bool = false Whether to run replicates in parallel. If true, you should add processors to your julia session (e.g. by addprocs(n)) and define your parameters and EvoDynamics on all workers. To do that, add @everywhere before them. For example, @everywhere EvoDynamics.
  • seeds = optionally, provide an array of integers as seeds for each replicate.
  • agentstep=EvoDynamics.agent_step! Define your own agent stepping if you wish to change the sequence of events or change any one event.
  • modelstep=EvoDynamics.model_step!
  • showprogress::Bool = false Whether to show a progress meter of the simulations.
source
using EvoDynamics
agentdata, modeldata, model = runmodel("parameters.jl")

Within runmodel, before the parameters are used for constructing and ABM object, they are checked for correctness in type and shape using the load_parameters function.

EvoDynamics.load_parametersFunction
load_parameters(paramfile::String)

Reads the parameters from a parameter file (.jl), checks them for correct format, and returns the model_parameters dictionary object. See the online docs for the list of parameters and their correct values.

The output of this function can be loaded to the model_initiation function to create an ABM object. A user would normally not need to do these steps directly, because they are handled by the runmodel function. These will be useful for debugging and running the model in a different way.

source

And then, using the model_initiation function, an agent-based model (ABM) is constructed.

EvoDynamics.model_initiationFunction
model_initiation(dd)

Innitializes the model and returns an ABM object that includes all the initial agents. The input of the function is a dictionary returned by the load_parameters function.

source

Having and ABM object and parameters that define the run conditions, the runmodel function uses run! or ensemblerun! functions from the Agents.jl package to run the model and collect data.

Creating simulation parameter files

EvoDynamics.jl reads simulation parameters (Model Parameters and Simulation Outline) from a julia file containing dictionaries and functions. This file can be populated manually using a text editor or from within a Julia session.

Collecting data

The interface to the model is from the runmodel function (see Tutorial).

EvoDynamics.jl uses Agents.jl underneath. See Agents.jl's documentation for details about writing functions to collect any data during simulations. Here, we explain the specific implementation of the model.

There are two main objects from which you can collect data: and agent object of type AbstractAgent and a model object of type ABM. Both of these types are defined the Agents.jl package.

Agent object has the following fields that are individual specific: id, pos, species, epistasisMat (epistasis matrix), pleiotropyMat (pleiotropy matrix), q (gene expression array), biotic_phenotype, abiotic_phenotype, age, sex, energy, interaction_history, and W (fitness).

The model object has the following fields that can be accessed with the . syntax and describe properties of species or the model: ngenes, nphenotypes, growthrates, selectionCoeffs, ploidy, optvals (optimal values), optinds (optval indices per generation), mutProbs (mutation probabilities), mutMagnitudes (mutation magnitudes), N, E (environmental noise), generations, nspecies, migration_traits, vision_radius, check_fraction, migration_thresholds, step, biotic_phenotypes (indices of biotic phenotypes per species), abiotic_phenotypes, max_ages, food_sources, interactions, resources, recombination, initial_energy.

You can collect data from agents and/or from the model object. To collect data from agents, use the adata keyword argument in the runmodel function, and to collect data from the model, use the mdata keyword. A complete description of the values these keywords take are at data collection section of the Agents.jl package.

For example, we use the function below to count the number of individual per species:

"Returns the size of each species."
function species_N(model::ABM)
  allagents = model.agents
  if length(allagents) == 0
    return fill(0, model.nspecies)
  else
    counts = countmap([a.species for a in values(model.agents)])
    output = fill(0, model.nspecies)
    for (k, v) in counts
      output[k] = v
    end
    return output
  end
end

using EvoDynamics

agentdata, modeldata, model = runmodel("parameters.yml", mdata=[species_N])

Running simulations in parallel

You can run replicate simulation in parallel. To that end, you need to add processors, and import EvoDynamics.jl and your parameters files on all cores:

using Distributed
addprocs(4)
@everywhere using EvoDynamics
@everywhere param_file = "params.jl"
@everywhere EvoDynamics.load_parameters(param_file)
adata, mdata, models = runmodel(param_file, replicates=10, parallel=true)

Modifying the sequence of events and specific functions

If you want to change the sequence of events within each time-step, you can do so by providing your own stepping function to the runmodel function. With this flexibility, you can also keep the sequence as it is, but change one specific event within the sequence by using a different function for it. A good staring point would be to copy the agent_step! function from the package (at src/simulation.jl).