# Modifying interaction_pairs() for EventQueueABM

Hello everyone,

I’m trying to modify inbuild function `interaction_pairs()` to make it usable for EventQueueABM model too. Any suggestion will be helpful…

Here is original code :

``````function interacting_pairs(model::ABM{<:ContinuousSpace}, r::Real, method;
scheduler = abmscheduler(model), nearby_f = nearby_ids_exact,
)
@assert method ∈ (:nearest, :all, :types)
pairs = Tuple{Int,Int}[]
if method == :nearest
true_pairs!(pairs, model, r, scheduler)
elseif method == :all
all_pairs!(pairs, model, r, nearby_f)
elseif method == :types
type_pairs!(pairs, model, r, scheduler, nearby_f)
end
return PairIterator(pairs, agent_container(model))
end

function all_pairs!(
pairs::Vector{Tuple{Int,Int}},
model::ABM{<:ContinuousSpace},
r::Real, nearby_f,
)
for a in allagents(model)
for nid in nearby_f(a, model, r)
# Sort the pair to overcome any uniqueness issues
new_pair = isless(a.id, nid) ? (a.id, nid) : (nid, a.id)
new_pair ∉ pairs && push!(pairs, new_pair)
end
end
end

function true_pairs!(pairs::Vector{Tuple{Int,Int}}, model::ABM{<:ContinuousSpace}, r::Real, scheduler)
distances = Vector{Float64}(undef, 0)
for a in (model[id] for id in scheduler(model))
nn = nearest_neighbor(a, model, r)
nn === nothing && continue
# Sort the pair to overcome any uniqueness issues
new_pair = isless(a.id, nn.id) ? (a.id, nn.id) : (nn.id, a.id)
if new_pair ∉ pairs
# We also need to check if our current pair is closer to each
# other than any pair using our first id already in the list,
# so we keep track of nn distances.
dist = euclidean_distance(a.pos, nn.pos, model)

idx = findfirst(x -> first(new_pair) == x, first.(pairs))
if idx === nothing
push!(pairs, new_pair)
push!(distances, dist)
elseif idx !== nothing && distances[idx] > dist
# Replace this pair, it is not the true neighbor
pairs[idx] = new_pair
distances[idx] = dist
end
end
end
to_remove = Int[]
# `counter` counts the number of occurencies for each item, it comes from DataStructure.jl
for doubles in [k for (k,v) in counter(Iterators.flatten(pairs)) if v>1]
# This list is the set of pairs that have two distances in the pair list.
# The one with the largest distance value must be dropped.
fidx = findfirst(isequal(doubles), first.(pairs))
if fidx !== nothing
lidx = findfirst(isequal(doubles), last.(pairs))
largest = distances[fidx] <= distances[lidx] ? lidx : fidx
push!(to_remove, largest)
else
# doubles are not from first sorted, there could be more than one.
idxs = findall(isequal(doubles), last.(pairs))
to_keep = findmin(map(i->distances[i], idxs))[2]
deleteat!(idxs, to_keep)
append!(to_remove, idxs)
end
end
deleteat!(pairs, unique!(sort!(to_remove)))
end

function type_pairs!(
pairs::Vector{Tuple{Int,Int}},
model::ABM{<:ContinuousSpace},
r::Real, scheduler, nearby_f,
)
# We don't know ahead of time what types the scheduler will provide. Get a list.
available_types = unique(typeof(model[id]) for id in scheduler(model))
for id in scheduler(model)
for nid in nearby_f(model[id], model, r)
neighbor_type = typeof(model[nid])
if neighbor_type ∈ available_types && neighbor_type !== typeof(model[id])
# Sort the pair to overcome any uniqueness issues
new_pair = isless(id, nid) ? (id, nid) : (nid, id)
new_pair ∉ pairs && push!(pairs, new_pair)
end
end
end
end

struct PairIterator{A}
pairs::Vector{Tuple{Int,Int}}
agents::Dict{Int,A}
end

Base.eltype(::PairIterator{A}) where {A} = Tuple{A, A}
Base.length(iter::PairIterator) = length(iter.pairs)
function Base.iterate(iter::PairIterator, i = 1)
i > length(iter) && return nothing
p = iter.pairs[i]
id1, id2 = p
return (iter.agents[id1], iter.agents[id2]), i + 1
end

``````

Modified version for only nearest:

``````function interacting_pairs(
model::EventQueueABM{<:ContinuousSpace},
r::Real,
exact = true,
)
pairs = Tuple{Int,Int}[]
true_pairs!(pairs, model, r)

return PairIterator(pairs, agent_container(model))
end

function true_pairs!(pairs::Vector{Tuple{Int,Int}}, model::EventQueueABM{<:ContinuousSpace}, r::Real)
distances = Vector{Float64}(undef, 0)
for a in (model[id] for id in allids(model))
nn = nearest_neighbor(a, model, r)
nn === nothing && continue
# Sort the pair to overcome any uniqueness issues
new_pair = isless(a.id, nn.id) ? (a.id, nn.id) : (nn.id, a.id)
if new_pair ∉ pairs
# We also need to check if our current pair is closer to each
# other than any pair using our first id already in the list,
# so we keep track of nn distances.
dist = euclidean_distance(a.pos, nn.pos, model)

idx = findfirst(x -> first(new_pair) == x, first.(pairs))
if idx === nothing
push!(pairs, new_pair)
push!(distances, dist)
elseif idx !== nothing && distances[idx] > dist
# Replace this pair, it is not the true neighbor
pairs[idx] = new_pair
distances[idx] = dist
end
end
end
to_remove = Int[]
for doubles in symdiff(unique(Iterators.flatten(pairs)), collect(Iterators.flatten(pairs)))
# This list is the set of pairs that have two distances in the pair list.
# The one with the largest distance value must be dropped.
fidx = findfirst(isequal(doubles), first.(pairs))
if fidx !== nothing
lidx = findfirst(isequal(doubles), last.(pairs))
largest = distances[fidx] <= distances[lidx] ? lidx : fidx
push!(to_remove, largest)
else
# doubles are not from first sorted, there could be more than one.
idxs = findall(isequal(doubles), last.(pairs))
to_keep = findmin(map(i->distances[i], idxs))[2]
deleteat!(idxs, to_keep)
append!(to_remove, idxs)
end
end
deleteat!(pairs, unique!(sort!(to_remove)))
end

struct PairIterator{A}
pairs::Vector{Tuple{Int,Int}}
agents::Dict{Int,A}
end

Base.length(iter::PairIterator) = length(iter.pairs)
function Base.iterate(iter::PairIterator, i = 1)
i > length(iter) && return nothing
p = iter.pairs[i]
id1, id2 = p
return (iter.agents[id1], iter.agents[id2]), i + 1
end

``````

At the moment Im getting error for agent_container. I thought it was some internal function of Agents.jl but seems like not.

Error:

``````ERROR: UndefVarError: `agent_container` not defined
Stacktrace:
[1] interacting_pairs(model::EventQueueABM{…}, r::Float64, exact::Bool)
@ Main ~/PhD/Program_Julia/code/continuous_time_ABM.jl:23
[2] interacting_pairs
@ ~/PhD/Program_Julia/code/continuous_time_ABM.jl:20 [inlined]
[3] move!(agent::CELL, model::EventQueueABM{…})
@ Main ~/PhD/Program_Julia/code/continuous_time_ABM.jl:136
[4] process_event!(event_tuple::Tuple{…}, model::EventQueueABM{…})
@ Agents ~/.julia/packages/Agents/8JW8b/src/simulations/step_eventqueue.jl:60
[5] one_step!(queue::DataStructures.BinaryHeap{…}, model_t::Base.RefValue{…}, stop_time::Float64, model::EventQueueABM{…})
@ Agents ~/.julia/packages/Agents/8JW8b/src/simulations/step_eventqueue.jl:42
[6] step_ahead!
@ ~/.julia/packages/Agents/8JW8b/src/simulations/step_eventqueue.jl:19 [inlined]
[7] step!
@ ~/.julia/packages/Agents/8JW8b/src/simulations/step_eventqueue.jl:5 [inlined]
[8] step!(abmobs::ABMObservable{…}, t::Float64)
@ AgentsVisualizations ~/.julia/packages/Agents/8JW8b/ext/AgentsVisualizations/src/model_observable.jl:37
[9] (::AgentsVisualizations.var"#77#80"{Int64, EventQueueABM{…}, ABMObservable{…}, Observable{…}})(io::VideoStream)
@ AgentsVisualizations ~/.julia/packages/Agents/8JW8b/ext/AgentsVisualizations/src/convenience.jl:123
[10] Record(func::AgentsVisualizations.var"#77#80"{…}, figlike::Figure; kw_args::@Kwargs{…})
@ Makie ~/.julia/packages/Makie/iRM0c/src/recording.jl:166
[11] record(func::Function, figlike::Figure, path::String; kw_args::@Kwargs{framerate::Int64, compression::Int64})
@ Makie ~/.julia/packages/Makie/iRM0c/src/recording.jl:148
[12] record
@ ~/.julia/packages/Makie/iRM0c/src/recording.jl:146 [inlined]
[13] abmvideo(file::String, model::EventQueueABM{…}; spf::Nothing, dt::Float64, framerate::Int64, frames::Int64, title::String, showstep::Bool, figure::@NamedTuple{…}, axis::@NamedTuple{}, recordkwargs::@NamedTuple{…}, kwargs::@Kwargs{…})
@ AgentsVisualizations ~/.julia/packages/Agents/8JW8b/ext/AgentsVisualizations/src/convenience.jl:120
[14] top-level scope
@ ~/PhD/Program_Julia/code/continuous_time_ABM.jl:248
Some type information was truncated. Use `show(err)` to see complete types.`````````

`agent_container(model) = getfield(model, :agents)` solves error issue.