model_accessing_API.jl has explicit references to StandardABM, EventQueueABM and ReinforcementLearningABM in the Dict/Vec/StructVecABM unions defined at the top of the file. This means that users wanting to implement new types of ABMs, which may still have a Dict/Vector/StructVector as a container, must override nextid and add_agent_to_container!(agent::AbstractAgent, model::ABM), though the implementation is essentially a copy-paste from existing code. Specifically, if add_agent_to_container! is not manually implemented (which isn't required by the documentation, nor does it raise an notimplemented error), adding more than one agent fails if a dictionary is used as the container, because maxid is not updated
function add_agent_to_container!(agent::AbstractAgent, model::ABM)
...
# Update maxid for DictABM
if model isa DictABM
maxid = getfield(model, :maxid)
if maxid[] < getid(agent)
maxid[] = getid(agent)
end
end
Similarly, in collect.jl, lines 376, 404 and 431 explicitly refer to EventBasedABM, instead of calling discretimeabm(model), which would allow other implementations of continuous time models beyond EventBasedABM.
Minimal Working Example
using Agents
using Random
@agent struct Ticker(NoSpaceAgent) end
struct MinimalABM{A, C <: Union{AbstractDict{Int, A}, AbstractVector{A}}, R <: AbstractRNG} <: AgentBasedModel{Nothing}
agents::C
rng::R
maxid::Base.RefValue{Int}
time::Base.RefValue{Int}
end
function MinimalABM(::Type{A}; container::Type = Dict) where {A <: AbstractAgent}
agents = container == Dict ? Dict{Int, A}() : Vector{A}()
return MinimalABM{A, typeof(agents), typeof(Random.default_rng())}(
agents, Random.default_rng(), Ref(0), Ref(0)
)
end
Agents.containertype(::MinimalABM{A, C}) where {A, C} = C
Agents.agenttype(::MinimalABM{A}) where {A} = A
Agents.discretimeabm(::MinimalABM) = true
Agents.extra_actions_after_add!(agent, model::MinimalABM) = nothing
Agents.step!(model::MinimalABM, t::Real) = model
Agents.nextid(model::MinimalABM{A, <:AbstractDict}) where {A} = getfield(model, :maxid)[] + 1
Agents.nextid(model::MinimalABM{A, <:AbstractVector}) where {A} = nagents(model) + 1
function try_add_two_agents(container::Type)
println("=== container = $container ===")
model = MinimalABM(Ticker; container)
a1 = add_agent!(Ticker, model)
println("first agent id: ", a1.id)
println("maxid after first add: ", getfield(model, :maxid)[])
a2 = add_agent!(Ticker, model)
println("second agent id: ", a2.id)
println("-> OK, no id collision")
end
try_add_two_agents(Vector)
try_add_two_agents(Dict)
This works for the Vector but fails for the Dict:
=== container = Vector ===
first agent id: 1
maxid after first add: 0
second agent id: 2
-> OK, no id collision
=== container = Dict ===
first agent id: 1
maxid after first add: 0
ERROR: Can't add agent to container. There is already an agent with id=1
Agents.jl version v7.0.3
model_accessing_API.jl has explicit references to StandardABM, EventQueueABM and ReinforcementLearningABM in the Dict/Vec/StructVecABM unions defined at the top of the file. This means that users wanting to implement new types of ABMs, which may still have a Dict/Vector/StructVector as a container, must override
nextidandadd_agent_to_container!(agent::AbstractAgent, model::ABM), though the implementation is essentially a copy-paste from existing code. Specifically, ifadd_agent_to_container!is not manually implemented (which isn't required by the documentation, nor does it raise annotimplementederror), adding more than one agent fails if a dictionary is used as the container, becausemaxidis not updatedSimilarly, in collect.jl, lines 376, 404 and 431 explicitly refer to EventBasedABM, instead of calling
discretimeabm(model), which would allow other implementations of continuous time models beyond EventBasedABM.Minimal Working Example
This works for the Vector but fails for the Dict:
Agents.jl version v7.0.3