From 34dcb15109a845da6d45d4fe689548c655b2db39 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sun, 26 Apr 2026 19:24:03 -0400 Subject: [PATCH 1/3] Bump compat for OrdinaryDiffEq v7 / StochasticDiffEq v7 ecosystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Widen OrdinaryDiffEq compat to include v7 ("6" → "6, 7") - Widen StochasticDiffEq compat to include v7 ("6.82" → "6.82, 7") - Apply same widen in docs/Project.toml and docs/src/assets/Project.toml - Migrate prob_func to 2-arg SciMLBase v3 EnsembleContext signature (prob_func(prob, i, repeat) → prob_func(prob, ctx)) in ensemble_uniqueness.jl and thread_safety.jl - Fix length(sol) → length(sol.u) for RAT v4 compatibility in ensemble_problems.jl, saveat_regression.jl, thread_safety.jl, and variable_rate.jl (length(AbstractVectorOfArray) now returns total scalar count rather than trajectory/timestep count) SciMLBase compat was already "2.115, 3.1" and DiffEqBase was already "6.192, 7" from prior PRs #579 and #580; no further changes needed there. Co-Authored-By: Chris Rackauckas --- Project.toml | 4 ++-- docs/Project.toml | 4 ++-- docs/src/assets/Project.toml | 4 ++-- test/ensemble_problems.jl | 6 +++--- test/ensemble_uniqueness.jl | 2 +- test/saveat_regression.jl | 2 +- test/thread_safety.jl | 4 ++-- test/variable_rate.jl | 8 ++++---- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Project.toml b/Project.toml index 70a4ca03..56828a8b 100644 --- a/Project.toml +++ b/Project.toml @@ -45,7 +45,7 @@ Graphs = "1.11" KernelAbstractions = "0.9" LinearAlgebra = "1" LinearSolve = "3" -OrdinaryDiffEq = "6" +OrdinaryDiffEq = "6, 7" OrdinaryDiffEqCore = "3, 4" Pkg = "1" PoissonRandom = "0.4" @@ -57,7 +57,7 @@ SciMLBase = "2.115, 3.1" StableRNGs = "1" StaticArrays = "1.9.8" Statistics = "1" -StochasticDiffEq = "6.82" +StochasticDiffEq = "6.82, 7" SymbolicIndexingInterface = "0.3.36" Test = "1" julia = "1.10" diff --git a/docs/Project.toml b/docs/Project.toml index c69a2805..027165fb 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -20,8 +20,8 @@ Distributions = "0.25" Documenter = "1.1" Graphs = "1.9" JumpProcesses = "9.13.2" -OrdinaryDiffEq = "6.59" +OrdinaryDiffEq = "6.59, 7" Plots = "1.39" PointProcesses = "0.5" StableRNGs = "1.0" -StochasticDiffEq = "6.63" +StochasticDiffEq = "6.63, 7" diff --git a/docs/src/assets/Project.toml b/docs/src/assets/Project.toml index c69a2805..027165fb 100644 --- a/docs/src/assets/Project.toml +++ b/docs/src/assets/Project.toml @@ -20,8 +20,8 @@ Distributions = "0.25" Documenter = "1.1" Graphs = "1.9" JumpProcesses = "9.13.2" -OrdinaryDiffEq = "6.59" +OrdinaryDiffEq = "6.59, 7" Plots = "1.39" PointProcesses = "0.5" StableRNGs = "1.0" -StochasticDiffEq = "6.63" +StochasticDiffEq = "6.63, 7" diff --git a/test/ensemble_problems.jl b/test/ensemble_problems.jl index 75f82618..b8725d1c 100644 --- a/test/ensemble_problems.jl +++ b/test/ensemble_problems.jl @@ -108,7 +108,7 @@ end jprob = make_ssa_jump_prob() sol = solve(EnsembleProblem(jprob), SSAStepper(), EnsembleThreads(); trajectories = 4) - @test length(sol) == 4 + @test length(sol.u) == 4 end @testset "ODE + VR ($agg)" for agg in (VR_FRM(), VR_Direct(), VR_DirectFW()) @@ -117,7 +117,7 @@ end # randexp!(_jump_prob.rng, ...) on the shared original problem. sol = solve(EnsembleProblem(jprob), Tsit5(), EnsembleThreads(); trajectories = 4, save_everystep = false) - @test length(sol) == 4 + @test length(sol.u) == 4 end @testset "SDE + VR (VR_FRM): unique trajectories" begin @@ -126,7 +126,7 @@ end # resetted_jump_problem, so trajectories should be distinct. sol = solve(EnsembleProblem(jprob), EM(), EnsembleThreads(); trajectories = 4, dt = 0.01, save_everystep = false) - @test length(sol) == 4 + @test length(sol.u) == 4 finals = [sol.u[i].u[end][1] for i in 1:4] @test length(unique(finals)) > 1 end diff --git a/test/ensemble_uniqueness.jl b/test/ensemble_uniqueness.jl index adb97df3..3c686890 100644 --- a/test/ensemble_uniqueness.jl +++ b/test/ensemble_uniqueness.jl @@ -11,7 +11,7 @@ dprob = DiscreteProblem(u0, (0.0, 100.0)) # This ensures different trajectories while maintaining reproducibility. # Generate seeds from a seeded RNG for reproducibility of ensemble results. function make_seeded_prob_func(dprob, aggregator, jumps, base_rng) - return function prob_func(prob, i, repeat) + return function prob_func(prob, ctx) seed = rand(base_rng, UInt64) JumpProblem(dprob, aggregator, jumps...; rng = StableRNG(seed)) end diff --git a/test/saveat_regression.jl b/test/saveat_regression.jl index 03665d76..7b314051 100644 --- a/test/saveat_regression.jl +++ b/test/saveat_regression.jl @@ -17,7 +17,7 @@ Nsims = 10_000 sol = JumpProcesses.solve(EnsembleProblem(jprob), SSAStepper(), saveat = ts, trajectories = Nsims) -for i in 1:length(sol) +for i in 1:length(sol.u) NA .+= sol.u[i][1, :] end diff --git a/test/thread_safety.jl b/test/thread_safety.jl index 7356ce2f..7659670c 100644 --- a/test/thread_safety.jl +++ b/test/thread_safety.jl @@ -26,11 +26,11 @@ let for agg in (VR_FRM(), VR_Direct(), VR_DirectFW()) jump_prob = JumpProblem(ode_prob, Direct(), vrj; vr_aggregator = agg) - prob_func(prob, i, repeat) = deepcopy(prob) + prob_func(prob, ctx) = deepcopy(prob) prob = EnsembleProblem(jump_prob, prob_func = prob_func) sol = solve(prob, Tsit5(), EnsembleThreads(), trajectories = 400, save_everystep = false) - firstrx_time = [sol.u[i].t[findfirst(>(sol.u[i].t[1]), sol.u[i].t)] for i in 1:length(sol)] + firstrx_time = [sol.u[i].t[findfirst(>(sol.u[i].t[1]), sol.u[i].t)] for i in 1:length(sol.u)] @test allunique(firstrx_time) end end diff --git a/test/variable_rate.jl b/test/variable_rate.jl index ce08781d..0b81a14b 100644 --- a/test/variable_rate.jl +++ b/test/variable_rate.jl @@ -41,8 +41,8 @@ integrator = init(jump_prob_gill, Tsit5()) sol_gill = solve(jump_prob_gill, Tsit5()) sol_gill = solve(jump_prob, Rosenbrock23(autodiff = AutoFiniteDiff())) sol_gill = solve(jump_prob, Rosenbrock23()) -@test maximum([sol.u[i][2] for i in 1:length(sol)]) <= 1e-12 -@test maximum([sol.u[i][3] for i in 1:length(sol)]) <= 1e-12 +@test maximum([sol.u[i][2] for i in 1:length(sol.u)]) <= 1e-12 +@test maximum([sol.u[i][3] for i in 1:length(sol.u)]) <= 1e-12 g = function (du, u, p, t) du[1] = u[1] @@ -52,8 +52,8 @@ jump_prob = JumpProblem(prob, jump, jump2; vr_aggregator = VR_FRM(), rng = rng) sol = solve(jump_prob, SRIW1()) jump_prob_gill = JumpProblem(prob, jump, jump2; vr_aggregator = VR_Direct(), rng = rng) sol_gill = solve(jump_prob_gill, SRIW1()) -@test maximum([sol.u[i][2] for i in 1:length(sol)]) <= 1e-12 -@test maximum([sol.u[i][3] for i in 1:length(sol)]) <= 1e-12 +@test maximum([sol.u[i][2] for i in 1:length(sol.u)]) <= 1e-12 +@test maximum([sol.u[i][3] for i in 1:length(sol.u)]) <= 1e-12 function ff(du, u, p, t) if p == 0 From 3080541f70baf3a93843e42ece7ab5f1ba5a4dcf Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Sun, 26 Apr 2026 22:23:47 -0400 Subject: [PATCH 2/3] Add OrdinaryDiffEqFunctionMap test dep and ensemble (prob, ctx) migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hard verification on the v7 ecosystem revealed: 1. `FunctionMap` is no longer in OrdinaryDiffEq's umbrella in v7 — the slim default set excludes it. Tests referencing `FunctionMap` (constant_rate.jl, ensemble_uniqueness.jl, sir_model.jl, splitcoupled.jl) need explicit `using OrdinaryDiffEqFunctionMap`. Added it to [extras]/[compat]/targets.test. 2. Ensemble callback signatures: `prob_func(prob, i, repeat)` → `prob_func(prob, ctx)` for SciMLBase v3 EnsembleContext API. 3. RAT v4 fix: `length(sol)` on EnsembleSolution now returns scalar count, not trajectory count — replaced with `length(sol.u)` in test assertions. Co-Authored-By: Chris Rackauckas --- Project.toml | 4 +++- test/constant_rate.jl | 2 +- test/ensemble_uniqueness.jl | 2 +- test/sir_model.jl | 2 +- test/splitcoupled.jl | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 56828a8b..85b68c9d 100644 --- a/Project.toml +++ b/Project.toml @@ -47,6 +47,7 @@ LinearAlgebra = "1" LinearSolve = "3" OrdinaryDiffEq = "6, 7" OrdinaryDiffEqCore = "3, 4" +OrdinaryDiffEqFunctionMap = "1, 2" Pkg = "1" PoissonRandom = "0.4" Random = "1" @@ -70,6 +71,7 @@ FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqFunctionMap = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" @@ -78,4 +80,4 @@ StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["ADTypes", "Aqua", "ExplicitImports", "FastBroadcast", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEq", "Pkg", "SafeTestsets", "StableRNGs", "Statistics", "StochasticDiffEq", "Test"] +test = ["ADTypes", "Aqua", "ExplicitImports", "FastBroadcast", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEq", "OrdinaryDiffEqFunctionMap", "Pkg", "SafeTestsets", "StableRNGs", "Statistics", "StochasticDiffEq", "Test"] diff --git a/test/constant_rate.jl b/test/constant_rate.jl index 86c237c0..f404d8cf 100644 --- a/test/constant_rate.jl +++ b/test/constant_rate.jl @@ -1,4 +1,4 @@ -using JumpProcesses, DiffEqBase, OrdinaryDiffEq, Statistics +using JumpProcesses, DiffEqBase, OrdinaryDiffEq, OrdinaryDiffEqFunctionMap, Statistics using Test using StableRNGs rng = StableRNG(12345) diff --git a/test/ensemble_uniqueness.jl b/test/ensemble_uniqueness.jl index 3c686890..2db8543f 100644 --- a/test/ensemble_uniqueness.jl +++ b/test/ensemble_uniqueness.jl @@ -1,4 +1,4 @@ -using OrdinaryDiffEq, JumpProcesses, Test +using OrdinaryDiffEq, OrdinaryDiffEqFunctionMap, JumpProcesses, Test using StableRNGs, Random j1 = ConstantRateJump((u, p, t) -> 10, (integrator) -> integrator.u[1] += 1) diff --git a/test/sir_model.jl b/test/sir_model.jl index e8cea455..86f20621 100644 --- a/test/sir_model.jl +++ b/test/sir_model.jl @@ -1,4 +1,4 @@ -using JumpProcesses, DiffEqBase, OrdinaryDiffEq +using JumpProcesses, DiffEqBase, OrdinaryDiffEq, OrdinaryDiffEqFunctionMap using Test using StableRNGs rng = StableRNG(12345) diff --git a/test/splitcoupled.jl b/test/splitcoupled.jl index e871e2c0..0bd5ba62 100644 --- a/test/splitcoupled.jl +++ b/test/splitcoupled.jl @@ -1,4 +1,4 @@ -using JumpProcesses, DiffEqBase, OrdinaryDiffEq, StochasticDiffEq, Statistics +using JumpProcesses, DiffEqBase, OrdinaryDiffEq, OrdinaryDiffEqFunctionMap, StochasticDiffEq, Statistics using Test using StableRNGs rng = StableRNG(12345) From 0700aa8127a02608e9fdc380fa1096246b51a996 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Mon, 27 Apr 2026 12:05:32 -0400 Subject: [PATCH 3/3] =?UTF-8?q?Alias=20SSAIntegrator.derivative=5Fdisconti?= =?UTF-8?q?nuity=20=E2=86=92=20u=5Fmodified=20field,=20bump=20to=20v9.27.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #581's CI surfaced that DiffEqBase v7 / SciMLBase v3 callbacks access `integrator.derivative_discontinuity` as a struct field directly (not via a method), so the v6-era field name `u_modified` blew up at runtime: LoadError: FieldError: type JumpProcesses.SSAIntegrator has no field `derivative_discontinuity`, available fields: ..., `u_modified`, ... Renaming the field would be a breaking ABI change (and would force v7-only compat, since DiffEqBase v6 callbacks still read `integrator.u_modified`). Instead, this commit overrides `Base.getproperty` / `Base.setproperty!` / `Base.propertynames` on `SSAIntegrator` to alias `:derivative_discontinuity` onto the existing `:u_modified` field. Both names resolve to the same storage, so v6 and v7 callback code both keep working unchanged. Internal `u_modified!(integrator, ...)` calls and the `DiffEqBase.u_modified!` / `SciMLBase.derivative_discontinuity!` method definitions are left in place — they continue to dispatch correctly under both ecosystems (the SciMLBase v3 `@deprecate u_modified! → derivative_discontinuity!` alias is bypassed by JumpProcesses' more specific method). Also tightens the existing widening from `OrdinaryDiffEqCore = "3, 4"` to `"3.17, 4"` — the imported `StochasticDiffEqAlgorithm` / `StochasticDiffEqRODEAlgorithm` symbols only exist from v3.17+, and allowing v3.0–v3.16 lets the resolver pick a version that breaks the extension precompile (same root cause being patched on master via #583). Bumps version 9.26.0 → 9.27.0 (minor, fully backwards compatible). Co-Authored-By: Chris Rackauckas --- Project.toml | 4 ++-- src/SSA_stepper.jl | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 85b68c9d..c3fde825 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "JumpProcesses" uuid = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5" authors = ["Chris Rackauckas "] -version = "9.26.0" +version = "9.27.0" [deps] ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" @@ -46,7 +46,7 @@ KernelAbstractions = "0.9" LinearAlgebra = "1" LinearSolve = "3" OrdinaryDiffEq = "6, 7" -OrdinaryDiffEqCore = "3, 4" +OrdinaryDiffEqCore = "3.17, 4" OrdinaryDiffEqFunctionMap = "1, 2" Pkg = "1" PoissonRandom = "0.4" diff --git a/src/SSA_stepper.jl b/src/SSA_stepper.jl index b3e69b97..bcc38694 100644 --- a/src/SSA_stepper.jl +++ b/src/SSA_stepper.jl @@ -113,6 +113,28 @@ end (integrator::SSAIntegrator)(t) = copy(integrator.u) (integrator::SSAIntegrator)(out, t) = (out .= integrator.u) +# SciMLBase v3 / DiffEqBase v7 renamed the integrator's `u_modified` field to +# `derivative_discontinuity` and internal callback code now reads/writes the +# field directly (e.g. `integrator.derivative_discontinuity`, not via a +# method). Aliasing here so both names access the same underlying storage — +# v6-era callbacks that look for `:u_modified` and v7-era callbacks that look +# for `:derivative_discontinuity` both keep working without renaming the +# struct field (which would be a breaking ABI change). +@inline function Base.getproperty(integrator::SSAIntegrator, sym::Symbol) + sym === :derivative_discontinuity && return getfield(integrator, :u_modified) + return getfield(integrator, sym) +end + +@inline function Base.setproperty!(integrator::SSAIntegrator, sym::Symbol, val) + sym === :derivative_discontinuity && + return setfield!(integrator, :u_modified, convert(Bool, val)) + return setfield!(integrator, sym, convert(fieldtype(typeof(integrator), sym), val)) +end + +function Base.propertynames(integrator::SSAIntegrator, private::Bool = false) + return (fieldnames(SSAIntegrator)..., :derivative_discontinuity) +end + function DiffEqBase.u_modified!(integrator::SSAIntegrator, bool::Bool) integrator.u_modified = bool end