Tutorial 2: Solving for equilibria
Let's get more familiar with solving for equilibria using a model of population growth.
Logistic growth
Our model for exponential growth in discrete time was \(n(t+1) = R n(t)\). When we plotted this over time for \(R>1\) we saw the population size increase quickly without bound. This is obviously quite unrealistic, eventually competition between individuals will slow and prevent population growth. To model this we make the reproductive factor \(R\) a function of the current population size \(n(t)\) and, in the simplest case, assume \(R\) declines linearly with \(n(t)\),
We call \(r\) the intrinsic growth rate (ie, growth rate when rare) and \(K\) the carrying capacity.
Below we plot the reproductive factor as a function of \(n(t)\) for a few different values of \(r\) and \(K\).
import matplotlib.pyplot as plt
import numpy as np
# Reproductive factor for logistic growth
def logistic_discrete(nt, r, K):
'''reproductive factor in discrete logistic model with growth rate r and carrying capacity k'''
return 1 + r * (1 - nt/K)
# Compare a few different growth rates and carrying capacities
fig, ax = plt.subplots()
for r, K in zip([1, 2, 1], [100, 100, 50]): #for each pair of r and K values
nt = np.linspace(0, 200) #for a range of population sizes from 0 to 200
R = logistic_discrete(nt, r, K) #calculate the reproductive factor
ax.plot(nt, R, label=f"r = {r}, K = {K}") #and plot
ax.plot(nt, [1 for i in nt], '--', color='gray') #1 line for reference
ax.set_xlabel('Population size, $n(t)$')
ax.set_ylabel('Reproductive factor, $R$')
ax.legend(frameon=False)
plt.ylim(0,None)
plt.show()

Our recursion equation is now
This is the recursion equation for logistic growth.
This recursion is a non-linear function of \(n(t)\) (non-linear means that there is a term in the equation where the term is taken to some power other than 1; here if we expand out the recursion we get a \(n(t)^2\) term). This reflects the fact that logistic growth models an interaction between individuals (competition).
To get a better sense of the model let's make a cob-web plot for \(K=1000\) and \(r=0.5\):
from sympy import *
import numpy as np
import matplotlib.pyplot as plt
r, K = 0.5, 1000 #choose parameter values
n = symbols('n') #define our variable n(t)
# Write out sympy equation
f = n*(1 + r*(1 - n/K)) #the recursion equation
# Compute function over a set of points in [0,2K] by 'lambdifying' sympy equation
x = np.linspace(0,2*K,100)
fy = lambdify(n, f)(x)
# Build function for generating figure
def plot_n(x,fy):
fig, ax = plt.subplots()
ax.plot(x, fy, color='black') #n_{t+1} as function of n_t
ax.plot(x, x, color='black', linestyle='--') #1:1 line for reference
ax.set_xlim(0,2*K)
ax.set_ylim(0,2*K)
ax.set_xlabel("population size at $t$, $n_t$")
ax.set_ylabel("population size at $t+1$, $n_{t+1}$")
return ax
# Plot figure
ax = plot_n(x,fy)
# make generator
def nt(n0, r, K, max=oo):
t, nnow, nnext = 0, n0, 0 #initial conditions
while t < max:
yield nnow, nnext #current value of n(t) and n(t+1)
nnext = nnow*(1 + r*(1 - nnow/K)) #update n(t+1)
yield nnow, nnext #current value of n(t) and n(t+1)
nnow = nnext #update n(t)
t += 1 #update t
# Initialize generator
nts = nt(n0=100, r=r, K=K, max=10)
# Compute x,y pairs by iterating through generator
ns = np.array([[x,y] for x,y in nts])
# Plot 'cobwebs'
ax.plot(ns[:,0], ns[:,1])
ax.set_title('r=%s'%r)
plt.show()

We see population size move towards an internal equilibrium. Things are not so straightforward when we increase \(r\)!
r,K = 2.7,1000 #choose parameter values
n = symbols('n') #define our variable n(t)
# Write out sympy equation
f = n*(1 + r*(1 - n/K)) #the recursion equation
# Compute function over a set of points in [0,2K] by 'lambdifying' sympy equation
x = np.linspace(0,2*K,100)
fy = lambdify(n, f)(x)
# Plot figure
ax = plot_n(x,fy)
# Initialize generator
nts = nt(n0=100, r=r, K=K, max=10)
# Compute x,y pairs by iterating through generator
ns = np.array([[x,y] for x,y in nts])
# Plot 'cobwebs'
ax.plot(ns[:,0], ns[:,1],)
plt.show()

Problem
Solve for the equilibria, \(\hat n\). Check this is consistent with the plots above.