Agents.jl: Problem collecting agent data when agent field is a Vector

Created on 31 Aug 2020  Β·  5Comments  Β·  Source: JuliaDynamics/Agents.jl

I'm working on a model where agents have a vector dynamic state, not related with the position in space, that I wish to measure each step.
A simplified version of my code is

using Agents, LinearAlgebra, LightGraphs

mutable struct FakeAgent <: AbstractAgent
    id::Int
    pos::Int
    state::Vector{Float64}
end

function fakestep!(agent,model)
    i = agent.id
    j = rand(1:size(agent.state,1))
    x = randn()
    model[agent.id].state[j] += x
    return nothing
end

N = 2
model = ABM(FakeAgent, 
                      GraphSpace(complete_graph(N)); 
                      scheduler=random_activation)

K = 3
# initialize agents to have obvious states: model.agents[i].state = [i, i, i]
for i in 1:N
    a = FakeAgent(i,i, i*ones(K))
    add_agent_pos!(a,model)
end

(adf,_) = run!(model, fakestep!, 10; adata=[:state])

first(adf,6)

which gives

"""
6Γ—3 DataFrame
β”‚ Row β”‚ step  β”‚ id    β”‚ state                       β”‚
β”‚     β”‚ Int64 β”‚ Int64 β”‚ Array{Float64,1}            β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ 0     β”‚ 1     β”‚ [3.71784, -1.2705, 0.75856] β”‚
β”‚ 2   β”‚ 0     β”‚ 2     β”‚ [0.69792, 2.55628, 1.05595] β”‚
β”‚ 3   β”‚ 1     β”‚ 1     β”‚ [3.71784, -1.2705, 0.75856] β”‚
β”‚ 4   β”‚ 1     β”‚ 2     β”‚ [0.69792, 2.55628, 1.05595] β”‚
β”‚ 5   β”‚ 2     β”‚ 1     β”‚ [3.71784, -1.2705, 0.75856] β”‚
β”‚ 6   β”‚ 2     β”‚ 2     β”‚ [0.69792, 2.55628, 1.05595] β”‚
"""

Notice how the state vector is always the last state after all steps.
If I fix the step function:

function fakestep_fixed!(agent,model)
    i = agent.id
    j = rand(1:size(agent.state,1))
    x = randn()

    new_state = copy(agent.state)
    new_state[j] += x

    model[agent.id].state = new_state
    return nothing
end

then I get what is expected

"""
6Γ—3 DataFrame
β”‚ Row β”‚ step  β”‚ id    β”‚ state                   β”‚
β”‚     β”‚ Int64 β”‚ Int64 β”‚ Array{Float64,1}        β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ 0     β”‚ 1     β”‚ [1.0, 1.0, 1.0]         β”‚
β”‚ 2   β”‚ 0     β”‚ 2     β”‚ [2.0, 2.0, 2.0]         β”‚
β”‚ 3   β”‚ 1     β”‚ 1     β”‚ [1.0, 1.0, 1.27255]     β”‚
β”‚ 4   β”‚ 1     β”‚ 2     β”‚ [2.0, 2.0, 1.23249]     β”‚
β”‚ 5   β”‚ 2     β”‚ 1     β”‚ [0.22367, 1.0, 1.27255] β”‚
β”‚ 6   β”‚ 2     β”‚ 2     β”‚ [2.32241, 2.0, 1.23249] β”‚
"""

I could, instead of rewriting the step function, use mdata and collect all the agents at once (works, but is not convenient).
I'm not sure if it is a bug or just a misuse of Julia or DataFrames.

(PS: Code running on Linux, with Julia 1.5.1 and Agents.jl v3.5.0)

bug data

Most helpful comment

And we thank you @flipgthb for opening such a great issue, with a true MWE, a lot of details, and pointing out an important consequence of our data collection! Making a good issue is hard work ;)

All 5 comments

This does indeed look like undefined behaviour. Thanks for reporting it!

Before running the model, you can clearly see

julia> [a.state for a in allagents(model)]
2-element Array{Array{Float64,1},1}:
 [2.0, 2.0, 2.0]
 [1.0, 1.0, 1.0]

Thus the step 0 info in the first run is incorrect for sure - even before the run! function starts stepping the model.

Will need to investigate why though, give us some time to find out.

As an aside: model[agent.id].state[j] += x can just be agent.state[j] += x

Yep, so we're not doing a deep copy when collecting containers it looks like:

julia> adata=[:state]
1-element Array{Symbol,1}:
 :state

julia> df_agent = init_agent_dataframe(model, adata)
0Γ—3 DataFrames.DataFrame


julia> collect_agent_data!(df_agent, model, adata, 0)
2Γ—3 DataFrames.DataFrame
β”‚ Row β”‚ step  β”‚ id    β”‚ state           β”‚
β”‚     β”‚ Int64 β”‚ Int64 β”‚ Array…          β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ 0     β”‚ 1     β”‚ [1.0, 1.0, 1.0] β”‚
β”‚ 2   β”‚ 0     β”‚ 2     β”‚ [2.0, 2.0, 2.0] β”‚

julia> model[1].state[2] += randn()
1.9550338329763712

julia> collect_agent_data!(df_agent, model, adata, 2)
4Γ—3 DataFrames.DataFrame
β”‚ Row β”‚ step  β”‚ id    β”‚ state               β”‚
β”‚     β”‚ Int64 β”‚ Int64 β”‚ Array{Float64,1}    β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1   β”‚ 0     β”‚ 1     β”‚ [1.0, 1.95503, 1.0] β”‚
β”‚ 2   β”‚ 0     β”‚ 2     β”‚ [2.0, 2.0, 2.0]     β”‚
β”‚ 3   β”‚ 2     β”‚ 1     β”‚ [1.0, 1.95503, 1.0] β”‚
β”‚ 4   β”‚ 2     β”‚ 2     β”‚ [2.0, 2.0, 2.0]     β”‚

As an aside: model[agent.id].state[j] += x can just be agent.state[j] += x

Thanks for replying and for the tip.
Also, thanks for all the work! This package will speed up the setup for my model :)

And we thank you @flipgthb for opening such a great issue, with a true MWE, a lot of details, and pointing out an important consequence of our data collection! Making a good issue is hard work ;)

@flipgthb we'll release 3.6 soon, just a few things to clear up. To fix your issue, you should now run

adf,_ = run!(model, fakestep!, 10; adata=[:state], obtainer = copy)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Datseris picture Datseris  Β·  3Comments

dunefox picture dunefox  Β·  11Comments

Datseris picture Datseris  Β·  7Comments

Libbum picture Libbum  Β·  7Comments

fbanning picture fbanning  Β·  10Comments