Cookbook

On this page, we present a complete simulation structure to get started with Blossom. In this case, we aim to model a predator-prey dynamic.

First, we can create a basic directory structure for our project:

predator-prey/
    config.yml
    custom.py

Your config file might look like this:

species:
  - name: predator
    population: 100
    max_age: inf
    action: predator_action
    movement: simple_random
    reproduction:
      type: pure_replication
    eating:
      type: eat_prey
      capacity: 250
      initial: 200
      metabolism: 10
      intake: 80
      days_without: 5
    linked_modules:
      - custom.py
  - name: prey
    population: 500
    max_age: inf
    action: prey_action
    movement: simple_random
    reproduction:
      type: pure_replication
    linked_modules:
      - custom.py
world:
  dimensionality: 2
  size: [1, 100]
  water:
    peak: inf
  food:
    peak: inf
  obstacles:
    peak: 0
timesteps: 5000
organism_limit: 20000

This produces a 1 x 100 world (technically one-dimensional) with unlimited resources, to focus on the organism interactions.

The custom methods are defined in external modules, such as this in custom.py:

import numpy as np

def predator_action(organism, universe):
    if universe.rng.random() < 1/2:
        if organism.food_current > organism.food_capacity // 2:
            return 'reproduce'
        else:
            return 'eat'
    else:
        if universe.rng.random() < 2/10: # 1/10:
            return 'eat'
        else:
            return 'move'

def eat_prey(organism, universe):
    location = tuple(organism.location)
    colocated_prey = []
    colocated_predators = []
    for org in universe.organisms_by_location[location]:
        if org.alive:
            if org.species_name == 'prey1':
                colocated_prey.append(org)
            elif org.species_name == 'predator1':
                colocated_predators.append(org)
    if len(colocated_prey) == 0 or len(colocated_prey) <= len(colocated_predators):
        return [organism]
    elif len(colocated_prey) == 1:
        prey = colocated_prey[0]
    else:
        prey = universe.rng.choice(colocated_prey)

    # food_from_prey = 0.8 * (prey.food_capacity)
    food_from_prey = organism.food_intake
    diff = organism.food_capacity - organism.food_current
    intake = min(food_from_prey, diff)
    organism = organism.update_parameter('food_current',
                                        intake,
                                        method='add')

    prey = prey.die('eaten')

    return [organism, prey]

def prey_action(organism, universe):
    if universe.rng.random() < 1/30:
        return 'reproduce'
    else:
        return 'move'

Notice that in the config file, the custom methods are listed by name and the external modules are linked via the linked_modules keyword.

To execute simulations, we can run this command within the project directory:

blossom run -s SEED

While it isn’t necessary by any means, setting a seed at runtime promotes reproducibility. If the run is interrupted, you can re-run this same command and it will attempt to continue from the last point. Otherwise, if you wish to restart from the beginning, run blossom run with the -r flag.