logo

Malthus Growth Model: Ideal Population Growth 📂Dynamics

Malthus Growth Model: Ideal Population Growth

Model

01_malthusian_growth_integration.gif

$$ \dot{N} = rN $$

Variables

  • $N(t)$: Represents the population size of a group at time $t$.

Parameters

  • $r \in \mathbb{R}$ : The Intrinsic Rate of Increase, if greater than $0$, the population grows, if less than $0$, it declines. It can also be defined by the difference $r:=b-d$ between Birth Rate $b$ and Death Rate $d$.

Description

Population Dynamics is the first pathway through which dynamics leads to mathematical biology, offering a mathematical approach to topics like population size or species coexistence. The Malthus growth model is one of the simplest of such models, where the population size of a species, breeding ideally (without obstacles), can be represented by a simple ordinary differential equation. The given equation is a separable first-order differential equation, hence its solution for the initial population $N_{0}$

$$ N(t) = N_{0} e^{rt} $$

is obtained like this, and since the expression involves an exponential function, it is perfectly described as Exponential Growth Model. The name Malthus Growth Model is derived from Thomas Robert Malthus, the author of An Essay on the Principle of Population.

One may wonder what use is a population model, but more advanced models are actually used in the biotech field, relating to the number of germs, viruses, or certain nucleotide sequences, and many application models are based on the approach to ‘moving quantitative variables’ themselves beyond the meaning of population size. For instance, Gordon Moore, co-founder of Intel, stated that “the performance of semiconductor integrated circuits doubles every two years,” which exactly means exponential growth, now called Moore’s Law. It’s not that Moore applied the Malthus growth model directly, but rather that the phenomenon of growth is generally explained this way. Besides the bio-field, this concept of growth can apply to customers using a service or the principal sum of risk-free assets in economics/management, or conversely, nuclear decomposition can be viewed as the negative growth of the number of nuclei.

Derivation

Modeling the growth of a population can be imagined by considering bacteria that reproduce by binary fission. The rate of increase in population size is proportional not only to their individual growth rates but also to the total population size itself. For example, consider a Petri dish $A$ with $10$ bacteria and another dish $B$ with $20$ bacteria. After a certain period, when the population size has doubled, dish $A$ would have $20$ bacteria, and dish $B$ would have $40$. In other words,

$$ (N = 10 \text{일 때의 증가량}) = 10 \\ (N = 20 \text{일 때의 증가량}) = 20 $$

Assuming this holds using the variable $N$,

$$ (N \text{의 증가량}) = N $$

Modifying the equation to consider growth rate, for some real number $r \in \mathbb{R}$,

$$ (N \text{의 변화량}) = rN $$

is obtained. If $r>0$, the change in population is positive, indicating an increase in population size, and if $r<0$, the change is negative, indicating a decrease over time. To express this change not verbally but mathematically, differentiation is needed. At time $t$, the rate of change of $N$ is $dN / dt$, modifying the left-hand side yields:

$$ {{dN} \over {dt}} = r N $$

Limitations

While the term differential equations might seem daunting, one can appreciate that each step of derivation stems from a common-sense premise. However, despite the seemingly rational premise, the biggest issue with this model is its total failure to reflect reality. Though it might be appropriate as the first textbook example due to its simplicity, it’s practically useless for any substantial application.

Actual population growth inevitably encounters what is known as the Malthusian Trap. Continuing with the Petri dish example, the dish, being a constrained environment, can neither forever sustain growth and reproduction with the provided nutrients nor offer infinite space. Eventually, a point will be reached where growth slows down, and ideal exponential growth stops.

While this model is the simplest, it’s rarely used to fit actual population sizes directly; it either holds meaning as a first-order term itself or various models are applied to overcome its unrealistic aspects. Methods considering death or competition between multiple species could be contemplated.

Visual Understanding

Let’s visually comprehend the Malthus growth model through an agent-based simulation.

Actions

(Every agent, on each turn)

  • Reproduce: With probability b, creates a new agent at its location.
  • Die: With probability d, is removed from the simulation.
b # 번식률
d # 사망률
replicated = (rand(N) .< b) # 번식 판정
new\_coordinate = coordinate[replicated,:]
coordinate = coordinate[rand(N) .> d,:] # 사망 판정
coordinate = cat(coordinate, new\_coordinate, dims = 1);

Checking how the system changes with the system-specific intrinsic growth rate parameter $r$ suffices, given the simple actions of reproduction and death used by agents.

Note that the reproduction probability b used in the simulation and the system’s birth rate $b$, along with the death probability d and the system’s death rate $d$, are generally not the same. Fitting the simulation exactly to an autonomous system described by differential equations required finding reasonably appropriate parameters through intuition.

In the case of $r>0$

N0 = 50 # 초기 인구수
b = 0.05 # 번식률
d = 0.02 # 사망률

malthusian_growth_integration1.gif

Although there might be some initial hesitation in simulation, since the birth rate surpasses the death rate, explosive growth ultimately occurs.

In the case of $r<0$

N0 = 50 # 초기 인구수
b = 0.04 # 번식률
d = 0.05 # 사망률

malthusian_growth_integration2.gif

It almost precisely follows the theoretical path to extinction.

In the case of $r=0$

N0 = 50 # 초기 인구수
b = 0.05 # 번식률
d = 0.05 # 사망률

malthusian_growth_integration3.gif

While the differential equation representation suggests no change with an initial value at the fixed point, the simulation shows fluctuations due to moment-to-moment luck, occasionally nearing extinction before recovering. This motion seems like a kind of Brownian motion, and indeed, increasing the population or lowering the birth and death rates would maintain a more stable equilibrium state.

Code

Below is the Julia code used in this post.

cd(@__DIR__) # 파일 저장 경로

@time using Plots
@time using Random
@time using Distributions
@time using LinearAlgebra
@time using DifferentialEquations

#---

function malthusian_growth!(du,u,p,t)
  N = u[1]
  r = p
  du[1] = dN = r*N
end

u0 = [50.0]
p = 2.65

tspan = (0.,1.8)
prob = ODEProblem(malthusian_growth!,u0,tspan,p)
sol = solve(prob; saveat=(0.0:0.1:1.8))

compare = plot(sol,vars=(0,1),
  linestyle = :dash,  color = :black,
  size = (400,300), label = "Theoretical", legend=:topleft)

#---

N0 = 50 # 초기 인구수
b = 0.05 # 번식률
d = 0.02 # 사망률
max_iteration = 180 # 시뮬레이션 기간
gaussian2 = MvNormal([0.0; 0.0], 0.03I) # 2차원 정규분포

Random.seed!(0)
time_evolution = [] # 인구수를 기록하기 위한
let
  coordinate = rand(gaussian2, N0)'
  N = N0

  anim = @animate for t = (0:max_iteration)/100
    row2 = @layout [a{0.6h}; b]
    figure = plot(size = [300,500], layout = row2)

    plot!(figure[1], coordinate[:,1], coordinate[:,2], Seriestype = :scatter,
      markercolor = RGB(1.,94/255,0.), markeralpha = 0.4, markerstrokewidth	= 0.1,
      aspect_ratio = 1, title = "t = $t",
      xaxis=true,yaxis=true,axis=nothing, legend = false)
      xlims!(figure[1], -10.,10.)
      ylims!(figure[1], -10.,10.)

    replicated = (rand(N) .< b) # 번식 판정
    new_coordinate = coordinate[replicated,:]
    coordinate = coordinate[rand(N) .> d,:] # 사망 판정
    coordinate = cat(coordinate, new_coordinate, dims = 1);

    N = size(coordinate, 1)
    push!(time_evolution, N)
    coordinate = coordinate + rand(gaussian2, N)'

    if t < 0.9
      plot!(figure[2], sol,vars=(0,1),
        linestyle = :dash,  color = :black,
        label = "Theoretical", legend=:bottomright)
    else
      plot!(figure[2], sol,vars=(0,1),
        linestyle = :dash,  color = :black,
        label = "Theoretical", legend=:topleft)
    end
    plot!(figure[2], 0.0:0.01:t, Time_evolution,
      color = RGB(1.,94/255,0.), linewidth = 2, label = "Simulation",
      yscale = :log10, yticks = 10 .^(1:4))
    ylims!(figure[2], 0.,min(time_evolution[end]*2,10000.))
  end
  gif(anim, "malthusian_growth_integration1.gif", fps = 18)
end

#---

function malthusian_growth!(du,u,p,t)
  N = u[1]
  r = p
  du[1] = dN = r*N
end

u0 = [50.0]
p = -1

tspan = (0.,1.8)
prob = ODEProblem(malthusian_growth!,u0,tspan,p)
sol = solve(prob; saveat=(0.0:0.1:1.8))

compare = plot(sol,vars=(0,1),
  linestyle = :dash,  color = :black,
  size = (400,300), label = "Theoretical", legend=:topleft)


#---

N0 = 50 # 초기 인구수
b = 0.04 # 번식률
d = 0.05 # 사망률
max_iteration = 180 # 시뮬레이션 기간
gaussian2 = MvNormal([0.0; 0.0], 0.03I) # 2차원 정규분포

Random.seed!(0)
time_evolution = [] # 인구수를 기록하기 위한
let
  coordinate = rand(gaussian2, N0)'
  N = N0

  anim = @animate for t = (0:max_iteration)/100
    row2 = @layout [a{0.6h}; b]
    figure = plot(size = [300,500], layout = row2)

    plot!(figure[1], coordinate[:,1], coordinate[:,2], Seriestype = :scatter,
      markercolor = RGB(1.,94/255,0.), markeralpha = 0.4, markerstrokewidth	= 0.1,
      aspect_ratio = 1, title = "t = $t",
      xaxis=true,yaxis=true,axis=nothing, legend = false)
      xlims!(figure[1], -10.,10.)
      ylims!(figure[1], -10.,10.)

    replicated = (rand(N) .< b) # 번식 판정
    new_coordinate = coordinate[replicated,:]
    coordinate = coordinate[rand(N) .> d,:] # 사망 판정
    coordinate = cat(coordinate, new_coordinate, dims = 1);

    N = size(coordinate, 1)
    push!(time_evolution, N)
    coordinate = coordinate + rand(gaussian2, N)'


    plot!(figure[2], sol,vars=(0,1),
      linestyle = :dash,  color = :black,
      label = "Theoretical", legend=:topright)
    plot!(figure[2], 0.0:0.01:t, Time_evolution,
      color = RGB(1.,94/255,0.), linewidth = 2, label = "Simulation",
      yscale = :log10, yticks = 10 .^(1:4))
    ylims!(figure[2], 0., 50.)
  end
  gif(anim, "malthusian_growth_integration2.gif", fps = 18)
end


#---

function malthusian_growth!(du,u,p,t)
  N = u[1]
  r = p
  du[1] = dN = r*N
end

u0 = [50.0]
p = 0

tspan = (0.,3.)
prob = ODEProblem(malthusian_growth!,u0,tspan,p)
sol = solve(prob; saveat=(0.0:0.1:3.0))

compare = plot(sol,vars=(0,1),
  linestyle = :dash,  color = :black,
  size = (400,300), label = "Theoretical", legend=:topleft)


#---

N0 = 50 # 초기 인구수
b = 0.05 # 번식률
d = 0.05 # 사망률
max_iteration = 300 # 시뮬레이션 기간
gaussian2 = MvNormal([0.0; 0.0], 0.03I) # 2차원 정규분포

Random.seed!(0)
time_evolution = [] # 인구수를 기록하기 위한
let
  coordinate = rand(gaussian2, N0)'
  N = N0

  anim = @animate for t = (0:max_iteration)/100
    row2 = @layout [a{0.6h}; b]
    figure = plot(size = [300,500], layout = row2)

    plot!(figure[1], coordinate[:,1], coordinate[:,2], Seriestype = :scatter,
      markercolor = RGB(1.,94/255,0.), markeralpha = 0.4, markerstrokewidth	= 0.1,
      aspect_ratio = 1, title = "t = $t",
      xaxis=true,yaxis=true,axis=nothing, legend = false)
      xlims!(figure[1], -10.,10.)
      ylims!(figure[1], -10.,10.)

    replicated = (rand(N) .< b) # 번식 판정
    new_coordinate = coordinate[replicated,:]
    coordinate = coordinate[rand(N) .> d,:] # 사망 판정
    coordinate = cat(coordinate, new_coordinate, dims = 1);

    N = size(coordinate, 1)
    push!(time_evolution, N)
    coordinate = coordinate + rand(gaussian2, N)'


    plot!(figure[2], sol,vars=(0,1),
      linestyle = :dash,  color = :black,
      label = "Theoretical", legend=:topleft)
    plot!(figure[2], 0.0:0.01:t, Time_evolution,
      color = RGB(1.,94/255,0.), linewidth = 2, label = "Simulation",
      yscale = :log10, yticks = 10 .^(1:4))
    ylims!(figure[2], 0., 100.)
  end
  gif(anim, "malthusian_growth_integration3.gif", fps = 18)
end