<jupyter_start><jupyter_text>Prey Predator Lab Notebook(to edit this notebook and the associated python files, `git checkout` the corresponding commit by its hash, eg. `git checkout 422024d`)<jupyter_code>from IPython.display import display, Markdown
from datetime import datetime
cur_datetime = datetime.now()
display(Markdown(f'# {cur_datetime.strftime("%d/%b/%Y %H:%M")}'))<jupyter_output><empty_output><jupyter_text>Prey Predator dynamics on SD and ABMOn this notebook, we'll explore and visualize how P&P behave both in a dynamic system approach and in an agent-based one. Both models were written on cadCAD - a library for Complex Adaptive Dynamics simulations which allows you to mix and prototype different modelling paradigms in a reproducible and consistent manner.<jupyter_code>%%capture
%matplotlib inline

# Dependences
from time import time
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np

# Experiments
import run
# Run all experiments. Typical run duration for an Core-i3 laptop is about 2-3min.
# Tweak the prey_predator_abm/sim_params.py file if you want it to take longer (or not).
start_time = time()
experiments = run.run()
end_time = time()
print("Execution in {:.1f}s".format(end_time - start_time))<jupyter_output>___________    ____
  ________ __ ___/ / ____/   |  / __ \
 / ___/ __` / __  / /   / /| | / / / /
/ /__/ /_/ / /_/ / /___/ ___ |/ /_/ /
\___/\__,_/\__,_/\____/_/  |_/_____/
by cadCAD

Execution Mode: local_proc
Configuration Count: 2
Dimensions of the first simulation: (Timesteps, Params, Runs, Vars) = (1000, 6, 1, 2)<jupyter_text>Dynamical System: Lotka-VolterraThis is the standard introductory model for Prey-Predator interactions and dynamical systems in general. It is based on the following equations:\begin{aligned}{\frac {d}{dt}Prey}&=\alpha * Prey-\beta * Prey * Predators,\\{\frac {d}{dt}Predators}&=\delta * Prey * Predators-\gamma * Predators,\end{aligned}Where $\alpha$ and $\gamma$  are the prey growth rate and predator elimination rate, and $\delta$ and $\gamma$ are interaction factors between preys and predators.The most prominent feature of it is the existence, depending on the choice of parameters, of a repeteable cycle around a fixed point which creates a dynamical equilibrium between the number of preys and predators on a system.<jupyter_code>fig = px.scatter(experiments.dataset[0],
                 x='prey_population',
                 y='predator_population',
                 color='timestep')

fig.show('png')<jupyter_output><empty_output><jupyter_text>Agent-based modellingThere are a lot of possible ABMs for any given phenomenon. cadCAD allows you to add, modify and remove simulation blocks and steps at will. For this demo, we'll adopt a model based on a grid world, on which preys and predators take the following actions at each timestep of their lifes:1. Food is grown on every site.2. All agents digest some of the food on their stomach and get older.3. All agents move (if possible) to an available random neighboring location.4. The agents reproduce themselves if there is an available partner nearby5. The prey agents feed on the available food6. The predator agents hunts the nearby preys7. All old enough agents dieThere is an inherent stochastic nature on this model, and every time that you run it, we'll have a completely different result for the same parameters. But we can see that there is sort of a random equilibrium that converges to the dynamical equilibrium which we presented on the dynamical simulation.ABMs tend to produce rich, high density datasets. We'll plot some of this data, but invite the reader to fork this repository and trace the network relations between the agents, or the geospatial statistics around the ABM, for example.<jupyter_code># Get the ABM results
agent_ds = experiments.dataset[1].agents
site_ds = experiments.dataset[1].sites
timesteps = experiments.dataset[1].timestep
# Get metrics

## Agent quantity
prey_count = agent_ds.map(lambda s: sum([1 for agent in s.values() if agent['type'] == 'prey']))
predator_count = agent_ds.map(lambda s: sum([1 for agent in s.values() if agent['type'] == 'predator']))


## Food quantity
food_at_sites = site_ds.map(lambda s: s.sum())
food_at_prey = agent_ds.map(lambda s: sum([agent['food'] 
                                           for agent 
                                           in s.values() if agent['type'] == 'prey']))
food_at_predators = agent_ds.map(lambda s: sum([agent['food'] 
                                                for agent in s.values() 
                                                if agent['type'] == 'predator']))

## Food metrics
median_site_food = site_ds.map(lambda s: np.median(s)) 
median_prey_food = agent_ds.map(lambda s: np.median([agent['food'] 
                                                     for agent in s.values() 
                                                     if agent['type'] == 'prey']))
median_predator_food = agent_ds.map(lambda s: np.median([agent['food'] 
                                                         for agent in s.values() 
                                                         if agent['type'] == 'predator']))

## Age metrics
prey_median_age = agent_ds.map(lambda s: np.median([agent['age'] for agent in s.values() if agent['type'] == 'prey']))
predator_median_age = agent_ds.map(lambda s: np.median([agent['age'] for agent in s.values() if agent['type'] == 'predator']))

# Create an analysis dataset
data = (pd.DataFrame({'timestep': timesteps,
                      'prey_count': prey_count,
                      'predator_count': predator_count,
                      'food_at_sites': food_at_sites,
                      'food_at_prey': food_at_prey,
                      'food_at_predators': food_at_predators,
                      'median_site_food': median_site_food,
                      'median_prey_food': median_prey_food,
                      'median_predator_food': median_predator_food,
                      'prey_median_age': prey_median_age,
                      'predator_median_age': predator_median_age})       
       )<jupyter_output><empty_output><jupyter_text>A temporal summary for the simulationOn the following plot, we can see how the prey, predator and available food changes along time. Can you see any cyclical patterns?<jupyter_code>fig = go.Figure()

fig.add_trace(go.Scatter(x=timesteps, 
                         y=prey_count / prey_count.max(),
                         mode='lines',
                         name='Prey count (normed)'))


fig.add_trace(go.Scatter(x=timesteps, 
                         y=food_at_sites / food_at_sites.max(),
                         mode='lines',
                         name='Predator count (normed)'))


fig.add_trace(go.Scatter(x=timesteps, 
                         y=predator_count / prey_count.max(),
                         mode='lines',
                         name='Food count (normed)'))

fig.show('png')<jupyter_output><empty_output><jupyter_text>Prey predator phase spaceNote that it sorts of converges, in a chaotic manner, to the dynamical system model<jupyter_code>fig = px.scatter(data,
                 x='prey_count',
                 y='predator_count',
                 color='timestep')
fig.show('png')<jupyter_output><empty_output><jupyter_text>Prey prevalence vs food availability on the worldThere is an inverse relation between the available food on the grid, and the number of prey.From the code, can you tell why there is higher variance when the number of prey is higher?<jupyter_code>fig = px.scatter(data,
                 x='prey_count',
                 y='food_at_sites',
                 color='timestep')
fig.show('png')<jupyter_output><empty_output><jupyter_text>Food inside agents stomachsOn average, the prey stomachs are always quite full, while the predators stomachs contents vary more.<jupyter_code>fig = px.scatter(data,
                 x='median_predator_food',
                 y='median_prey_food',
                 color='timestep')
fig.show('png')<jupyter_output><empty_output><jupyter_text>Brute force plottingThere are a lot of patterns to explore and extract knowlodge. On the following plot, you can see all pairwise variable comparisons. Can you notice anything interesting worth investigating further?<jupyter_code>import seaborn as sns
import matplotlib.pyplot as plt

sns.pairplot(data=data, 
             diag_kind='hist', 
             markers='+')
plt.show('png')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09320843091334895, 'fraction_numerical': 0.003629976580796253, 'mean_word_length': 2.903564899451554, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '138173', 'license': 'MIT', 'n_tokens_mistral': 2451, 'n_tokens_neox': 2319, 'n_words': 797, 'path': 'demos/prey-predator/lab_notebook.ipynb', 'repo_name': 'calvbore/demos', 'size': '8540'}