-
Notifications
You must be signed in to change notification settings - Fork 125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
abmplot
fails when 1) number of agents changes over time and 2) custom agent_size
function is used
#1035
Comments
abmplot
fails when 1) number of agents changes over time and 2) custom agent_size function is usedabmplot
fails when 1) number of agents changes over time and 2) custom agent_size
function is used
since you are using Agents 6.0 syntax, you have a old GLMakie version installed somehow probably |
Thank you for your quick response. I also use GLMakie v0.9.11 (and Makie v0.20.10 if this is relevant). |
ah no sorry, didn't see that in your reproducer, yes the error is there also for me :-) |
I investigated a bit the issue and I'm unsure of what could be the cause because this works instead: using Agents, GLMakie
@agent struct Particle(ContinuousAgent{2,Float64}) end
function build_model()
space = ContinuousSpace((1, 1))
model = StandardABM(Particle, space; agent_step! = particle_step!)
for i = 1:100
pos = Tuple(rand(2))
vel = Tuple(rand(2))
add_agent!(Particle(i, pos, vel), model)
end
return model
end
function particle_step!(p, model)
randomwalk!(p, model, 1.0)
if rand() < 0.5
remove_agent!(p, model) # Number of agents changes here.
end
end
p_color(p::Particle) = :red
model = build_model()
figure, ax, abmobs = abmplot(model; add_controls = true, agent_color = p_color)
resize!(figure, 1200, 800)
wait(display(figure)) which is strange because internally |
I am trying to find where in the source code the UPDATE of plotted agents is done. I cannot, unfortunately. If you can find where the update of the scatterplot is done in the source code please paste a link here and I ll have a look. |
Thanks again for your analysis. I am not sure if I can help but I looked in the sources and found in
and before
|
Hm, I do not know the solution. I thought it was a concurrency issue with makie. Where one vector (eg size) is updated and a plot update is triggered before the other vector (e.g. color) is updated, leading to missmatch of dimensions. However, normally the plotting code works fine with agents being deleted, we have many examples and animations where this occur and agents have different colors. What is fundamentally different in this MWE that makes it not work...? Is it really that the size is different? If we run e.g., the rock paper scissors example where each type also has different size, would we get the same error...? |
yes, tried, we get the same error. I think I found a github issue and PR which describe the culprit of this: MakieOrg/Makie.jl#3658 and JuliaGizmos/Observables.jl#109 |
great, thanks! |
We can also solve this on our side by the way. Instead of using
it is actually super sipmle to change the source code to do that. |
I wanted to provide an update and also say thanks for all the great work on Agents.jl! As a newcomer to Julia, primarily interested in simulation development, I've found it to be an invaluable tool. My goal is to visualize a simulation where both the number of agents and their visual attributes (position, size, color, and marker) change dynamically at each step. Using the Minimum Working Example (MWE) provided earlier in this thread as a starting point, I've implemented a solution (code follows at the end) that appears to avoid the DimensionMismatch error during runtime. This was achieved by:
While this approach has successfully mitigated the DimensionMismatch error, I've encountered a new challenge: the updates to agent data don't seem to be triggering or reflecting in the visualization during the simulation run. Given these circumstances, I would greatly appreciate more detailed guidelines about the workaround and update mechanism in Agents.jl, particularly in the context of dynamic visualizations. Specifically, a code update you could provide would be immensely helpful. Thank you for your time and continued efforts on this excellent package. using Agents, GLMakie, Random
@agent struct Particle(ContinuousAgent{2,Float64})
size::Float32
color::RGBf
marker::Symbol
end
# Model parameters and constants
const WALK_SPEED = 0.01
const REMOVAL_CHANCE = 0.03
const ADDITION_CHANCE = 0.01
const GROWTH_RATE = 0.3
const MAX_SIZE = 30f0
mutable struct ModelParams
n_particles::Int
end
function build_model(params::ModelParams)
space = ContinuousSpace((1, 1))
model = StandardABM(Particle, space; agent_step! = particle_step!, properties = params)
for i in 1:params.n_particles
add_particle!(model)
end
model
end
function add_particle!(model)
color = RGBAf(rand(), rand(), rand(), 0.7)
marker = rand([:circle, :rect, :star5])
add_agent!(
pos = Tuple(rand(2)), # position
model;
vel = Tuple(rand(2)), # velocity
size = 10f0, # initial size
color = color,
marker = marker)
end
function particle_step!(p, model)
randomwalk!(p, model, WALK_SPEED)
p.size = min(p.size * (1 + GROWTH_RATE), MAX_SIZE)
if rand(abmrng(model)) < REMOVAL_CHANCE
remove_agent!(p, model)
end
if rand(abmrng(model)) < ADDITION_CHANCE
add_particle!(model)
end
end
function agent_data(model)
sorted_agents = sort(collect(allagents(model)), by = a -> a.id)
Dict(
:pos => [Point2f(a.pos) for a in sorted_agents],
:size => [a.size for a in sorted_agents],
:color => [a.color for a in sorted_agents],
:marker => [a.marker for a in sorted_agents]
)
end
function run_simulation(; n_particles=100)
params = ModelParams(n_particles)
model = build_model(params)
abmobs = ABMObservable(model)
data_obs = Observable(agent_data(model))
pos_obs = @lift($(data_obs)[:pos])
size_obs = @lift($(data_obs)[:size])
color_obs = @lift($(data_obs)[:color])
marker_obs = @lift($(data_obs)[:marker])
fig, ax, abmplot_output = abmplot(
model;
agent_pos = pos_obs,
agent_size = size_obs,
agent_color = color_obs,
agent_marker = marker_obs,
add_controls = true
)
on(abmobs.model) do m
data_obs[] = agent_data(m)
end
resize!(fig, 1200, 800)
display(fig)
return fig, abmobs, data_obs
end
# Run the simulation
fig, abmobs, data_obs = run_simulation() |
Thanks for your kind words! Why don't you try what I said here: #1035 (comment) ? This needs to be done directly at the source code. I believe this will be the solution! |
Thank you so much for your immediate reply! I appreciate your suggestion, and I want to clarify that it was actually your advice that I tried to implement first. However, despite attempting various approaches, I wasn't able to completely avoid the DimensionMismatch error. As a newcomer to Julia, I found it challenging to implement the solution correctly, which is why I ended up with the version I shared in my previous comment. Given my inexperience, I would greatly appreciate if you could provide a more detailed example of how to implement this solution correctly. Specifically, I'm unsure about:
Any additional guidance or a small code snippet demonstrating this approach would be immensely helpful. Thank you again for your patience and support! |
Right, I am actually on holiday so I'll come back here at a later time with more ! |
While waiting for your return and reply, I've conducted a series of progressive tests to isolate the error. Here's a summary of my findings:
using Agents
using GLMakie
using Random
using Statistics
@agent struct Particle(ContinuousAgent{2,Float64})
size::Float64
color::RGBAf
marker::Symbol
energy::Float64
remove::Bool # New field to mark for removal
end
function initialize_model(n_particles)
space = ContinuousSpace((1.0, 1.0), periodic = true)
Random.seed!(50)
model = StandardABM(Particle, space;
properties = Dict(
:step => 0,
:interaction_radius => 0.05,
:energy_transfer => 0.1
),
agent_step! = particle_step!,
model_step! = model_step!
)
for _ in 1:n_particles
add_agent!(
pos = Tuple(rand(2)), # position
model;
vel = (rand() - 0.5, rand() - 0.5) .* 0.01, # Reduced velocity
size = rand(5.0:15.0), # size
color = RGBAf(rand(), rand(), rand(), 0.8),
marker = rand([:circle, :rect, :star5]),
energy = rand(50.0:100.0), # energy
remove = false # Initialize as not marked for removal
)
end
return model
end
function particle_step!(particle, model)
# Update position manually
new_pos = particle.pos .+ particle.vel
new_pos = mod.(new_pos, 1) # Wrap around the unit square
particle.pos = new_pos
# Make size changes more noticeable
particle.size = 5.0 + (particle.energy / 10.0)
particle.size = clamp(particle.size, 5.0, 20.0)
particle.energy -= 0.5 # Increased energy loss
nearby_particles = nearby_agents(particle, model, model.interaction_radius)
for nearby in nearby_particles
if nearby.energy > particle.energy
energy_transfer = model.energy_transfer * (nearby.energy - particle.energy)
particle.energy += energy_transfer
nearby.energy -= energy_transfer
end
end
if particle.energy < 10.0 && rand() < 0.05 # Increased removal probability
println("Marking agent $(particle.id) for removal. Current energy: $(particle.energy)")
particle.remove = true # Mark for removal instead of removing immediately
end
end
function model_step!(model)
println("Start of model_step!. Step: $(model.step), Agents: $(nagents(model))")
# Remove marked agents
for agent in allagents(model)
if agent.remove
println("Removing marked agent $(agent.id)")
remove_agent!(agent, model)
end
end
# Add new agents (as before)
if rand() < 0.1 # Increased addition probability
new_agent = add_agent!(
pos = Tuple(rand(2)), # position
model;
vel = (rand() - 0.5, rand() - 0.5) .* 0.01, # Reduced velocity
size = rand(5.0:15.0), # size
color = RGBAf(rand(), rand(), rand(), 0.8),
marker = rand([:circle, :rect, :star5]),
energy = rand(50.0:100.0), # energy
remove = false # Initialize as not marked for removal
)
println("Added new agent $(new_agent.id)")
end
model.step += 1
println("End of model_step!. Step: $(model.step), Agents: $(nagents(model))")
end
function run_simulation(; n_particles=100)
model = initialize_model(n_particles)
fig = Figure(size = (1000, 800))
ax = Axis(fig[1, 1], aspect = DataAspect())
xlims!(ax, 0, 1)
ylims!(ax, 0, 1)
function update_plot!(model, ax)
empty!(ax) # Clear the existing plot
agents = collect(allagents(model))
n = length(agents)
positions = [Point2f(a.pos) for a in agents]
sizes = [Float32(a.size) for a in agents]
colors = [a.color for a in agents]
markers = [a.marker for a in agents]
# Create a new scatter plot
scatter!(ax, positions, color = colors, markersize = sizes, marker = markers)
println("Updated plot. Agents: $(n), Step: $(model.step)")
end
update_plot!(model, ax)
button = Button(fig[2, 1], label = "Step")
on(button.clicks) do _
println("Step button clicked")
Agents.step!(model, 1)
update_plot!(model, ax)
println("Step completed")
end
# Ensure the plot takes up most of the figure
colsize!(fig.layout, 1, Relative(1.0))
rowsize!(fig.layout, 1, Relative(0.9))
display(fig)
return fig, model
end
# Run the simulation
fig, model = run_simulation()
function run_simulation(; n_particles=100)
model = initialize_model(n_particles)
fig = Figure(size = (1000, 800))
ax = Axis(fig[1, 1], aspect = DataAspect())
xlims!(ax, 0, 1)
ylims!(ax, 0, 1)
# Initial plot data
positions = [Point2f(a.pos) for a in allagents(model)]
sizes = [Float32(a.size) for a in allagents(model)]
colors = [a.color for a in allagents(model)]
markers = [a.marker for a in allagents(model)]
# Create the scatter plot
scatter_plot = scatter!(ax, positions, color = colors, markersize = sizes, marker = markers)
function update_plot!(model, scatter_plot)
try
agents = collect(allagents(model)) # Collect agents to ensure consistent iteration
n = length(agents)
new_positions = Vector{Point2f}(undef, n)
new_sizes = Vector{Float32}(undef, n)
new_colors = Vector{RGBAf}(undef, n)
new_markers = Vector{Symbol}(undef, n)
for (i, a) in enumerate(agents)
new_positions[i] = Point2f(a.pos)
new_sizes[i] = Float32(a.size)
new_colors[i] = a.color
new_markers[i] = a.marker
end
# Update all plot properties at once
scatter_plot[1] = new_positions
scatter_plot.color = new_colors
scatter_plot.markersize = new_sizes
scatter_plot.marker = new_markers
println("Updated plot. Agents: $(n), Step: $(model.step)")
catch e
println("Error in update_plot!: ", e)
for (arr_name, arr) in zip(["positions", "sizes", "colors", "markers"],
[new_positions, new_sizes, new_colors, new_markers])
println("$arr_name length: $(length(arr))")
end
rethrow(e)
end
end
update_plot!(model, scatter_plot)
button = Button(fig[2, 1], label = "Step")
on(button.clicks) do _
println("Step button clicked")
Agents.step!(model, 1)
update_plot!(model, scatter_plot)
println("Step completed")
end
# Ensure the plot takes up most of the figure
colsize!(fig.layout, 1, Relative(1.0))
rowsize!(fig.layout, 1, Relative(0.9))
display(fig)
return fig, model, scatter_plot
end
These findings suggest that the DimensionMismatch error is related to how the scatter plot is updated when the number of agents changes. The error in I hope this information helps in diagnosing and resolving these issues. Moreover, the MWE code can be used as a testbed for further experimentation. |
I don't have much time at the moment to further analyze the situation but I think this issue is pretty similar: MakieOrg/Makie.jl#2441, I think that you can understand from here what can be changed in Agents, thanks for the MWEs! |
Thank you for your reply and for taking the time to respond. I appreciate the link to the Makie.jl issue #2441, which does indeed seem related. Given your expertise with Agents.jl, your insights on how to best implement the solution proposed in the Makie.jl issue within the Agents.jl framework would be extremely valuable. Specifically, I'm unsure how to properly synchronize the updates of multiple observables in the context of an agent-based model where the number of agents can change dynamically. Furthermore, during these tests and as I mentioned above (please see my previous message Agents.jl/issues/1035#issuecomment-2321529956, points 3 and 4) I think I have come across another issue with Agents.jl, specifically in the |
I have just taken a look at |
Thank you for your reply as well as for your actions (#1073). I appreciate your attention to this matter. I'd like to provide an update on the current issue: I've developed a version of function run_simulation(; n_particles=100)
model = initialize_model(n_particles)
model_obs = Observable(model)
fig = Figure(size = (1000, 800))
ax = Axis(fig[1, 1], aspect = DataAspect())
xlims!(ax, 0, 1)
ylims!(ax, 0, 1)
# Create observables for plot data
pos_obs = Observable(Point2f[])
size_obs = Observable(Float32[])
color_obs = Observable(RGBAf[])
marker_obs = Observable(Symbol[])
function update_observables(ax, model)
empty!(ax) # Clear the existing plot
agents = collect(allagents(model))
pos_obs.val = [Point2f(a.pos) for a in agents]
size_obs.val = [Float32(a.size) for a in agents]
color_obs.val = [a.color for a in agents]
marker_obs[] = [a.marker for a in agents] # This will trigger the update
end
# Initial update
update_observables(ax, model)
# Create the scatter plot with observables
# Either of the following commands run without error
# @lift(scatter!(ax, $pos_obs, color = $color_obs, markersize = $size_obs, marker = $marker_obs))
@lift(abmplot!(ax, model; positions = $pos_obs, agent_color = $color_obs, agent_size = $size_obs, agent_marker = $marker_obs))
on(model_obs) do m
update_observables(ax, m)
end
button = Button(fig[2, 1], label = "Step")
on(button.clicks) do _
println("Step button clicked")
Agents.step!(model_obs[], 1)
model_obs[] = model_obs[] # Trigger observable update
println("Step completed")
end
# Ensure the plot takes up most of the figure
colsize!(fig.layout, 1, Relative(1.0))
rowsize!(fig.layout, 1, Relative(0.9))
display(fig)
return fig
end
|
Describe the bug
I have a simulation where I remove agents by chance (by
remove_agent!
). I also define a custom function (p_size
) for an agent's size provided toabmplot
. When I run the simulation (see MWE below), an exception is thrown:See also full stack trace at the end.
What am I doing wrong?
PS: Thank you very much for the
Agents.jl
package! I am really happy to use it.Minimal Working Example
Here's the example. Please note that the example works when the custom agent_size function
p_size
is not used -- see comments at the end of the example.Agents.jl version
Agents v6.0.12 with Julia 1.10.2 and Linux Ubuntu 22.04.
Full stack trace
The text was updated successfully, but these errors were encountered: