Week 11

Lotka-Volterra model of predator-prey interactions (2)

Lecture in a nutshell

(Continued from Week 10)

  • Model 2. Logistic growth of prey + Type I functional response
    1. Model equations:
      \(\begin{align}\frac {dN}{dt} = rN(1-\frac{N}{K})-aNP\end{align}\\\)
      \(\begin{align}\frac {dP}{dt} = eaNP-dP\end{align}\)
    2. The equilibrium points \((N^{*}, P^{*})\):
      • \(E_{0} = (0, 0)\)
      • \(E_{N} = (K, 0)\)
      • \(E_{NP} = (\frac{d}{ea}, \frac{r}{a}(1-\frac{d}{eak}))\)
    3. Local stability analysis:
      1. \(E_{0}\)
        • \(J_{E_{0}} = \begin{vmatrix}r & 0 \\ 0 & -d\end{vmatrix}\)
        • \(\lambda's = r\) and \(-d\) (one positive and one negative)
        • Unstable
      2. \(E_{N}\)
        • \(J_{E_{N}} = \begin{vmatrix}-r & -aK \\ 0 & eaK-d\end{vmatrix}\)
        • \(\lambda's = -r\) and \(eaK-d\)
        • Stable if \(eaK < d~(K < \frac{d}{ea})\)
      3. \(E_{NP}\)
        • \(J_{E_{NP}} = \begin{vmatrix}-\frac{rd}{eaK} & -\frac{d}{e} \\ eaP^{*} & 0\end{vmatrix}\)
        • Characteristic equation: \(\lambda^{2} - (\frac{rd}{eaK})\lambda + adP^{*} = 0\)
        • \(\lambda_{1} + \lambda_{2} = -\frac{rd}{eaK} < 0~\&~\lambda_{1}\lambda_{2} = adP^{*} > 0\)
        • Stable if \(P^{*} > 0\) (feasibility) and \(K > \frac{d}{ea}\)

  • Model 3. Exponential growth of prey + Type II functional response
    1. Model equations:
      \(\begin{align}\frac {dN}{dt} = rN-(\frac{aN}{1+ahN})P\end{align}\\\)
      \(\begin{align}\frac {dP}{dt} = e(\frac{aN}{1+ahN})P-dP\end{align}\)
    2. Internal equilibrium is unstable (oscillations with increasing amplitude)

  • Model 4. Logistic growth of prey + Type II functional response
    1. Model equations:
      \(\begin{align}\frac {dN}{dt} = rN(1-\frac{N}{K})-(\frac{aN}{1+ahN})P\end{align}\\\)
      \(\begin{align}\frac {dP}{dt} = e(\frac{aN}{1+ahN})P-dP\end{align}\)
    2. The equilibrium points \((N^{*}, P^{*})\):
      • \(E_{0} = (0, 0)\)
      • \(E_{N} = (K, 0)\)
      • \(E_{NP} = (\frac{d}{a(e-dh)}, \frac{r}{a}(1-\frac{d}{a(e-dh)k})(1+ah\frac{d}{a(e-dh)}))\)
    3. Local stability analysis:
      1. \(E_{0}\)
        • \(J_{E_{0}} = \begin{vmatrix}r & 0 \\ 0 & -d\end{vmatrix}\)
        • \(\lambda's = r\) and \(-d\)
        • Unstable
      2. \(E_{N}\)
        • \(J_{E_{N}} = \begin{vmatrix}-r & \frac{-aK}{1+ahK} \\ 0 & \frac{eaK}{1+ahK}-d\end{vmatrix}\)
        • \(\lambda's = -r\) and \(\frac{eaK}{1+ahK}-d\)
        • Stable if \((K < \frac{d}{a(e-dh)})\)
      3. \(E_{NP}\)
        • \(J_{E_{NP}} = \begin{vmatrix}N^{*}(\frac{-r}{K}+\frac{a^{2}hP^{*}}{(1+ahN^{*})^{2}}) & -\frac{aN^{*}}{1+ahN^{*}} \\ \frac{eaP^{*}}{(1+ahN^{*})^{2}} & 0\end{vmatrix}\)
        • Characteristic equation: \(\lambda^{2} - N^{*}(\frac{-r}{K}+\frac{a^{2}hP^{*}}{(1+ahN^{*})^{2}})\lambda + \frac{ea^{2}N^{*}P^{*}}{(1+ahN^{*})^{3}} = 0\)
        • If feasible (\(N^{*}, P^{*} > 0\)), \(E_{NP}\) is stable if \(\frac{-r}{K}+\frac{a^{2}hP^{*}}{(1+ahN^{*})^{2}} < 0\) (or \(N^{*} > \frac{-1+ahk}{2ah}\))
        • Paradox of enrichment: increasing the carrying capacity K of prey will destabilize the system
        • Poincaré–Bendixson theorem: the trajectory must exhibit sustained periodic motion in a bounded 2D state-space if there is no equilibrium within.


Lab demonstration

Similar to what we’ve done in the previous class, in this lab we are going to analyze the Rosenzweig–MacArthur predator–prey model:

\(\begin{align}\frac {dN}{dt} = rN(1-\frac{N}{K})-a\frac{N}{1+ahN}P\end{align}\\\)
\(\begin{align}\frac {dP}{dt} = ea\frac{N}{1+ahN}P-dP\end{align}\)

Please simulate the model using the parameter set (N = 5, P = 2, r = 1.0, K = 5.0, a = 1.3, h = 0.9, e = 0.6, d = 0.5) and plot the population trajectories of predator and prey as well as show their population dynamics in the state-space diagram.

What will happen if you add a perturbation to the system (i.e., change the initial conditions)? Try out different values of N and P and visualize the differences in the state-space diagram. Also compare the results of the Rosenzweig–MacArthur model and the original Lotka-Volterra model. What do you find regarding the final equilibrium cycles?

library(tidyverse)
library(deSolve)

### Model specification
RM_predation_model <- function(times, state, parms) {
  with(as.list(c(state, parms)), {
    dN_dt = r*N*(1-(N/K))-(a*N/(1+a*h*N))*P
    dP_dt = e*(a*N/(1+a*h*N))*P-d*P
    return(list(c(dN_dt, dP_dt)))  
  })
}

### Model parameters
times <- seq(0, 200, by = 0.01)  
state <- c(N = 5, P = 2)  
parms <- c(r = 1.0, K = 5.0, a = 1.3, h = 0.9, e = 0.6, d = 0.5) 

### Model application
pop_size <- ode(func = RM_predation_model, times = times, y = state, parms = parms)

### Visualize the population dynamics
# (1) population trajectories
pop_size %>%
  as.data.frame() %>%
  pivot_longer(cols = -time, names_to = "species", values_to = "N") %>%
  ggplot(aes(x = time, y = N, color = species)) + 
  geom_line(size = 1.5) +
  theme_classic(base_size = 12) +
  labs(x = "Time", y = "Population size") +
  scale_x_continuous(limits = c(0, 200.5), expand = c(0, 0)) +
  scale_y_continuous(limits = c(0, max(pop_size[, -1])*1.2), expand = c(0, 0)) +
  scale_color_brewer(name = NULL, palette = "Set1", labels = c("Prey", "Predator"), direction = -1)

# (2) state-space diagram
pop_size %>%
  as.data.frame() %>%
  ggplot(aes(x = N, y = P)) + 
  geom_path() + 
  geom_vline(xintercept = with(as.list(parms), d/(e*a-a*d*h)), color = "#E41A1C", size = 1) +
  geom_function(data = data.frame(x = seq(0, 5, 0.01)), aes(x = x), fun = function(n){with(as.list(parms), r*(1+a*h*n)*(1-n/K)/a)}, inherit.aes = F, color = "#377EB8", size = 1) + 
  geom_point(aes(x = tail(N, 1), y = tail(P, 1)), size = 2) +
  theme_classic(base_size = 14) +
  theme(axis.line.x = element_line(color = "#E41A1C", size = 1),
        axis.line.y = element_line(color = "#377EB8", size = 1)) + 
  labs(x = "Prey", y = "Predator") + 
  scale_x_continuous(limits = c(0, max(pop_size[, "N"]*1.05)), expand = c(0, 0)) + 
  scale_y_continuous(limits = c(0, max(pop_size[, "P"]*1.05)), expand = c(0, 0)) 


Special topic: time-scale separation

Time-scale separation is a useful technique to reduce the dimension of the model system, where some state variables are assumed to operate at a much shorter time scale (i.e., fast variables) compared with the others (i.e., slow variables). The fast variables will adjust rapidly to their new equilibria in response to a slight change in the slow variables, such that the slow variables can be viewed as “constants” from the perspective of fast variables.

Here, we are going to use the Lotka-Volterra model with logistic prey growth to demonstrate time-scale separation:

\(\begin{align}\frac {dN}{dt} = rN(1-\frac{N}{K})-aNP\end{align}\\\)
\(\begin{align}\frac {dP}{dt} = eaNP-dP\end{align}\)

We will treat prey as a fast variable and predator as a slow variable. First, we find the quasi-equilibrium \(N^{\wedge}\) of prey by setting the prey equation to zero:

\(\begin{align}N^{\wedge} = K(1-\frac{aP}{r}) \end{align}\)

And then we plug \(N^{\wedge}\) into predator equation:

\(\begin{align}\frac {dP}{dt} = (eaK-d)P(1-\frac{eaK(\frac{a}{r})}{eaK-d}P)\end{align}\)

What do you see in the above equation? It is actually a logistic growth model! So under time-scale separation, predator will exhibit logistic population growth. We will show this shortly in the code below.

One simple way to achieve time-scale separation in this model is to increase the prey growth rate so that prey will grow much faster than predator, mimicking the shorter generation time of prey relative to that of predator.

library(tidyverse)
library(deSolve)

Prey_logistic_model <- function(times, state, parms) {
  with(as.list(c(state, parms)), {
    dN_dt = r*N*(1-(N/K))-a*N*P
    dP_dt = e*a*N*P-d*P
    return(list(c(dN_dt, dP_dt)))  
  })
}

times <- seq(0, 100, by = 0.01)  
state <- c(N = 40, P = 20)  
parms <- c(r = 40.0, K = 60, a = 0.1, e = 0.1, d = 0.5)  # r is chosen to be sufficiently large for time-scale separation

pop_size <- ode(func = Prey_logistic_model, times = times, y = state, parms = parms)

# population trajectories
pop_size %>%
  as.data.frame() %>%
  pivot_longer(cols = -time, names_to = "species", values_to = "N") %>%
  ggplot(aes(x = time, y = N, color = species)) + 
  geom_line(size = 1.5) +
  theme_classic(base_size = 12) +
  labs(x = "Time", y = "Population size") +
  scale_x_continuous(limits = c(0, 100.5), expand = c(0, 0)) +
  scale_y_continuous(limits = c(0, max(pop_size[, -1])*1.2), expand = c(0, 0)) +
  scale_color_brewer(name = NULL, palette = "Set1", labels = c("Prey", "Predator"), direction = -1)

# state-space diagram
pop_size %>%
  as.data.frame() %>%
  ggplot(aes(x = N, y = P)) + 
  geom_point(color = "grey60", size = 3, shape = 21) + 
  geom_vline(xintercept = with(as.list(parms), d/(e*a)), color = "#E41A1C", size = 1) +
  geom_abline(slope = with(as.list(parms), -r/(a*K)), 
              intercept = with(as.list(parms), r/a),
              color = "#377EB8", size = 1) +
  geom_point(aes(x = with(as.list(parms), d/(e*a)),
                 y = with(as.list(parms), -r/(a*K)*d/(e*a) + r/a)),
             size = 4) + 
  theme_classic(base_size = 14) +
  theme(axis.line.x = element_line(color = "#E41A1C", size = 1),
        axis.line.y = element_line(color = "#377EB8", size = 1)) +           labs(x = "Prey", y = "Predator") + 
  scale_y_continuous(limits = c(NA, 100))

If we solve for the new predator growth equation derived under time-scale separation using the same parameter set and visualize the population trajectory, we will get almost exactly the same predator growth pattern as that in the original model.

Time_scale_logistic <- function(times, state, parms) {
  with(as.list(c(state, parms)), {
    dP_dt = (e*a*K-d)*P*(1-((e*a*K*(a/r))/(e*a*K-d))*P)
    return(list(c(dP_dt)))  
  })
}

times <- seq(0, 100, by = 0.1)
state <- c(N = 40, P = 20) 
state_timescale <- c(P = 20)  
parms <- c(r = 40.0, K = 60, a = 0.1, e = 0.1, d = 0.5)  # r is chosen to be sufficiently large for time-scale separation

pop_size_original <- ode(func = Prey_logistic_model, times = times, y = state, parms = parms)
pop_size_timescale <- ode(func = Time_scale_logistic, times = times, y = state_timescale, parms = parms)

# plot the two predator population trajectories in the same figure
pop_size_timescale %>%
  as.data.frame() %>%
  mutate(P_original = pop_size_original[, "P"]) %>%
  rename(P_timescale = P) %>%
  pivot_longer(cols = -time, names_to = "model", values_to = "n") %>%
  ggplot(aes(x = time, y = n, color = model)) + 
  geom_point(size = 2.5, alpha = 0.8, shape = 21) +
  theme_classic(base_size = 14) +
  labs(x = "Time", y = "Population size") +
  scale_x_continuous(limits = c(0, 100.5), expand = c(0, 0)) +
  scale_y_continuous(limits = c(0, max(pop_size_timescale[, -1])*1.2), expand = c(0, 0)) +
  scale_color_brewer(name = NULL, palette = "Set2", labels = c("Original", "Time-scale"))


When you gradually shift r from small to large values, you can see that the population dynamics of predator and prey change quite dramatically. Some interesting patterns will arise!

Additional readings

No additional readings this week.

Assignments

No assignments this week.