Skip to content

Structure abm examples for agents.jl #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Agents/Flocking/benchmark.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Agents
using BenchmarkTools

include("model.jl")

a = @benchmark step!(model, agent_step!, model_step!, 100) setup = (
(model, agent_step!, model_step!) = flocking()) samples=100

println("Agents.jl Flocking (ms): ", minimum(a.times) * 1e-6)


63 changes: 63 additions & 0 deletions Agents/Flocking/model.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using LinearAlgebra

@agent Bird ContinuousAgent{2} begin
speed::Float64
cohere_factor::Float64
separation::Float64
separate_factor::Float64
match_factor::Float64
visual_distance::Float64
end

function flocking(;
n_birds = 300,
speed = 1.0,
cohere_factor = 0.03,
separation = 1.0,
separate_factor = 0.015,
match_factor = 0.05,
visual_distance = 5.0,
extent = (100, 100),
spacing = visual_distance / 1.5,
)
space2d = ContinuousSpace(extent; spacing)
model = UnremovableABM(Bird, space2d, scheduler = Schedulers.Randomly())
for _ in 1:n_birds
vel = Tuple(rand(model.rng, 2) * 2 .- 1)
add_agent!(
model,
vel,
speed,
cohere_factor,
separation,
separate_factor,
match_factor,
visual_distance,
)
end
return model, flocking_agent_step!, dummystep
end

function flocking_agent_step!(bird, model)
neighbor_ids = nearby_ids(bird, model, bird.visual_distance)
N = 0
match = separate = cohere = (0.0, 0.0)
for id in neighbor_ids
N += 1
neighbor = model[id].pos
heading = neighbor .- bird.pos
cohere = cohere .+ heading
if euclidean_distance(bird.pos, neighbor, model) < bird.separation
separate = separate .- heading
end
match = match .+ model[id].vel
end
N = max(N, 1)
cohere = cohere ./ N .* bird.cohere_factor
separate = separate ./ N .* bird.separate_factor
match = match ./ N .* bird.match_factor
bird.vel = (bird.vel .+ cohere .+ separate .+ match) ./ 2
bird.vel = bird.vel ./ norm(bird.vel)
move_agent!(bird, model, bird.speed)
end

10 changes: 10 additions & 0 deletions Agents/ForestFire/benchmark.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Agents
using BenchmarkTools

include("model.jl")

a = @benchmark step!(model, agent_step!, model_step!, 100) setup =
((model, agent_step!, model_step!) = forest_fire()) samples=100

println("Agents.jl ForestFire (ms): ", minimum(a.times) * 1e-6)

31 changes: 31 additions & 0 deletions Agents/ForestFire/model.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Agents, Random

@agent Automata GridAgent{2} begin end

function forest_fire(; density = 0.7, griddims = (100, 100))
space = GridSpaceSingle(griddims; periodic = false, metric = :manhattan)
rng = Random.MersenneTwister()
## Empty = 0, Green = 1, Burning = 2, Burnt = 3
forest = UnremovableABM(Automata, space; rng, properties = (trees = zeros(Int, griddims),))
for I in CartesianIndices(forest.trees)
if rand(abmrng(forest)) < density
## Set the trees at the left edge on fire
forest.trees[I] = I[1] == 1 ? 2 : 1
end
end
return forest, dummystep, tree_step!
end

function tree_step!(forest)
## Find trees that are burning (coded as 2)
for I in findall(isequal(2), forest.trees)
for idx in nearby_positions(I.I, forest)
## If a neighbor is Green (1), set it on fire (2)
if forest.trees[idx...] == 1
forest.trees[idx...] = 2
end
end
## Finally, any burning tree is burnt out (3)
forest.trees[I] = 3
end
end
9 changes: 9 additions & 0 deletions Agents/Schelling/benchmark.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Agents
using BenchmarkTools

include("model.jl")

a = @benchmark step!(model, agent_step!, model_step!, 10) setup = (
(model, agent_step!, model_step!) = schelling()) samples = 100

println("Agents.jl Schelling (ms): ", minimum(a.times) * 1e-6)
43 changes: 43 additions & 0 deletions Agents/Schelling/model.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Agents

@agent SchellingAgent GridAgent{2} begin
mood::Bool # whether the agent is happy in its position. (true = happy)
group::Int # The group of the agent, determines mood as it interacts with neighbors
end

"""
```julia
schelling(;
numagents = 320,
griddims = (20, 20),
min_to_be_happy = 3,
)
```
Same as in [Schelling's segregation model](@ref).
"""
function schelling(; numagents = 2000, griddims = (50, 50), min_to_be_happy = 3)
@assert numagents < prod(griddims)
space = GridSpaceSingle(griddims, periodic = false)
properties = Dict(:min_to_be_happy => min_to_be_happy)
model = UnremovableABM(SchellingAgent, space; properties, 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, schelling_agent_step!, dummystep
end

function schelling_agent_step!(agent, model)
agent.mood == true && return # do nothing if already 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 ≥ model.min_to_be_happy
agent.mood = true
else
move_agent_single!(agent, model)
end
end
10 changes: 10 additions & 0 deletions Agents/WolfSheep/benchmark.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Agents
using BenchmarkTools

include("model.jl")

a = @benchmark step!(model, agent_step!, model_step!, 500) setup = (
(model, agent_step!, model_step!) = predator_prey()) samples = 100

println("Agents.jl WolfSheep (ms): ", minimum(a.times) * 1e-6)

124 changes: 124 additions & 0 deletions Agents/WolfSheep/model.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
@agent SheepWolf GridAgent{2} begin
type::Symbol # :sheep or :wolf
energy::Float64
reproduction_prob::Float64
Δenergy::Float64
end

Sheep(id, pos, energy, repr, Δe) = SheepWolf(id, pos, :sheep, energy, repr, Δe)
Wolf(id, pos, energy, repr, Δe) = SheepWolf(id, pos, :wolf, energy, repr, Δe)

function predator_prey(;
n_sheep = 60,
n_wolves = 40,
dims = (25, 25),
regrowth_time = 20,
Δenergy_sheep = 5,
Δenergy_wolf = 13,
sheep_reproduce = 0.2,
wolf_reproduce = 0.1,
)
space = GridSpace(dims, periodic = false)
properties = (
fully_grown = falses(dims),
countdown = zeros(Int, dims),
regrowth_time = regrowth_time,
)
model = ABM(SheepWolf, space; properties, scheduler = Schedulers.by_property(:type))
id = 0
for _ in 1:n_sheep
id += 1
energy = rand(1:(Δenergy_sheep*2)) - 1
sheep = Sheep(id, (0, 0), energy, sheep_reproduce, Δenergy_sheep)
add_agent!(sheep, model)
end
for _ in 1:n_wolves
id += 1
energy = rand(1:(Δenergy_wolf*2)) - 1
wolf = Wolf(id, (0, 0), energy, wolf_reproduce, Δenergy_wolf)
add_agent!(wolf, model)
end
for p in positions(model) # random grass initial growth
fully_grown = rand(abmrng(model), Bool)
countdown = fully_grown ? regrowth_time : rand(abmrng(model), 1:regrowth_time) - 1
model.countdown[p...] = countdown
model.fully_grown[p...] = fully_grown
end
return model, predator_agent_step!, predator_model_step!
end

predator_agent_step!(agent::SheepWolf, model) =
agent.type == :sheep ? sheep_step!(agent, model) : wolf_step!(agent, model)

function sheep_step!(sheep, model)
randomwalk!(sheep, model, 1)
sheep.energy -= 1
sheep_eat!(sheep, model)
if sheep.energy < 0
kill_agent!(sheep, model)
return
end
if rand(abmrng(model)) <= sheep.reproduction_prob
wolfsheep_reproduce!(sheep, model)
end
end

function wolf_step!(wolf, model)
randomwalk!(wolf, model, 1)
wolf.energy -= 1
agents = collect(agents_in_position(wolf.pos, model))
dinner = filter!(x -> x.type == :sheep, agents)
wolf_eat!(wolf, dinner, model)
if wolf.energy < 0
kill_agent!(wolf, model)
return
end
if rand(abmrng(model)) <= wolf.reproduction_prob
wolfsheep_reproduce!(wolf, model)
end
end

function sheep_eat!(sheep, model)
if model.fully_grown[sheep.pos...]
sheep.energy += sheep.Δenergy
model.fully_grown[sheep.pos...] = false
end
end

function wolf_eat!(wolf, sheep, model)
if !isempty(sheep)
dinner = rand(abmrng(model), sheep)
kill_agent!(dinner, model)
wolf.energy += wolf.Δenergy
end
end

function wolfsheep_reproduce!(agent, model)
agent.energy /= 2
id = nextid(model)
offspring = SheepWolf(
id,
agent.pos,
agent.type,
agent.energy,
agent.reproduction_prob,
agent.Δenergy,
)
add_agent_pos!(offspring, model)
return
end

function predator_model_step!(model)
@inbounds for p in positions(model)
if !(model.fully_grown[p...])
if model.countdown[p...] ≤ 0
model.fully_grown[p...] = true
model.countdown[p...] = model.regrowth_time
else
model.countdown[p...] -= 1
end
end
end
end


39 changes: 0 additions & 39 deletions Agents/benchmark.jl

This file was deleted.

5 changes: 4 additions & 1 deletion runall.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/bin/bash

echo "Benchmarking Julia"
julia --project=@. Agents/benchmark.jl
julia --project=@. Agents/WolfSheep/benchmark.jl
julia --project=@. Agents/Flocking/benchmark.jl
julia --project=@. Agents/Schelling/benchmark.jl
julia --project=@. Agents/ForestFire/benchmark.jl

echo "Benchmarking NetLogo"
# Don't run above 8 threads otherwise errors will spit once the JVMs try
Expand Down