Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/biology/population_dynamics.tex
51 views
unlisted
1
% Population Dynamics Template
2
% Topics: Lotka-Volterra predator-prey, stability analysis, limit cycles
3
% Style: Tutorial with analytical and numerical approaches
4
5
\documentclass[a4paper, 11pt]{article}
6
\usepackage[utf8]{inputenc}
7
\usepackage[T1]{fontenc}
8
\usepackage{amsmath, amssymb}
9
\usepackage{graphicx}
10
\usepackage{siunitx}
11
\usepackage{booktabs}
12
\usepackage{subcaption}
13
\usepackage[makestderr]{pythontex}
14
15
% Theorem environments
16
\newtheorem{definition}{Definition}[section]
17
\newtheorem{theorem}{Theorem}[section]
18
\newtheorem{example}{Example}[section]
19
\newtheorem{remark}{Remark}[section]
20
21
\title{Population Dynamics: Predator-Prey Models and Stability Analysis}
22
\author{Mathematical Biology Tutorial}
23
\date{\today}
24
25
\begin{document}
26
\maketitle
27
28
\begin{abstract}
29
This tutorial presents a comprehensive analysis of predator-prey population dynamics
30
using the Lotka-Volterra equations and their extensions. We examine the classical model,
31
perform stability analysis of equilibrium points, and explore modifications including
32
carrying capacity and functional responses. Computational analysis demonstrates phase
33
portraits, limit cycles, and the ecological implications of parameter variations.
34
\end{abstract}
35
36
\section{Introduction}
37
38
The Lotka-Volterra predator-prey model is a cornerstone of mathematical ecology,
39
describing the coupled dynamics of two interacting species. Despite its simplicity,
40
the model captures essential features of predator-prey oscillations observed in nature.
41
42
\begin{definition}[Lotka-Volterra Equations]
43
The classical predator-prey model is:
44
\begin{align}
45
\frac{dx}{dt} &= \alpha x - \beta xy \\
46
\frac{dy}{dt} &= \delta xy - \gamma y
47
\end{align}
48
where $x$ is prey density, $y$ is predator density, $\alpha$ is prey growth rate,
49
$\beta$ is predation rate, $\delta$ is predator growth efficiency, and $\gamma$ is
50
predator death rate.
51
\end{definition}
52
53
\section{Theoretical Framework}
54
55
\subsection{Equilibrium Analysis}
56
57
\begin{theorem}[Equilibrium Points]
58
The Lotka-Volterra system has two equilibrium points:
59
\begin{enumerate}
60
\item Trivial equilibrium: $(x^*, y^*) = (0, 0)$
61
\item Coexistence equilibrium: $(x^*, y^*) = (\gamma/\delta, \alpha/\beta)$
62
\end{enumerate}
63
\end{theorem}
64
65
\begin{theorem}[Stability of Coexistence Equilibrium]
66
The Jacobian matrix at $(x^*, y^*)$ is:
67
\begin{equation}
68
J = \begin{pmatrix}
69
0 & -\beta x^* \\
70
\delta y^* & 0
71
\end{pmatrix}
72
\end{equation}
73
The eigenvalues are $\lambda = \pm i\sqrt{\alpha\gamma}$, indicating a center (neutral
74
stability). Trajectories form closed orbits around the equilibrium.
75
\end{theorem}
76
77
\subsection{Extensions}
78
79
\begin{definition}[Logistic Prey Growth]
80
Adding carrying capacity to prey growth:
81
\begin{align}
82
\frac{dx}{dt} &= \alpha x \left(1 - \frac{x}{K}\right) - \beta xy \\
83
\frac{dy}{dt} &= \delta xy - \gamma y
84
\end{align}
85
This creates a globally stable equilibrium (spiral sink) for appropriate parameters.
86
\end{definition}
87
88
\begin{definition}[Holling Type II Functional Response]
89
Predator satiation is modeled by:
90
\begin{equation}
91
\frac{dx}{dt} = \alpha x - \frac{\beta xy}{1 + \beta h x}
92
\end{equation}
93
where $h$ is handling time per prey item.
94
\end{definition}
95
96
\begin{remark}[Functional Responses]
97
Holling classified functional responses as:
98
\begin{itemize}
99
\item \textbf{Type I}: Linear (constant attack rate)
100
\item \textbf{Type II}: Saturating (handling time limitation)
101
\item \textbf{Type III}: Sigmoidal (learning or switching behavior)
102
\end{itemize}
103
\end{remark}
104
105
\section{Computational Analysis}
106
107
\begin{pycode}
108
import numpy as np
109
import matplotlib.pyplot as plt
110
from scipy.integrate import odeint
111
from scipy.linalg import eig
112
113
np.random.seed(42)
114
115
# Classic Lotka-Volterra
116
def lotka_volterra(y, t, alpha, beta, delta, gamma):
117
x, p = y
118
dxdt = alpha * x - beta * x * p
119
dpdt = delta * x * p - gamma * p
120
return [dxdt, dpdt]
121
122
# With logistic prey growth
123
def lv_logistic(y, t, alpha, beta, delta, gamma, K):
124
x, p = y
125
dxdt = alpha * x * (1 - x/K) - beta * x * p
126
dpdt = delta * x * p - gamma * p
127
return [dxdt, dpdt]
128
129
# With Type II functional response
130
def lv_type2(y, t, alpha, beta, delta, gamma, h):
131
x, p = y
132
functional = (beta * x) / (1 + beta * h * x)
133
dxdt = alpha * x - functional * p
134
dpdt = delta * functional * p - gamma * p
135
return [dxdt, dpdt]
136
137
# Rosenzweig-MacArthur model
138
def rosenzweig_macarthur(y, t, r, K, a, h, e, d):
139
N, P = y
140
dNdt = r * N * (1 - N/K) - (a * N * P) / (1 + a * h * N)
141
dPdt = e * (a * N * P) / (1 + a * h * N) - d * P
142
return [dNdt, dPdt]
143
144
# Parameters
145
alpha = 1.0
146
beta = 0.1
147
delta = 0.075
148
gamma = 1.5
149
K = 100
150
151
# Initial conditions
152
x0, y0 = 10, 5
153
initial = [x0, y0]
154
155
# Time array
156
t = np.linspace(0, 100, 2000)
157
158
# Solve classic model
159
solution = odeint(lotka_volterra, initial, t, args=(alpha, beta, delta, gamma))
160
prey = solution[:, 0]
161
predators = solution[:, 1]
162
163
# Equilibrium points
164
x_eq = gamma / delta
165
y_eq = alpha / beta
166
167
# Solve logistic model
168
sol_logistic = odeint(lv_logistic, initial, t, args=(alpha, beta, delta, gamma, K))
169
170
# Solve Type II model
171
h = 0.5
172
sol_type2 = odeint(lv_type2, initial, t, args=(alpha, beta, delta, gamma, h))
173
174
# Rosenzweig-MacArthur with limit cycle
175
sol_rm = odeint(rosenzweig_macarthur, [10, 2], t, args=(1.0, 50, 0.1, 0.5, 0.5, 0.3))
176
177
# Multiple initial conditions for phase portrait
178
ics = [[5, 2], [10, 5], [15, 3], [8, 8], [20, 6]]
179
trajectories = []
180
for ic in ics:
181
sol = odeint(lotka_volterra, ic, t, args=(alpha, beta, delta, gamma))
182
trajectories.append(sol)
183
184
# Vector field for phase portrait
185
x_range = np.linspace(0.1, 40, 20)
186
y_range = np.linspace(0.1, 20, 20)
187
X, Y = np.meshgrid(x_range, y_range)
188
U = alpha * X - beta * X * Y
189
V = delta * X * Y - gamma * Y
190
speed = np.sqrt(U**2 + V**2)
191
U_norm = U / speed
192
V_norm = V / speed
193
194
# Isoclines
195
x_iso = np.linspace(0.1, 40, 200)
196
prey_iso = alpha / beta * np.ones_like(x_iso)
197
pred_iso = gamma / delta * np.ones_like(x_iso)
198
199
# Period estimation
200
prey_peaks = np.where((prey[1:-1] > prey[:-2]) & (prey[1:-1] > prey[2:]))[0] + 1
201
if len(prey_peaks) >= 2:
202
period = np.mean(np.diff(t[prey_peaks]))
203
else:
204
period = 0
205
206
# Jacobian analysis
207
J_eq = np.array([[0, -beta * x_eq],
208
[delta * y_eq, 0]])
209
eigenvalues, eigenvectors = eig(J_eq)
210
211
# Conservation quantity
212
H = delta * prey - gamma * np.log(prey) + beta * predators - alpha * np.log(predators)
213
H0 = H[0]
214
215
# Create figure
216
fig = plt.figure(figsize=(14, 12))
217
218
# Plot 1: Time series
219
ax1 = fig.add_subplot(3, 3, 1)
220
ax1.plot(t, prey, 'b-', linewidth=1.5, label='Prey')
221
ax1.plot(t, predators, 'r-', linewidth=1.5, label='Predators')
222
ax1.set_xlabel('Time')
223
ax1.set_ylabel('Population')
224
ax1.set_title('Classic Lotka-Volterra')
225
ax1.legend(fontsize=8)
226
227
# Plot 2: Phase portrait
228
ax2 = fig.add_subplot(3, 3, 2)
229
ax2.quiver(X, Y, U_norm, V_norm, speed, cmap='viridis', alpha=0.6)
230
for traj in trajectories:
231
ax2.plot(traj[:, 0], traj[:, 1], linewidth=1)
232
ax2.plot(x_eq, y_eq, 'r*', markersize=12, label='Equilibrium')
233
ax2.axhline(y=y_eq, color='green', linestyle='--', alpha=0.5)
234
ax2.axvline(x=x_eq, color='blue', linestyle='--', alpha=0.5)
235
ax2.set_xlabel('Prey')
236
ax2.set_ylabel('Predator')
237
ax2.set_title('Phase Portrait')
238
ax2.set_xlim([0, 40])
239
ax2.set_ylim([0, 20])
240
241
# Plot 3: Logistic prey growth
242
ax3 = fig.add_subplot(3, 3, 3)
243
ax3.plot(t, sol_logistic[:, 0], 'b-', linewidth=1.5, label='Prey')
244
ax3.plot(t, sol_logistic[:, 1], 'r-', linewidth=1.5, label='Predators')
245
ax3.set_xlabel('Time')
246
ax3.set_ylabel('Population')
247
ax3.set_title('With Logistic Prey Growth')
248
ax3.legend(fontsize=8)
249
250
# Plot 4: Logistic phase space
251
ax4 = fig.add_subplot(3, 3, 4)
252
ax4.plot(sol_logistic[:, 0], sol_logistic[:, 1], 'g-', linewidth=1)
253
ax4.plot(sol_logistic[0, 0], sol_logistic[0, 1], 'ko', markersize=8)
254
ax4.plot(sol_logistic[-1, 0], sol_logistic[-1, 1], 'r*', markersize=12)
255
ax4.set_xlabel('Prey')
256
ax4.set_ylabel('Predator')
257
ax4.set_title('Logistic Model Phase Space')
258
259
# Plot 5: Type II functional response
260
ax5 = fig.add_subplot(3, 3, 5)
261
ax5.plot(t, sol_type2[:, 0], 'b-', linewidth=1.5, label='Prey')
262
ax5.plot(t, sol_type2[:, 1], 'r-', linewidth=1.5, label='Predators')
263
ax5.set_xlabel('Time')
264
ax5.set_ylabel('Population')
265
ax5.set_title('Type II Functional Response')
266
ax5.legend(fontsize=8)
267
268
# Plot 6: Rosenzweig-MacArthur limit cycle
269
ax6 = fig.add_subplot(3, 3, 6)
270
ax6.plot(sol_rm[500:, 0], sol_rm[500:, 1], 'purple', linewidth=1.5)
271
ax6.set_xlabel('Prey')
272
ax6.set_ylabel('Predator')
273
ax6.set_title('Rosenzweig-MacArthur Limit Cycle')
274
275
# Plot 7: Conserved quantity
276
ax7 = fig.add_subplot(3, 3, 7)
277
ax7.plot(t, H, 'b-', linewidth=1.5)
278
ax7.axhline(y=H0, color='red', linestyle='--', alpha=0.7)
279
ax7.set_xlabel('Time')
280
ax7.set_ylabel('$H(x, y)$')
281
ax7.set_title('Conserved Quantity')
282
ax7.set_ylim([H0 - 1, H0 + 1])
283
284
# Plot 8: Functional responses
285
ax8 = fig.add_subplot(3, 3, 8)
286
N_range = np.linspace(0, 50, 200)
287
type1 = beta * N_range
288
type2 = (beta * N_range) / (1 + beta * h * N_range)
289
type3 = (beta * N_range**2) / (1 + beta * h * N_range**2)
290
ax8.plot(N_range, type1, 'b-', linewidth=2, label='Type I')
291
ax8.plot(N_range, type2, 'g-', linewidth=2, label='Type II')
292
ax8.plot(N_range, type3, 'r-', linewidth=2, label='Type III')
293
ax8.set_xlabel('Prey density $N$')
294
ax8.set_ylabel('Predation rate')
295
ax8.set_title('Functional Responses')
296
ax8.legend(fontsize=8)
297
298
# Plot 9: Parameter sensitivity
299
ax9 = fig.add_subplot(3, 3, 9)
300
alphas = [0.8, 1.0, 1.2]
301
colors = ['blue', 'green', 'red']
302
for a, c in zip(alphas, colors):
303
sol = odeint(lotka_volterra, initial, t, args=(a, beta, delta, gamma))
304
ax9.plot(t[:500], sol[:500, 0], color=c, linewidth=1.5, label=f'$\\alpha = {a}$')
305
ax9.set_xlabel('Time')
306
ax9.set_ylabel('Prey population')
307
ax9.set_title('Prey Growth Rate Sensitivity')
308
ax9.legend(fontsize=8)
309
310
plt.tight_layout()
311
plt.savefig('population_dynamics_analysis.pdf', dpi=150, bbox_inches='tight')
312
plt.close()
313
\end{pycode}
314
315
\begin{figure}[htbp]
316
\centering
317
\includegraphics[width=\textwidth]{population_dynamics_analysis.pdf}
318
\caption{Predator-prey dynamics analysis: (a) Classic Lotka-Volterra time series;
319
(b) Phase portrait with multiple trajectories and isoclines; (c-d) Logistic prey growth
320
model showing damped oscillations; (e) Type II functional response dynamics; (f)
321
Rosenzweig-MacArthur limit cycle; (g) Conserved quantity verification; (h) Comparison
322
of functional responses; (i) Sensitivity to prey growth rate.}
323
\label{fig:predprey}
324
\end{figure}
325
326
\section{Results}
327
328
\subsection{Model Parameters}
329
330
\begin{pycode}
331
print(r"\begin{table}[htbp]")
332
print(r"\centering")
333
print(r"\caption{Lotka-Volterra Model Parameters and Results}")
334
print(r"\begin{tabular}{lcc}")
335
print(r"\toprule")
336
print(r"Parameter & Symbol & Value \\")
337
print(r"\midrule")
338
print(f"Prey growth rate & $\\alpha$ & {alpha} \\\\")
339
print(f"Predation rate & $\\beta$ & {beta} \\\\")
340
print(f"Predator efficiency & $\\delta$ & {delta} \\\\")
341
print(f"Predator death rate & $\\gamma$ & {gamma} \\\\")
342
print(r"\midrule")
343
print(f"Equilibrium prey & $x^*$ & {x_eq:.2f} \\\\")
344
print(f"Equilibrium predator & $y^*$ & {y_eq:.2f} \\\\")
345
print(f"Oscillation period & $T$ & {period:.2f} \\\\")
346
print(r"\bottomrule")
347
print(r"\end{tabular}")
348
print(r"\label{tab:parameters}")
349
print(r"\end{table}")
350
\end{pycode}
351
352
\subsection{Stability Analysis}
353
354
\begin{pycode}
355
print(r"\begin{table}[htbp]")
356
print(r"\centering")
357
print(r"\caption{Stability Analysis at Coexistence Equilibrium}")
358
print(r"\begin{tabular}{lc}")
359
print(r"\toprule")
360
print(r"Property & Value \\")
361
print(r"\midrule")
362
print(f"Eigenvalue 1 & ${eigenvalues[0]:.4f}$ \\\\")
363
print(f"Eigenvalue 2 & ${eigenvalues[1]:.4f}$ \\\\")
364
print(f"Angular frequency & $\\omega = {np.abs(eigenvalues[0].imag):.4f}$ \\\\")
365
print(f"Theoretical period & $T = 2\\pi/\\omega = {2*np.pi/np.abs(eigenvalues[0].imag):.2f}$ \\\\")
366
print(r"Stability type & Center (neutral) \\")
367
print(r"\bottomrule")
368
print(r"\end{tabular}")
369
print(r"\label{tab:stability}")
370
print(r"\end{table}")
371
\end{pycode}
372
373
\section{Discussion}
374
375
\begin{example}[Conservation Law]
376
The classical Lotka-Volterra system conserves the quantity:
377
\begin{equation}
378
H(x, y) = \delta x - \gamma \ln x + \beta y - \alpha \ln y
379
\end{equation}
380
This integral of motion ensures closed orbits in phase space.
381
\end{example}
382
383
\begin{remark}[Structural Instability]
384
The classical model is structurally unstable: any perturbation to the equations
385
(such as adding density dependence) changes the qualitative behavior from neutral
386
cycles to either damped oscillations (stable spiral) or limit cycles.
387
\end{remark}
388
389
\begin{example}[Paradox of Enrichment]
390
In the Rosenzweig-MacArthur model, increasing carrying capacity $K$ can destabilize
391
the equilibrium. Beyond a critical $K$, the system transitions from a stable spiral
392
to a limit cycle via a Hopf bifurcation. This counterintuitive result suggests that
393
enriching an ecosystem can lead to larger oscillations and potential extinction.
394
\end{example}
395
396
\begin{remark}[Ecological Implications]
397
Predator-prey oscillations explain phenomena such as:
398
\begin{itemize}
399
\item Lynx-hare cycles in Canadian fur trade records
400
\item Phytoplankton-zooplankton dynamics in lakes
401
\item Host-parasitoid population cycles
402
\item Microbial predator-prey systems
403
\end{itemize}
404
The oscillation period depends on the geometric mean of growth and death rates.
405
\end{remark}
406
407
\section{Conclusions}
408
409
This analysis demonstrates the rich dynamics of predator-prey systems:
410
\begin{enumerate}
411
\item Classic Lotka-Volterra produces neutral oscillations with period $T \approx \py{f"{period:.1f}"}$
412
\item Equilibrium at $(x^*, y^*) = (\py{f"{x_eq:.1f}"}, \py{f"{y_eq:.1f}"}$) with eigenvalues $\lambda = \pm i\py{f"{np.abs(eigenvalues[0].imag):.3f}"}$
413
\item Adding logistic prey growth creates damped oscillations (stable spiral)
414
\item Type II functional response can generate limit cycles
415
\item The conserved quantity $H$ ensures orbit closure in the classic model
416
\end{enumerate}
417
418
\section*{Further Reading}
419
420
\begin{itemize}
421
\item Murray, J.D. \textit{Mathematical Biology I: An Introduction}, 3rd ed. Springer, 2002.
422
\item Edelstein-Keshet, L. \textit{Mathematical Models in Biology}. SIAM, 2005.
423
\item Kot, M. \textit{Elements of Mathematical Ecology}. Cambridge, 2001.
424
\end{itemize}
425
426
\end{document}
427
428