Starting from the example Continuous space social distancing, I'm trying to create a model that involves merging each pair of nearby agents into an agent of a different type, with other properties.
using Agents
@agent struct Single(ContinuousAgent{2, Float64})
mass :: Float64
propertyA :: Integer
end
@agent struct Double(ContinuousAgent{2, Float64})
mass :: Float64
propertyB :: Float64
propertyC :: Integer
end
using Random
agent_step!(agent, model) = move_agent!(agent, model, model.dt)
function merge_agents!(a1, a2, model)
new_mass = a1.mass + a2.mass
new_pos = ((a1.pos[1]+a2.pos[1])/2.0, (a1.pos[2]+a2.pos[2])/2.0)
new_vel = sincos(2π * rand(abmrng(model))) .* 0.0
add_agent!(Double, model; pos = new_pos, vel = new_vel, mass = new_mass, propertyB = 20.0, propertyC = 5)
remove_agent!(a1, model)
remove_agent!(a2, model)
end
function model_step!(model)
for (a1, a2) in interacting_pairs(model, 0.01, :nearest)
if isa(a1, Single) && isa(a2, Single)
merge_agents!(a1, a2, model)
end
end
end
function ball_model(; speed = 0.002)
space2d = ContinuousSpace((1, 1); spacing = 0.02)
model = StandardABM(Union{Single, Double}, space2d; agent_step!, model_step!, properties = Dict(:dt => 1.0),rng = MersenneTwister(42))
for ind in 1:100
new_pos = Tuple(rand(abmrng(model), 2))
new_vel = sincos(2π * rand(abmrng(model))) .* speed
add_agent!(Single, model; pos = new_pos, vel = new_vel, mass = 1.0, propertyA = 10)
end
return model
end
model = ball_model()
using CairoMakie
abmvideo(
"mergingAgents-Union.mp4",
model;
title = "Merging agents (Union)",
frames = 500,
dt = 1,
framerate = 25,
agent_size = agent -> isa(agent, Double) ? 20 : 10,
agent_color = agent -> isa(agent, Double) ? :blue : :green
)
The code above works as desired.
However, I've seen that the Agents.jl documentation recommends using @multiagent
instead of Union
, so I tried to adapt the code accordingly:
using Agents
@agent struct Single(ContinuousAgent{2, Float64})
mass :: Float64
propertyA :: Integer
end
@agent struct Double(ContinuousAgent{2, Float64})
mass :: Float64
propertyB :: Float64
propertyC :: Integer
end
@multiagent Nucleus(Single, Double) <: AbstractAgent
using Random
agent_step!(agent, model) = move_agent!(agent, model, model.dt)
function merge_agents!(a1, a2, model)
new_mass = a1.mass + a2.mass
new_pos = ((a1.pos[1]+a2.pos[1])/2.0, (a1.pos[2]+a2.pos[2])/2.0)
new_vel = sincos(2π * rand(abmrng(model))) .* 0.0
new_agent = Nucleus(Double(model, new_pos, new_vel, new_mass, 20.0, 5))
add_agent!(new_agent, model)
remove_agent!(a1, model)
remove_agent!(a2, model)
end
function model_step!(model)
for (a1, a2) in interacting_pairs(model, 0.01, :nearest)
if isa(a1, Single) && isa(a2, Single)
merge_agents!(a1, a2, model)
end
end
end
function ball_model(; speed = 0.002)
space2d = ContinuousSpace((1, 1); spacing = 0.02)
model = StandardABM(Nucleus, space2d; agent_step!, model_step!, properties = Dict(:dt => 1.0),rng = MersenneTwister(42))
for ind in 1:100
new_pos = Tuple(rand(abmrng(model), 2))
new_vel = sincos(2π * rand(abmrng(model))) .* speed
new_agent = Nucleus(Single(model, new_pos, new_vel, 1.0, 10))
add_agent_own_pos!(new_agent, model)
end
return model
end
model = ball_model()
using CairoMakie
abmvideo(
"mergingAgents-multiagent.mp4",
model;
title = "Merging agents (multiagent)",
frames = 500,
dt = 1,
framerate = 25,
agent_size = agent -> isa(agent, Double) ? 20 : 10,
agent_color = agent -> isa(agent, Double) ? :blue : :green
)
Now, the agents don't merge anymore.
What am I doing wrong?
The Agents.@multiagent
(currently v6.1) documentation says
@multiagent YourAgentType(AgentTypesToMerge...) [<: OptionalSupertype]
Define multiple agent "subtypes", which are variants of a unique type YourAgentType. This means that all "subtypes" are enclosed in the overarching type. Then, You cannot distinguish them on the basis of
typeof
, but need to use instead thevariantof
function. [...]The way to retrieve the variant of the agent is through the function
variantof
. [...]You can also access the enclosed variant instance with the
variant
function. [...]
Using your agents constructed with a Nucleus
wrapping a Single
or a Double
, the problem is that expressions like isa(anAgent, Single)
and isa(anAgent, Double)
are false.
anAgent = Nucleus(Single(id=-1; pos=[-2.0,-3.0], vel=[-0.2,-0.3], mass=-4.0, propertyA=-5))
typeof(anAgent) # Nucleus
isa(anAgent, Nucleus) # true
isa(anAgent, Single) # false
Solutions are to unwrap the Nucleus
with variant(anAgent)
or variantof(anAgent)
.
isa(variant(anAgent), Single) # true
variantof(anAgent) == Single # true
For example
function model_step!(model)
for (a1, a2) in interacting_pairs(model, 0.01, :nearest)
if isa(variant(a1), Single) && isa(variant(a2), Single) # modified
merge_agents!(a1, a2, model)
end
end
end
abmvideo(
"mergingAgents-multiagent.mp4",
model;
title = "Merging agents (multiagent)",
frames = 500,
dt = 1,
framerate = 25,
agent_size = agent -> isa(variant(agent), Double) ? 20 : 10, # modified
agent_color = agent -> isa(variant(agent), Double) ? :blue : :green # modified
)