Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,13 +1,36 @@
|
|
1 |
import streamlit as st
|
2 |
-
from dynamical_system import invasion_fitness, invasion_fitness2
|
3 |
import numpy as np
|
4 |
from tools import plot_3D_invfitness, plot_invasionfitness, make_interactive_video, plot_PIP
|
|
|
|
|
5 |
|
6 |
st.set_page_config(layout="wide")
|
7 |
st.title("Adaptive dynamics")
|
8 |
|
9 |
-
st.subheader("When there is no cost on reproduction")
|
|
|
|
|
|
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
zlist = np.linspace(0, 2, 100)
|
13 |
alpha = 0.4
|
@@ -15,45 +38,127 @@ alpha = 0.4
|
|
15 |
X, Y = np.meshgrid(zlist, zlist)
|
16 |
inv_fitness3D = invasion_fitness(X, Y, pars=alpha)
|
17 |
|
|
|
18 |
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
0.01, zlist[-1], 0.03, zlist, invasion_fitness, alpha, [-2, 2]))
|
23 |
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
zm, zlist, invasion_fitness, alpha, [-2, 2]))
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
st.plotly_chart(plot_PIP(zlist, invasion_fitness, alpha))
|
|
|
|
|
35 |
|
36 |
st.header("When there is cost in reproduction")
|
37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
zlist = np.linspace(0, 1, 100)
|
39 |
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
col5, col6 = st.columns(2, gap="large")
|
42 |
with col5:
|
|
|
|
|
|
|
|
|
43 |
st.plotly_chart(
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
46 |
)
|
47 |
with col6:
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
54 |
|
55 |
col7, col8 = st.columns(2, gap="large")
|
56 |
with col7:
|
57 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
with col8:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
st.plotly_chart(plot_PIP(zlist, invasion_fitness2, (alpha, beta)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
from dynamical_system import invasion_fitness, invasion_fitness2, invasion_fitness3, pop_dynamics2
|
3 |
import numpy as np
|
4 |
from tools import plot_3D_invfitness, plot_invasionfitness, make_interactive_video, plot_PIP
|
5 |
+
from scipy.integrate import solve_ivp
|
6 |
+
import plotly.graph_objects as go
|
7 |
|
8 |
st.set_page_config(layout="wide")
|
9 |
st.title("Adaptive dynamics")
|
10 |
|
11 |
+
st.subheader("Example 1: When there is no cost on reproduction")
|
12 |
+
st.write(
|
13 |
+
r"""
|
14 |
+
The ecological dynamics of the resident is
|
15 |
|
16 |
+
$\frac{dn_r}{dt} = n_r(z_r - \alpha n_r)$
|
17 |
+
|
18 |
+
where $z_r$ is the intrinsic growth rate of the resident,
|
19 |
+
$\alpha$ is the competition coefficient among resident individual
|
20 |
+
|
21 |
+
The resident reaches equilibrium $n^* = \frac{z_r}{\alpha}$ before a mutant arises.
|
22 |
+
New mutant with a different intrinsic growth rate $z_m$ arises has dynamics as followed
|
23 |
+
|
24 |
+
$\frac{dn_m}{dt} = n_m(z_m - \alpha (n_r + n_m))$
|
25 |
+
|
26 |
+
The mutant growth rate is $r(z_m, z) = z_m - \alpha n^*$.
|
27 |
+
This is also the invasion fitness of the mutant.
|
28 |
+
The invasion fitness depends on the mutant's trait value $z_m$,
|
29 |
+
and the resident's trait value $z_r$ through the resident density at equilibrium $n^*$
|
30 |
+
|
31 |
+
The video shows the process of mutant invasion and replacement
|
32 |
+
"""
|
33 |
+
)
|
34 |
|
35 |
zlist = np.linspace(0, 2, 100)
|
36 |
alpha = 0.4
|
|
|
38 |
X, Y = np.meshgrid(zlist, zlist)
|
39 |
inv_fitness3D = invasion_fitness(X, Y, pars=alpha)
|
40 |
|
41 |
+
st.write("Invasion process video")
|
42 |
|
43 |
+
st.plotly_chart(
|
44 |
+
make_interactive_video(0.01, zlist[-1], 50, zlist, invasion_fitness, alpha, [-2, 2])
|
45 |
+
)
|
|
|
46 |
|
47 |
|
48 |
+
st.write(
|
49 |
+
"""
|
50 |
+
Now you can try varying the mutant trait value to see how the fitness landscape change.
|
|
|
51 |
|
52 |
+
Some suggestions for you to think:
|
53 |
+
|
54 |
+
- In which case mutants with smaller growth rate value invade?
|
55 |
+
|
56 |
+
- Do you see the relateness among the three graphs?
|
57 |
+
|
58 |
+
Hint: Rotate the 3D graph to match with the axes of the first two graph to see if:
|
59 |
+
|
60 |
+
The first graph is the vertical slice (gray surface) in the last graph
|
61 |
+
|
62 |
+
PIP is the projection of the fitness surface in the last graph,
|
63 |
+
|
64 |
+
"""
|
65 |
+
)
|
66 |
+
|
67 |
+
zm = st.slider("Mutant trait value", 0.0, 2.0, value=0.2, step=0.01)
|
68 |
+
|
69 |
+
col1, col2, col3 = st.columns(3)
|
70 |
+
with col1:
|
71 |
+
st.plotly_chart(plot_invasionfitness(zm, zlist, invasion_fitness, alpha, [-2, 2]))
|
72 |
+
with col2:
|
73 |
st.plotly_chart(plot_PIP(zlist, invasion_fitness, alpha))
|
74 |
+
with col3:
|
75 |
+
st.plotly_chart(plot_3D_invfitness(zlist, inv_fitness3D, zm, (-4, 4)))
|
76 |
|
77 |
st.header("When there is cost in reproduction")
|
78 |
|
79 |
+
st.write(
|
80 |
+
r"""
|
81 |
+
Now we include some cost in having a high intrinsical growth rate.
|
82 |
+
|
83 |
+
The ecological dynamics of the resident is now
|
84 |
+
|
85 |
+
$\frac{dn_r}{dt} = n_r(z_r - z_r^\beta - \alpha n_r)$
|
86 |
+
|
87 |
+
Do you see that now if the growth rate $z_r$ increases then there is an additional cost $z_r^\beta$.
|
88 |
+
|
89 |
+
The value of $\beta$ affect the shape of the cost
|
90 |
+
"""
|
91 |
+
)
|
92 |
zlist = np.linspace(0, 1, 100)
|
93 |
|
94 |
+
col_par1, col_par2 = st.columns(2, gap="large")
|
95 |
+
with col_par1:
|
96 |
+
beta = st.slider(r"Value of $\beta$", 0.1, 2.0, value=1.2, step=0.01)
|
97 |
+
with col_par2:
|
98 |
+
z_val = st.slider("Trait value", 0.0, 1.0, value=0.1, step=0.01)
|
99 |
+
|
100 |
+
z_star = (1 / beta) ** (1 / (beta - 1))
|
101 |
+
|
102 |
+
ndsol = solve_ivp(
|
103 |
+
pop_dynamics2,
|
104 |
+
(0, 550),
|
105 |
+
[np.random.uniform(0, 0.05)],
|
106 |
+
t_eval=np.linspace(0, 550, 200),
|
107 |
+
args=((alpha, beta, z_val),),
|
108 |
+
)
|
109 |
+
|
110 |
col5, col6 = st.columns(2, gap="large")
|
111 |
with col5:
|
112 |
+
if ndsol.y[0, -1] > 0:
|
113 |
+
st.write("The population density reaches", ndsol.y[0, -1])
|
114 |
+
else:
|
115 |
+
st.write("The population density reaches", 0)
|
116 |
st.plotly_chart(
|
117 |
+
go.Figure(
|
118 |
+
data=go.Scatter(x=ndsol.t, y=ndsol.y[0, :], mode="lines"),
|
119 |
+
layout=go.Layout(
|
120 |
+
xaxis_title="Time", yaxis_title="Population dynamics", yaxis=dict(range=(0, 0.3))
|
121 |
+
),
|
122 |
+
),
|
123 |
)
|
124 |
with col6:
|
125 |
+
st.plotly_chart(
|
126 |
+
go.Figure(
|
127 |
+
data=[
|
128 |
+
go.Scatter(x=zlist, y=zlist, name="Intrinsic growth rate"),
|
129 |
+
go.Scatter(x=zlist, y=zlist**beta, name="Cost on mortality"),
|
130 |
+
]
|
131 |
+
)
|
132 |
+
)
|
133 |
|
134 |
col7, col8 = st.columns(2, gap="large")
|
135 |
with col7:
|
136 |
+
st.write("Invasion process video")
|
137 |
+
z_start = st.number_input(
|
138 |
+
"Enter the start value of z then click play", 1e-5, 1.0, 0.1, step=0.01
|
139 |
+
)
|
140 |
+
st.plotly_chart(
|
141 |
+
make_interactive_video(
|
142 |
+
z_start, z_star, 20, zlist, invasion_fitness2, (alpha, beta), [-0.2, 0.2]
|
143 |
+
)
|
144 |
+
)
|
145 |
with col8:
|
146 |
+
zm2 = st.slider("Mutant trait value", 0.0, 1.0, value=0.1, step=0.01)
|
147 |
+
|
148 |
+
st.plotly_chart(plot_invasionfitness(zm2, zlist, invasion_fitness2, (alpha, beta), [-0.2, 0.2]))
|
149 |
+
|
150 |
+
X, Y = np.meshgrid(zlist, zlist)
|
151 |
+
inv_fitness3D2 = invasion_fitness2(X, Y, pars=(alpha, beta))
|
152 |
+
|
153 |
+
col9, col10 = st.columns(2, gap="large")
|
154 |
+
range = (np.min(inv_fitness3D2) - np.mean(inv_fitness3D2) - 1e-5, np.max(inv_fitness3D2))
|
155 |
+
with col9:
|
156 |
+
st.plotly_chart(plot_3D_invfitness(zlist, inv_fitness3D2, zm, range))
|
157 |
+
with col10:
|
158 |
st.plotly_chart(plot_PIP(zlist, invasion_fitness2, (alpha, beta)))
|
159 |
+
|
160 |
+
|
161 |
+
st.header("Assymetric competition")
|
162 |
+
zlist = np.linspace(-3, 3, 100)
|
163 |
+
inv_fitness3D3 = invasion_fitness3(zlist, zlist, (0, 2.4))
|
164 |
+
st.plotly_chart(plot_PIP(zlist, invasion_fitness3, (0, 2.4)))
|