Agent-based Model Simulation of Mortality
Simulation
This post aims to simulate the degrowth of a population from a macroscopic perspective by implementing an action in which generated agents die. Everything related to space or movement in this simulation is merely for visualization and has no actual purpose.
Variables
- $t$: Represents the current turn.
- $N(t)$: Represents the number of agents at turn $t$.
Parameters
- $N_{0} \in \mathbb{N}$: Represents the number of agents at the start of the simulation.
- $d \in [0,1]$: Represents the probability of an agent dying, referred to as the death rate.
- $t_{\text{end}} \in \mathbb{N}$: Determines the turn at which the simulation ends.
Actions
(For all agents, every turn)
- Death: Excluded from the simulation with a probability of
d
. - Movement: Moves according to a vector drawn from a bivariate normal distribution $N \left( \mathbb{0} , \Sigma \right)$.
Code Review
Step 1. Initialization and Agent Creation
julia> N0 = 500 # 초기 인구수
500
julia> d = 0.04 # 사망률
0.04
julia> max\_iteration = 180 # 시뮬레이션 기간
180
julia> gaussian2 = MvNormal([0.0; 0.0], 0.04I) # 2차원 정규분포
IsoNormal(
dim: 2
μ: [0.0, 0.0]
Σ: [0.04 0.0; 0.0 0.04]
)
julia> Random.seed!(0);
julia> Time\_evolution = [] # 인구수를 기록하기 위한 스택
Any[]
Similar to the Agent-Based Simulation Tutorial, this step involves creating agents. Here, each variable reflects the parameters set above.
N0
$\leftarrow N_{0} = 5$d
$\leftarrow d = 0.04$max_iteration
$\leftarrow t_{\text{end}} = 180$
Random.seed!(0)
is set to reproduce results, and time_evolution
is a stack for recording the number of entities, which does not impact the simulation itself but is an important technique for repeating and analyzing simulations.
julia> coordinate = rand(gaussian2, N0)'
500×2 Adjoint{Float64,Array{Float64,2}}:
0.135821 0.165683
-0.0706015 -0.0269708
0.117323 0.0594672
0.0129895 -0.0218035
-0.102842 0.314866
-0.137781 -0.152561
0.0794965 0.162326
-0.0692709 -0.0375145
⋮
-0.22697 0.178074
0.036792 -0.35028
-0.253518 0.418463
-0.226243 -0.111246
0.120505 -0.0111552
0.0882067 0.0526977
0.398744 -0.114504
-0.215111 0.259829
julia> N = N0
500
Since $N$ represents the number of entities, initially, it must be provided as in $N \gets N_{0}$.
Step 2. Agent Death
julia> coordinate = coordinate[(rand(N) .> d),:]
481×2 Array{Float64,2}:
0.135821 0.165683
-0.0706015 -0.0269708
0.117323 0.0594672
0.0129895 -0.0218035
-0.102842 0.314866
0.0794965 0.162326
-0.0692709 -0.0375145
⋮
-0.253518 0.418463
-0.226243 -0.111246
0.120505 -0.0111552
0.0882067 0.0526977
0.398744 -0.114504
-0.215111 0.259829
For the current number of agents, random numbers between 0 and 1 are drawn. If this is less than d
, it means that death has occurred with a probability of d
. In the screenshot above, 19 agents died, leaving 481 agents. This shows that about 4% of the original 500 agents died, indicating that our parameter settings have been almost accurately reflected.
Step 3. Recording and Agent Movement
julia> push!(time\_evolution, N)
1-element Array{Any,1}:
481
julia> coordinate = coordinate + rand(gaussian2, N)'
481×2 Array{Float64,2}:
0.399141 0.492689
-0.151015 -0.00161691
-0.00843121 0.10119
0.111592 0.00865223
0.0591775 0.150437
0.0060825 0.0383255
-0.0652535 -0.0688566
-0.702124 -0.485473
⋮
0.437486 -0.354416
-0.149827 0.156366
-0.0737965 -0.168831
0.344358 -0.136392
-0.231089 -0.0695535
0.446688 0.143493
0.133667 0.156873
Since 19 agents were reduced in the Step 2., $N$ was also updated, and the number of entities at that time was recorded. Agents’ coordinates are represented as matrices, so adding a $N \times 2$ matrix, which is stacked by drawing $N$ times from a bivariate normal distribution, implements agent movement. After completing this, it returns to Step 2. Repeat this process until reaching $t = t_{\text{end}}$, and then stop.
By just repeating the above processes, one can observe the phenomenon similar to the gif at the top of the post.
Full Code
Below is the Julia code used in this post.
cd(@__DIR__) # 파일 저장 경로
@time using Plots
@time using Random
@time using Distributions
@time using LinearAlgebra
N0 = 500 # 초기 인구수
d = 0.04 # 사망률
max_iteration = 180 # 시뮬레이션 기간
gaussian2 = MvNormal([0.0; 0.0], 0.04I) # 2차원 정규분포
Random.seed!(0);
time_evolution = [] # 인구수를 기록하기 위한 스택
let
coordinate = rand(gaussian2, N0)'
N = N0
anim = @animate for t = (0:max_iteration)/100
plot(coordinate[:,1], coordinate[:,2], Seriestype = :scatter,
markercolor = RGB(1.,94/255,0.), markeralpha = 0.4, markerstrokewidth = 0.1,
title = "t = $t", aspect_ratio = 1, size = [400,400],
xaxis=true,yaxis=true,axis=nothing, legend = false)
xlims!(-10.,10.)
ylims!(-10.,10.)
coordinate = coordinate[(rand(N) .> d),:]
N = size(coordinate, 1)
push!(time_evolution, N)
coordinate = coordinate + rand(gaussian2, N)'
end
gif(anim, "malthusian_growth_simulation2.gif", fps = 18)
end