Schelling¶
- n x n grid에서 agent들의 행복하게 느끼는 지수(min_to_be_happy)에 따라 어떻게 무리 짓는지를 시뮬레이션
- agent들은 주변에 같은 그룹에 속한 agent들이 있을 때는 그 자리에 머물고 없어면 같은 그룹에 속한 agent들이 있는 곳으로 이동한다.
이 모델은 인종별, 성향별, 성별등의 무리 짓기등의 시뮬레이션에 사용된다.
동작 환경¶
- Julia : v1.7.2
- Agents : v5.5.0
- InteractiveDynamics : v0.21.11
- GLMakie : v0.6.13
- CairoMakie : v0.8.13
- GLMakie를 사용하고 싶은 경우 설정에 대해서는 https://julialang.kr/?p=3684 를 참조
In [1]:
using Agents
using Random
In [2]:
@agent SchellingAgent GridAgent{2} begin
mood::Bool
group::Int
end
In [3]:
for (name, type) in zip(fieldnames(SchellingAgent),fieldtypes(SchellingAgent))
println(name,"::",type)
end
id::Int64 pos::Tuple{Int64, Int64} mood::Bool group::Int64
In [4]:
function initialize(;numagents = 320, griddims = (20,20), min_to_be_happy = 3, seed = 125)
space = GridSpaceSingle(griddims, periodic = false)
properties = Dict(:min_to_be_happy => min_to_be_happy)
rng = Random.MersenneTwister(seed)
model = ABM(
SchellingAgent,space;
properties, rng, scheduler = Schedulers.Randomly()
)
for n in 1:numagents
agent = SchellingAgent(n,(1,1),false,n < numagents / 2 ? 1 : 2 )
add_agent_single!(agent,model)
end
return model
end
Out[4]:
initialize (generic function with 1 method)
In [5]:
function agent_step!(agent, model)
minhappy = model.min_to_be_happy
count_neighbors_same_group = 0
for neighbor in nearby_agents(agent, model)
if agent.group == neighbor.group
count_neighbors_same_group += 1
end
end
if count_neighbors_same_group ≥ minhappy
agent.mood = true
else
agent.mood = false
move_agent_single!(agent, model)
end
return
end
Out[5]:
agent_step! (generic function with 1 method)
In [6]:
# ENV["DISPLAY"]="localhost:10.0"
using InteractiveDynamics
using CairoMakie
CairoMakie.activate!()
# using GLMakie
# GLMakie.activate!()
In [7]:
model = initialize(; numagents = 300)
Out[7]:
AgentBasedModel with 300 agents of type SchellingAgent space: GridSpaceSingle with size (20, 20), metric=chebyshev, periodic=false scheduler: Agents.Schedulers.Randomly properties: min_to_be_happy
In [8]:
groupcolor(a) = a.group == 1 ? :blue : :orange
groupmarker(a) = a.group == 1 ? :circle : :rect
plotkwargs =(;ac = groupcolor, am = groupmarker, as = 10)
Out[8]:
(ac = groupcolor, am = groupmarker, as = 10)
Plot¶
In [9]:
fig, ax, abmobs= abmplot(model;
agent_step! = agent_step!, model_step! = dummystep,
plotkwargs...)
fig
Out[9]:
In [10]:
let
ENV["DISPLAY"]="localhost:10.0"
using GLMakie
GLMakie.activate!()
fig, ax, abmobs= abmplot(model;
agent_step! = agent_step!, model_step! = dummystep,
plotkwargs...)
display(fig)
end
Out[10]:
GLMakie.Screen(...)
Video¶
In [11]:
model = initialize()
abmvideo(
"schelling.mp4",
model, agent_step!, dummystep;
title = "Schelling", frames = 20,
framerate = 1,
plotkwargs...
)
Collecting data¶
In [12]:
using Statistics: mean
■ agent 정보(구조체의 필드)에 대해 수집
- n=10 : 모든 agent에 대해 0 ~ 10 step까지 진행시 pos, mood, group 데이터를 수집한다.
- 수집데이터 row는 agent# x step(n>=0) = 3 x 11 (0 step 포함) = 33
- run!의 리턴은 (agent_dataframe, model_dataframe)
In [13]:
adata = [:pos, :mood, :group]
model = initialize(;numagents=3, )
agent_df, model_df = run!(model, agent_step!,10;adata)
agent_df[1:15,:]
Out[13]:
15 rows × 5 columns
step | id | pos | mood | group | |
---|---|---|---|---|---|
Int64 | Int64 | Tuple… | Bool | Int64 | |
1 | 0 | 1 | (14, 19) | 0 | 1 |
2 | 0 | 2 | (14, 10) | 0 | 2 |
3 | 0 | 3 | (7, 11) | 0 | 2 |
4 | 1 | 1 | (6, 3) | 0 | 1 |
5 | 1 | 2 | (1, 16) | 0 | 2 |
6 | 1 | 3 | (2, 13) | 0 | 2 |
7 | 2 | 1 | (14, 5) | 0 | 1 |
8 | 2 | 2 | (20, 14) | 0 | 2 |
9 | 2 | 3 | (15, 20) | 0 | 2 |
10 | 3 | 1 | (4, 14) | 0 | 1 |
11 | 3 | 2 | (15, 14) | 0 | 2 |
12 | 3 | 3 | (5, 15) | 0 | 2 |
13 | 4 | 1 | (1, 9) | 0 | 1 |
14 | 4 | 2 | (16, 11) | 0 | 2 |
15 | 4 | 3 | (8, 10) | 0 | 2 |
■ agent 구조체와 function 조합의 데이터 수집
- adata의 원소로 들어가는 function의 파라미터는 agent임
- pos : (x,y)
In [14]:
x(agent) = agent.pos[1]
adata = [x, :mood, :group]
model = initialize(;numagents=3,min_to_be_happy=2)
agent_df, model_df = run!(model, agent_step!, 10; adata)
agent_df[1:15,:]
Out[14]:
15 rows × 5 columns
step | id | x | mood | group | |
---|---|---|---|---|---|
Int64 | Int64 | Int64 | Bool | Int64 | |
1 | 0 | 1 | 14 | 0 | 1 |
2 | 0 | 2 | 14 | 0 | 2 |
3 | 0 | 3 | 7 | 0 | 2 |
4 | 1 | 1 | 6 | 0 | 1 |
5 | 1 | 2 | 1 | 0 | 2 |
6 | 1 | 3 | 2 | 0 | 2 |
7 | 2 | 1 | 14 | 0 | 1 |
8 | 2 | 2 | 20 | 0 | 2 |
9 | 2 | 3 | 15 | 0 | 2 |
10 | 3 | 1 | 4 | 0 | 1 |
11 | 3 | 2 | 15 | 0 | 2 |
12 | 3 | 3 | 5 | 0 | 2 |
13 | 4 | 1 | 1 | 0 | 1 |
14 | 4 | 2 | 16 | 0 | 2 |
15 | 4 | 3 | 8 | 0 | 2 |
■ agent 구조체와 function 조합의 데이터 수집 및 통계 데이터
In [15]:
adata = [(:mood,sum),(x, mean)]
agent_df, model_df = run!(model,agent_step!,10;adata)
agent_df
Out[15]:
11 rows × 3 columns
step | sum_mood | mean_x | |
---|---|---|---|
Int64 | Int64 | Float64 | |
1 | 0 | 0 | 13.6667 |
2 | 1 | 0 | 5.0 |
3 | 2 | 0 | 5.33333 |
4 | 3 | 0 | 8.0 |
5 | 4 | 0 | 15.0 |
6 | 5 | 0 | 10.0 |
7 | 6 | 0 | 15.3333 |
8 | 7 | 0 | 6.0 |
9 | 8 | 0 | 9.33333 |
10 | 9 | 0 | 12.6667 |
11 | 10 | 0 | 13.0 |
■ agent 구조체와 function 조합의 데이터 수집 및 통계 그래프
In [16]:
let
ENV["DISPLAY"]="localhost:10.0"
using GLMakie
GLMakie.activate!()
model = initialize()
params = Dict(:min_to_be_happy => 0:8)
alabels = ["happy","avg. x"]
fig, abmobs = abmexploration(model;
agent_step! , model_step! = dummystep, params, plotkwargs...,
adata, alabels,
)
display(fig)
end
Out[16]:
GLMakie.Screen(...)
위 코드의 결과 화면
Save / Load Model¶
1) model 생성 후 400 step 이후 안정된 model을 저장
2) 저장된 모델 로딩
3) 로딩된 모델에 100개의 agent 투입 후 40 step 진행
4) 저장된 모델 로딩
5) 로딩된 모델에 새로운 그룹 추가
In [17]:
# agent 200개가 400step후에 안정화 된것으로 기대
model = initialize(numagents=200, min_to_be_happy=4, seed=42)
run!(model, agent_step!, 400)
fig, ax, abmobs = abmplot(model; plotkwargs...)
fig
Out[17]:
In [18]:
#---
# save model : HDF5 기반 데이터 포맷으로 저장됨
#---
AgentsIO.save_checkpoint("schelling.jld2",model)
In [19]:
# model loading
model = AgentsIO.load_checkpoint("schelling.jld2"; scheduler = Schedulers.Randomly())
Out[19]:
AgentBasedModel with 200 agents of type SchellingAgent space: GridSpaceSingle with size (20, 20), metric=chebyshev, periodic=false scheduler: Agents.Schedulers.Randomly properties: min_to_be_happy
In [20]:
# 추가로 100개의 agent 투입
for i in 1:100
agent = SchellingAgent(nextid(model),(1,1),false,1)
add_agent_single!(agent,model)
end
fig, ax, abmobs = abmplot(model; plotkwargs...)
fig
Out[20]:
In [21]:
# model을 40 step 진행후 새로 추가된 agent들도 각자의 그룹을 찾아 안정화 되어감
run!(model,agent_step!,40)
fig, ax, abmobs = abmplot(model; plotkwargs...)
fig
Out[21]:
In [22]:
# 저장된 모델 로딩 후 새로운 그룹 추가
model = AgentsIO.load_checkpoint("schelling.jld2"; scheduler=Schedulers.Randomly())
# 새로운 그룹 추가
for i in 1:100
agent = SchellingAgent(nextid(model),(1,1),false,3)
add_agent_single!(agent,model)
end
# 새로운 그룹이 추가 되어 color 및 marker재정의
groupcolor(a) = (:blue, :orange, :green)[a.group]
groupmarker(a) = (:circle, :rect, :cross)[a.group]
#plotkwargs =(;ac = groupcolor, am = groupmarker, as = 10)
fig, ax, abmobs = abmplot(model; plotkwargs...)
fig
Out[22]:
In [23]:
# 새로운 그룹 추가 후 40step 진행
run!(model,agent_step!,40)
fig, ax, abmobs = abmplot(model; plotkwargs...)
fig
Out[23]: