From 1523c80b220c0f583201c9b958ad4cc3b5c2c4f7 Mon Sep 17 00:00:00 2001 From: Simon Frost Date: Mon, 30 Mar 2026 09:45:11 -0700 Subject: [PATCH 1/2] Fix maxevent handling with finite maxtime (cherry picked from commit 376ee1ceb6cda778a9be3b1bf3539f30929d4572) --- src/ABMs.jl | 3 +-- test/ABMs.jl | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ABMs.jl b/src/ABMs.jl index 98b2235..10dd59c 100644 --- a/src/ABMs.jl +++ b/src/ABMs.jl @@ -486,7 +486,6 @@ end function run!(abm::ABM, rt::RuntimeABM, output::Traj; save=_->nothing, maxevent=MAXEVENT, maxtime=Inf, dt=0.1) - maxevent = isinf(maxtime) ? maxevent : typemax(Int) # Helper functions that automatically incorporate the runtime `rt` getname(rule::Int)::String = string(isnothing(abm.rules[rule].name) ? rule : abm.rules[rule].name) @@ -606,4 +605,4 @@ function run!(abm::ABM, rt::RuntimeABM, output::Traj; return output end -end # module \ No newline at end of file +end # module diff --git a/test/ABMs.jl b/test/ABMs.jl index edf95f8..1ea9ece 100644 --- a/test/ABMs.jl +++ b/test/ABMs.jl @@ -80,6 +80,11 @@ traj = run!(ABM([rem_edge]), G); traj = run!(ABM([add_loop]), G); @test length(traj) > 3 # after we add a loop, the match persists + is resampled +let traj = run!(ABM([create_loop]), Graph(); maxevent=2, maxtime=10.0) + @test length(traj) == 2 + @test traj.events[end][1] == 2.0 +end + # Test events in parallel From 8e6dbee6962e0291e45fcd13405e9f6975278758 Mon Sep 17 00:00:00 2001 From: Simon Frost Date: Mon, 13 Apr 2026 15:53:29 -0700 Subject: [PATCH 2/2] Require explicit run bounds by default Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/literate/game_of_life.jl | 2 +- src/ABMs.jl | 8 +++++++- test/ABMs.jl | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/literate/game_of_life.jl b/docs/literate/game_of_life.jl index f7b3cc4..91c6b3e 100644 --- a/docs/literate/game_of_life.jl +++ b/docs/literate/game_of_life.jl @@ -416,7 +416,7 @@ match_coords(birth, G) # This is easy! Pass in our model and our initial state. We optionally could # limit the run via some maximum number of steps or time, but this one will # achieve steady state within 3 time steps. -res = run!(GoL_coords, G); +res = run!(GoL_coords, G; maxevent=100); @test length(res) == 13 # ## View results diff --git a/src/ABMs.jl b/src/ABMs.jl index 10dd59c..5e378a2 100644 --- a/src/ABMs.jl +++ b/src/ABMs.jl @@ -470,7 +470,7 @@ Base.isempty(t::Traj) = isempty(t.events) Base.length(t::Traj) = length(t.events) -const MAXEVENT = 100 +const MAXEVENT = typemax(Int) """ Run an ABM, creating a fresh runtime + trajectory. @@ -480,12 +480,18 @@ dt - timestep for checking discrete events when running ODE dynamics. """ function run!(abm::ABM, init::T; save=_->nothing, maxevent=MAXEVENT, maxtime=Inf, kw...) where T<:ACSet + maxevent == typemax(Int) && isinf(maxtime) && error( + "run! requires at least one finite bound; specify maxevent and/or maxtime." + ) run!(abm::ABM, RuntimeABM(abm, init; kw...), Traj(init); save, maxtime, maxevent) end function run!(abm::ABM, rt::RuntimeABM, output::Traj; save=_->nothing, maxevent=MAXEVENT, maxtime=Inf, dt=0.1) + maxevent == typemax(Int) && isinf(maxtime) && error( + "run! requires at least one finite bound; specify maxevent and/or maxtime." + ) # Helper functions that automatically incorporate the runtime `rt` getname(rule::Int)::String = string(isnothing(abm.rules[rule].name) ? rule : abm.rules[rule].name) diff --git a/test/ABMs.jl b/test/ABMs.jl index 1ea9ece..0ffc2dc 100644 --- a/test/ABMs.jl +++ b/test/ABMs.jl @@ -73,11 +73,11 @@ push!(new_abm, do_nothing) traj = run!(abm, G; maxevent=10); -traj = run!(ABM([rem_edge]), G); +traj = run!(ABM([rem_edge]), G; maxevent=10); @test length(traj) == 3 -traj = run!(ABM([add_loop]), G); +traj = run!(ABM([add_loop]), G; maxevent=10); @test length(traj) > 3 # after we add a loop, the match persists + is resampled let traj = run!(ABM([create_loop]), Graph(); maxevent=2, maxtime=10.0) @@ -85,6 +85,16 @@ let traj = run!(ABM([create_loop]), Graph(); maxevent=2, maxtime=10.0) @test traj.events[end][1] == 2.0 end +let err = try + run!(ABM([create_loop]), Graph()) + nothing + catch e + e + end + @test err isa ErrorException + @test occursin("specify maxevent and/or maxtime", sprint(showerror, err)) +end + # Test events in parallel