Dear all, I am working on an ABM and now I am trying to improve performance through in-model parallelization using @threads since I have 10^6 agents (I am working with fish populations and simulation is in 0D for now, I would like to avoid super-individuals atm).
My agents do not interact with each other so, ideally, multi-threading should be possible, but I struggled with function creating new agents (calling add_agent!). So I decided to run them separately and in series in a complex_step!() function. But I guess the same problem is with remove_agent!()
I get infact this error:
*julia> show(err)*
*1-element ExceptionStack:*
*LoadError: UndefRefError: access to undefined reference*
*Stacktrace:*
* [1] getindex*
* @ .\essentials.jl:13 [inlined]*
* [2] iterate*
* @ .\array.jl:945 [inlined]*
* [3] filter!(f::var"#8#11", a::Vector{Person})*
* @ Base .\array.jl:2721*
* [4] complex_step!(model::StandardABM{Nothing, Person, typeof(Agents.Schedulers.fastest), Dict{Symbol, Real}, Random.TaskLocalRNG})*
* @ Main c:\Users\elli2\Documents\PhD\Multi-SPelAgents\#MWE.jl:65*
* [5] step!*
* @ C:\Users\elli2\.julia\packages\Agents\xtlGn\src\simulations\step.jl:40 [inlined]*
* [6] run!(model::StandardABM{Nothing, Person, typeof(Agents.Schedulers.fastest), Dict{Symbol, Real}, Random.TaskLocalRNG}, agent_step!::typeof(dummystep), model_step!::typeof(complex_step!), n::Int64; when::Bool, when_model::Bool, mdata::Nothing, adata::Nothing, obtainer::Function, agents_first::Bool, showprogress::Bool)*
* @ Agents C:\Users\elli2\.julia\packages\Agents\xtlGn\src\simulations\collect.jl:151*
* [7] run!(model::StandardABM{Nothing, Person, typeof(Agents.Schedulers.fastest), Dict{Symbol, Real}, Random.TaskLocalRNG}, agent_step!::Function, model_step!::Function, n::Int64)*
* @ Agents C:\Users\elli2\.julia\packages\Agents\xtlGn\src\simulations\collect.jl:114*
* [8] top-level scope*
* @ c:\Users\elli2\Documents\PhD\Multi-SPelAgents\#MWE.jl:82*
* [9] eval*
* @ .\boot.jl:385 [inlined]*
* [10] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)*
* @ Base .\loading.jl:2070*
* [11] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::@Kwargs{})*
* @ Base .\essentials.jl:887*
* [12] invokelatest(::Any, ::Any, ::Vararg{Any})*
* @ Base .\essentials.jl:884*
* [13] inlineeval(m::Module, code::String, code_line::Int64, code_column::Int64, file::String; softscope::Bool)*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:263*
* [14] (::VSCodeServer.var"#67#72"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:181*
* [15] withpath(f::VSCodeServer.var"#67#72"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams}, path::String) *
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\repl.jl:274*
* [16] (::VSCodeServer.var"#66#71"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:179*
* [17] hideprompt(f::VSCodeServer.var"#66#71"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\repl.jl:38*
* [18] (::VSCodeServer.var"#65#70"{Bool, Bool, Bool, Module, String, Int64, Int64, String, VSCodeServer.ReplRunCodeRequestParams})()*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:150*
* [19] with_logstate(f::Function, logstate::Any)*
* @ Base.CoreLogging .\logging.jl:515*
* [20] with_logger*
* @ .\logging.jl:627 [inlined]*
* [21] (::VSCodeServer.var"#64#69"{VSCodeServer.ReplRunCodeRequestParams})()*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:255*
* [22] #invokelatest#2*
* @ Base .\essentials.jl:887 [inlined]*
* [23] invokelatest(::Any)*
* @ Base .\essentials.jl:884*
* [24] (::VSCodeServer.var"#62#63")()*
* @ VSCodeServer c:\Users\elli2\.vscode\extensions\julialang.language-julia-1.65.2\scripts\packages\VSCodeServer\src\eval.jl:34*
*in expression starting at c:\Users\elli2\Documents\PhD\Multi-SPelAgents\#MWE.jl:82*
running this MWE:
#MWE
using Agents
using Distributed
@agent Person NoSpaceAgent begin
type::Symbol
height::Int64
end
properties = Dict(:time_sim => 0,
:height_to_die => 30,
:height_to_generate => 90,
:sum_h => 0.0)
#step functions
function random_h!(Person, model)
Person.height = rand(1:100)
end
function die!(Person, model)
if Person.height < model.height_to_die
remove_agent!(Person, model)
end
end
function add_people!(Person, model)
#youngs generate less people
if Person.type == :young && Person.height > model.height_to_generate
for i in 1:10
add_agent!(model, rand((:baby, :young, :adult)), rand(1:100))
end
#adults generate more people
elseif Person.type == :adult && Person.height > model.height_to_generate
for i in 10:20
add_agent!(model, rand((:baby, :young, :adult)), rand(1:100))
end
end
end
function evolve_environment!(model)
model.time_sim += 1
model.sum_h = 0
for person in allagents(model)
model.sum_h += getproperty(person, :height)
end
end
#complex step
function complex_step!(model)
#first check of agents
#The collect(values(allagents(model))) is used to create a copy of the agents in the model.
#This is necessary because you can't add or remove agents
#while iterating over them directly?
all_agents = collect(values(allagents(model)))
println(nagents(model))
Threads.@threads for person in all_agents
random_h!(person, model)
die!(person, model)
end
all_agents = collect(values(allagents(model)))
young_adults = filter!(person -> person.type == :young || person.type == :adult, all_agents)
println(nagents(model))
#update of agents who survived and can reproduce; only young and adults can generate
for person in young_adults
add_people!(person, model)
end
evolve_environment!(model)
end
model = ABM(Person; properties)
#add agents
for i in 1:100
add_agent!(model, rand((:baby, :young, :adult)), rand(1:100))
end
#run
run!(model, dummystep, complex_step!, 1000)
Probably is more than a minimum example but I tried to include all the functions I need in a simplified way and where I got errors.
I know multi-thread is really case-dependent and complicated, but still hope some of you can help since this is an important step for my simulation.
Thank you