Spaces:
Sleeping
Sleeping
import streamlit as st | |
from dynamical_system import invasion_fitness, invasion_fitness2, invasion_fitness3, pop_dynamics2 | |
import numpy as np | |
from tools import plot_3D_invfitness, plot_invasionfitness, make_interactive_video, plot_PIP | |
from scipy.integrate import solve_ivp | |
import plotly.graph_objects as go | |
st.set_page_config(layout="wide") | |
st.title("Adaptive dynamics") | |
st.subheader("Example 1: When there is no cost on reproduction") | |
st.write( | |
r""" | |
The ecological dynamics of the resident is | |
$\frac{dn_r}{dt} = n_r(z_r - \alpha n_r)$ | |
where $z_r$ is the intrinsic growth rate of the resident, | |
$\alpha$ is the competition coefficient among resident individual | |
The resident reaches equilibrium $n^* = \frac{z_r}{\alpha}$ before a mutant arises. | |
New mutant with a different intrinsic growth rate $z_m$ arises has dynamics as followed | |
$\frac{dn_m}{dt} = n_m(z_m - \alpha (n_r + n_m))$ | |
The mutant growth rate is $r(z_m, z) = z_m - \alpha n^*$. | |
This is also the invasion fitness of the mutant. | |
The invasion fitness depends on the mutant's trait value $z_m$, | |
and the resident's trait value $z_r$ through the resident density at equilibrium $n^*$ | |
The video shows the process of mutant invasion and replacement | |
""" | |
) | |
zlist = np.linspace(0, 2, 100) | |
alpha = 0.4 | |
X, Y = np.meshgrid(zlist, zlist) | |
inv_fitness3D = invasion_fitness(X, Y, pars=alpha) | |
st.write("Invasion process video") | |
st.plotly_chart( | |
make_interactive_video( | |
0.01, zlist[-1], 50, zlist, invasion_fitness, alpha, [-2, 2]) | |
) | |
st.write( | |
""" | |
Now you can try varying the mutant trait value to see how the fitness landscape change. | |
Some suggestions for you to think: | |
- In which case mutants with smaller growth rate value invade? | |
- Do you see the relateness among the three graphs? | |
Hint: Rotate the 3D graph to match with the axes of the first two graph to see if: | |
The first graph is the vertical slice (gray surface) in the last graph | |
PIP is the projection of the fitness surface in the last graph, | |
""" | |
) | |
zm = st.slider("Mutant trait value", 0.0, 2.0, value=0.2, step=0.01) | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.plotly_chart(plot_invasionfitness( | |
zm, zlist, invasion_fitness, alpha, [-2, 2])) | |
with col2: | |
st.plotly_chart(plot_PIP(zlist, invasion_fitness, alpha)) | |
with col3: | |
st.plotly_chart(plot_3D_invfitness(zlist, inv_fitness3D, zm, (-4, 4))) | |
st.header("When there is cost in reproduction") | |
st.write( | |
r""" | |
Now we include some cost in having a high intrinsical growth rate. | |
The ecological dynamics of the resident is now | |
$\frac{dn_r}{dt} = n_r(z_r - z_r^\beta - \alpha n_r)$ | |
Do you see that now if the growth rate $z_r$ increases then there is an additional cost $z_r^\beta$. | |
The value of $\beta$ affect the shape of the cost | |
""" | |
) | |
zlist = np.linspace(0, 1, 100) | |
col_par1, col_par2 = st.columns(2, gap="large") | |
with col_par1: | |
beta = st.slider(r"Value of $\beta$", 0.1, 2.0, value=1.2, step=0.01) | |
with col_par2: | |
z_val = st.slider("Trait value", 0.0, 1.0, value=0.1, step=0.01) | |
z_star = (1 / beta) ** (1 / (beta - 1)) | |
ndsol = solve_ivp( | |
pop_dynamics2, | |
(0, 550), | |
[np.random.uniform(0, 0.05)], | |
t_eval=np.linspace(0, 550, 200), | |
args=((alpha, beta, z_val),), | |
) | |
col5, col6 = st.columns(2, gap="large") | |
with col5: | |
if ndsol.y[0, -1] > 0: | |
st.write( | |
r"The population density reaches $\frac{z_r - z_r^\beta}{\alpha}$ = ", ndsol.y[0, -1]) | |
else: | |
st.write("The population density reaches", 0) | |
st.plotly_chart( | |
go.Figure( | |
data=go.Scatter(x=ndsol.t, y=ndsol.y[0, :], mode="lines"), | |
layout=go.Layout( | |
xaxis_title="Time", yaxis_title="Population dynamics", yaxis=dict(range=(0, 0.3)) | |
), | |
), | |
) | |
with col6: | |
st.plotly_chart( | |
go.Figure( | |
data=[ | |
go.Scatter(x=zlist, y=zlist, name="Intrinsic growth rate"), | |
go.Scatter(x=zlist, y=zlist**beta, name="Cost on mortality"), | |
] | |
) | |
) | |
st.write("Invasion process video") | |
z_start = st.number_input( | |
"Enter the start value of z then click play", 1e-5, 1.0, 0.1, step=0.01 | |
) | |
if z_start - z_start**beta < 0: | |
st.write("Population goes extinct") | |
else: | |
if beta > 1: | |
z_end = z_star | |
elif beta < 1: | |
zlist = np.linspace(0, 2, 100) | |
z_end = zlist[-1] | |
st.plotly_chart( | |
make_interactive_video( | |
z_start, z_end, 20, zlist, invasion_fitness2, (alpha, beta), [ | |
-0.2, 0.2] | |
) | |
) | |
st.write("Now try yourself with the step by step invasion replacement process to verify the video") | |
col7, col8, col9 = st.columns(3, gap="large") | |
with col7: | |
zm2 = st.slider("Mutant trait value", 0.0, 1.0, value=0.1, step=0.01) | |
st.plotly_chart(plot_invasionfitness( | |
zm2, zlist, invasion_fitness2, (alpha, beta), [-0.2, 0.2])) | |
X, Y = np.meshgrid(zlist, zlist) | |
inv_fitness3D2 = invasion_fitness2(X, Y, pars=(alpha, beta)) | |
range = (np.min(inv_fitness3D2) - np.mean(inv_fitness3D2) - | |
1e-5, np.max(inv_fitness3D2)) | |
with col8: | |
st.plotly_chart(plot_PIP(zlist, invasion_fitness2, (alpha, beta))) | |
with col9: | |
st.plotly_chart(plot_3D_invfitness(zlist, inv_fitness3D2, zm, range)) | |
st.header("Assymetric competition") | |
st.write(r""" | |
Assymetric competition results in evolutionary branching | |
Now the intrinsic growth rate is no longer a linear function of the trait z. It follows a Gaussian distribution. | |
$\frac{dn_r}{dt} = (\rho(z) - \alpha(\delta_z) n_r) n_r$ | |
where $\rho(z) = e^{-(z - z_0)^2}$ is a Gaussian function. | |
""") | |
zlist = np.linspace(-3, 3, 100) | |
inv_fitness3D3 = invasion_fitness3(zlist, zlist, (0, 2.4)) | |
st.plotly_chart(plot_PIP(zlist, invasion_fitness3, (0, 2.4))) | |