Agents.jl icon indicating copy to clipboard operation
Agents.jl copied to clipboard

Fix hover inspection for abstract spaces

Open hbsmith opened this issue 7 months ago • 5 comments

Currently there's a bug in Agents where trying to hover inspect a 2D continuous plot throws a DimensionMismatch error. This seems analogous to #1090 , fixed in #1092 .

You can reproduce the bug by doing something like:

using Agents
using GLMakie
using AgentsExampleZoo

model = AgentsExampleZoo.flocking()

fig, abmobs = abmexploration(model)

fig

And trying to hover over any agent. You'll get an error like:

nested task error: DimensionMismatch: arrays could not be broadcast to a common size
    Stacktrace:
      [1] _bcs1(a::StaticArrays.SOneTo{2}, b::StaticArrays.SOneTo{3})
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:77
      [2] _bcs(shape::Tuple{StaticArrays.SOneTo{2}}, newshape::Tuple{StaticArrays.SOneTo{3}})
        @ Base.Broadcast ./broadcast.jl:522
      [3] broadcast_shape(::Tuple{StaticArrays.SOneTo{2}}, ::Tuple{StaticArrays.SOneTo{3}})
        @ Base.Broadcast ./broadcast.jl:516
      [4] static_combine_axes
        @ ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:20 [inlined]
      [5] _axes(bc::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(-), Tuple{SVector{2, Float64}, Tuple{Float64, Float64, Float64}}}, ::Nothing)
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:25
      [6] axes
        @ ./broadcast.jl:234 [inlined]
      [7] static_axes(A::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(-), Tuple{SVector{2, Float64}, Tuple{Float64, Float64, Float64}}})
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:22
      [8] static_combine_axes(A::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(-), Tuple{SVector{2, Float64}, Tuple{Float64, Float64, Float64}}})
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:21
      [9] static_combine_axes
        @ ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:20 [inlined]
     [10] _axes(bc::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(min), Tuple{Tuple{Float64, Float64, Float64}, Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(-), Tuple{SVector{2, Float64}, Tuple{Float64, Float64, Float64}}}}}, ::Nothing)
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:25
     [11] axes
        @ ./broadcast.jl:234 [inlined]
     [12] static_axes(A::Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(min), Tuple{Tuple{Float64, Float64, Float64}, Base.Broadcast.Broadcasted{StaticArraysCore.StaticArrayStyle{1}, Nothing, typeof(-), Tuple{SVector{2, Float64}, Tuple{Float64, Float64, Float64}}}}})
        @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:22
     [13] static_combine_axes (repeats 2 times)
        @ ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:20 [inlined]
     [14] instantiate
        @ ~/.julia/packages/StaticArrays/LSPcF/src/broadcast.jl:32 [inlined]
     [15] materialize
        @ ./broadcast.jl:872 [inlined]
     [16] euclidean_distance(p1::Tuple{Float64, Float64, Float64}, p2::Tuple{Float64, Float64, Float64}, space::ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)})
        @ Agents ~/.julia/dev/Agents/src/spaces/utilities.jl:42
     [17] euclidean_distance(p1::Tuple{Float64, Float64, Float64}, p2::Tuple{Float64, Float64, Float64}, model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG})
        @ Agents ~/.julia/dev/Agents/src/spaces/utilities.jl:26
     [18] distance_from_cell_center(pos::Tuple{Float64, Float64, Float64}, model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG})
        @ Agents ~/.julia/dev/Agents/src/spaces/continuous.jl:119
     [19] nearby_ids_approx(pos::Tuple{Float64, Float64, Float64}, model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}, r::Float64)
        @ Agents ~/.julia/dev/Agents/src/spaces/continuous.jl:217
     [20] nearby_ids_exact(pos::Tuple{Float64, Float64, Float64}, model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}, r::Float64)
        @ Agents ~/.julia/dev/Agents/src/spaces/continuous.jl:231
     [21] ids_to_inspect(model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}, pos::Tuple{Float64, Float64, Float64})
        @ AgentsVisualizations ~/.julia/dev/Agents/ext/AgentsVisualizations/src/spaces/continuous.jl:43
     [22] agent2string(model::StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}, pos::Tuple{Float64, Float64, Float64})
        @ AgentsVisualizations ~/.julia/dev/Agents/ext/AgentsVisualizations/src/spaces/abstract.jl:143
     [23] show_data(inspector::DataInspector, p::Plot{AgentsVisualizations._abmplot, Tuple{ABMObservable{Observable{StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}}, Nothing, Nothing, Nothing, Nothing, Bool, Observable{Int64}, Observable{Tuple{Base.RefValue{Int64}, Vector{Int64}}}}}}, idx::UInt32, source::Scatter{Tuple{Vector{Point{2, Float32}}}})
        @ AgentsVisualizations ~/.julia/dev/Agents/ext/AgentsVisualizations/src/spaces/abstract.jl:101
     [24] show_data_recursion(inspector::DataInspector, plot::Plot{AgentsVisualizations._abmplot, Tuple{ABMObservable{Observable{StandardABM{ContinuousSpace{2, true, Float64, typeof(Agents.no_vel_update)}, AgentsExampleZoo.Bird, Vector{AgentsExampleZoo.Bird}, Tuple{DataType}, typeof(AgentsExampleZoo.flocking_agent_step!), typeof(dummystep), Agents.Schedulers.Randomly, Nothing, TaskLocalRNG}}, Nothing, Nothing, Nothing, Nothing, Bool, Observable{Int64}, Observable{Tuple{Base.RefValue{Int64}, Vector{Int64}}}}}}, idx::UInt32, source::Scatter{Tuple{Vector{Point{2, Float32}}}})
        @ Makie ~/.julia/packages/Makie/6KcTF/src/interaction/inspector.jl:373
     [25] show_data_recursion(inspector::DataInspector, plot::Scatter{Tuple{Vector{Point{2, Float32}}}}, idx::UInt32)
        @ Makie ~/.julia/packages/Makie/6KcTF/src/interaction/inspector.jl:343
     [26] on_hover(inspector::DataInspector)
        @ Makie ~/.julia/packages/Makie/6KcTF/src/interaction/inspector.jl:326
     [27] (::Makie.var"#1338#1342"{Scene, DataInspector})(ch::Channel{Nothing})
        @ Makie ~/.julia/packages/Makie/6KcTF/src/interaction/inspector.jl:291
     [28] (::Base.var"#739#740"{Makie.var"#1338#1342"{Scene, DataInspector}, Channel{Nothing}})()
        @ Base ./channels.jl:142
Error in callback:
TaskFailedException
Stacktrace:
  [1] check_channel_state
    @ ./channels.jl:188 [inlined]
  [2] put!
    @ ./channels.jl:356 [inlined]
  [3] (::Makie.var"#1339#1343"{Channel{Nothing}})(::Tuple{Float64, Float64}, ::Tuple{Float64, Float64})
    @ Makie ~/.julia/packages/Makie/6KcTF/src/interaction/inspector.jl:301
  [4] invokelatest(::Any, ::Any, ::Vararg{Any}; kwargs::@Kwargs{})
    @ Base ./essentials.jl:1055
  [5] invokelatest(::Any, ::Any, ::Vararg{Any})
    @ Base ./essentials.jl:1052
  [6] (::Observables.OnAny)(value::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:420
  [7] #invokelatest#2
    @ ./essentials.jl:1055 [inlined]
  [8] invokelatest
    @ ./essentials.jl:1052 [inlined]
  [9] notify
    @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
 [10] setindex!(observable::Observable, val::Any)
    @ Observables ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123
 [11] (::GLMakie.MousePositionUpdater)(::Makie.TickState)
    @ GLMakie ~/.julia/packages/GLMakie/YAome/src/events.jl:6
 [12] #invokelatest#2
    @ ./essentials.jl:1055 [inlined]
 [13] invokelatest
    @ ./essentials.jl:1052 [inlined]
 [14] notify
    @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:206 [inlined]
 [15] setindex!
    @ ~/.julia/packages/Observables/YdEbO/src/Observables.jl:123 [inlined]
 [16] pollevents(screen::GLMakie.Screen{GLFW.Window}, frame_state::Makie.TickState)
    @ GLMakie ~/.julia/packages/GLMakie/YAome/src/screen.jl:547
 [17] on_demand_renderloop(screen::GLMakie.Screen{GLFW.Window})
    @ GLMakie ~/.julia/packages/GLMakie/YAome/src/screen.jl:1038
 [18] renderloop(screen::GLMakie.Screen{GLFW.Window})
    @ GLMakie ~/.julia/packages/GLMakie/YAome/src/screen.jl:1066
 [19] (::GLMakie.var"#79#80"{GLMakie.Screen{GLFW.Window}})()
    @ GLMakie ~/.julia/packages/GLMakie/YAome/src/screen.jl:927

This pull request fixes that.

hbsmith avatar Apr 28 '25 06:04 hbsmith

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 85.23%. Comparing base (8b5b456) to head (b042605). Report is 187 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #1159       +/-   ##
===========================================
+ Coverage   70.12%   85.23%   +15.10%     
===========================================
  Files          42       38        -4     
  Lines        2718     2614      -104     
===========================================
+ Hits         1906     2228      +322     
+ Misses        812      386      -426     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov-commenter avatar Apr 28 '25 07:04 codecov-commenter

@hbsmith can you put a note in the dev docs and then we merge this? or is anything else missing?

Datseris avatar May 20 '25 07:05 Datseris

It's not clear to me what you want me to write or where exactly in the dev docs you want it to go. If you tell me what you want exactly, I can add it.

hbsmith avatar May 20 '25 07:05 hbsmith

I don't understand the error and how this PR solves it. The positions in the flocking model are 2D. If you make a tuple out of them it is still 2D. What is the point if accessing them with 1:length(...)? Which is also a very expensive operation, we need to find a more performant way to do this.

Datseris avatar May 20 '25 08:05 Datseris

if I remember correctly the problem is that pos contains some more dimensions than what you would expect for some reason. So I trust that this commit fixes the problem. I would specialize the function for ContinuousSpace though for safety.

Tortar avatar May 28 '25 21:05 Tortar