Skip to content

Commit 69fedc0

Browse files
authored
Merge pull request #680 from isaacsas/integration/v10-master-2026-09-22
JumpProcess v10 Attempt 2
2 parents 1816ee4 + eea3935 commit 69fedc0

114 files changed

Lines changed: 6533 additions & 1797 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/Downgrade.yml‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ on:
1313
jobs:
1414
downgrade:
1515
name: "Downgrade"
16-
# Runs strict (allow_reresolve=false); expected RED until the upstream fix
17-
# ForwardDiff/LogExpFunctions — red until ForwardDiff 1.3.4 (#811). Auto-greens on upstream release.
16+
# The downgrade action discards URL sources. Restore this job after the
17+
# StochasticDiffEqCore branch for JumpProcesses 10 is released and the
18+
# temporary pin is removed.
19+
if: ${{ false }}
1820
uses: "SciML/.github/.github/workflows/downgrade.yml@v1"
21+
with:
22+
group: All
1923
secrets: "inherit"

‎.github/workflows/ThreadSafety.yml‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: "Thread Safety Tests"
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
paths-ignore:
8+
- 'docs/**'
9+
pull_request:
10+
branches:
11+
- master
12+
paths-ignore:
13+
- 'docs/**'
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: ${{ github.ref_name != github.event.repository.default_branch || github.ref != 'refs/tags/v*' }}
18+
19+
jobs:
20+
thread-safety:
21+
name: "Thread Safety"
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
version:
26+
# Restore "lts" when the temporary StochasticDiffEq pins are removed: Julia 1.10
27+
# ignores [sources], and registered StochasticDiffEq packages do not admit
28+
# JumpProcesses 10, so the test environment cannot resolve.
29+
- "1"
30+
- "pre"
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v7
34+
35+
- name: "Setup Julia ${{ matrix.version }}"
36+
uses: julia-actions/setup-julia@v3
37+
with:
38+
version: "${{ matrix.version }}"
39+
40+
- name: "Cache Julia packages"
41+
uses: julia-actions/cache@v2
42+
with:
43+
token: "${{ secrets.GITHUB_TOKEN }}"
44+
45+
- name: "Build package"
46+
uses: julia-actions/julia-buildpkg@v1
47+
48+
- name: "Run thread safety tests (4 threads)"
49+
run: |
50+
julia --threads=4 --code-coverage=user --check-bounds=yes --compiled-modules=yes \
51+
--project=@. --color=yes -e '
52+
using Pkg
53+
Pkg.test()
54+
'
55+
env:
56+
GROUP: ThreadSafety
57+
58+
- name: "Process Coverage"
59+
uses: julia-actions/julia-processcoverage@v1
60+
61+
- name: "Report Coverage"
62+
uses: codecov/codecov-action@v7
63+
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
64+
with:
65+
files: lcov.info
66+
token: "${{ secrets.CODECOV_TOKEN }}"
67+
fail_ci_if_error: false

‎HISTORY.md‎

Lines changed: 176 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,178 @@
11
# Breaking updates and feature summaries across releases
22

3-
## JumpProcesses unreleased (master branch)
3+
## 10.0 (Breaking)
4+
5+
JumpProcesses 10.0 is a transitional release. StochasticDiffEq, ModelingToolkit, and
6+
Catalyst do not yet support it, so environments that include them keep JumpProcesses
7+
9.x until their updates, and JumpProcesses 10.0.1, are released. Jump problems coupled
8+
to delay differential equations (DDEs) are not supported in JumpProcesses 10.0 at this
9+
time.
10+
11+
- **Breaking**: The `rng` keyword argument has been removed from
12+
`JumpProblem`. Pass `rng` to `solve` or `init` instead:
13+
```julia
14+
# Before (no longer works):
15+
jprob = JumpProblem(dprob, Direct(), jump; rng = Xoshiro(1234))
16+
sol = solve(jprob, SSAStepper())
17+
# After:
18+
jprob = JumpProblem(dprob, Direct(), jump)
19+
sol = solve(jprob, SSAStepper(); rng = Xoshiro(1234))
20+
```
21+
- **Breaking**: `JumpProcesses.DEFAULT_RNG` has been removed. Custom jump
22+
affects should draw from `SciMLBase.get_rng(integrator)`. Add SciMLBase as
23+
a direct project dependency and load it with `using SciMLBase` to use this
24+
interface. Pass `rng` or `seed` to `solve`/`init` to control the solver's RNG.
25+
- Jump callbacks now sample from the integrator's RNG. `JumpProblem`s,
26+
aggregators, and callbacks no longer store their own RNGs. Aggregators and
27+
callbacks still contain mutable working state, so concurrent solves must
28+
isolate that state; sharing an arbitrary `JumpProblem` across threads or
29+
tasks is not made safe by this change.
30+
- `SSAStepper` and the OrdinaryDiffEq ODE/DAE pathways accept `rng` and `seed`
31+
through `solve`/`init`; all five CPU tau-leaping algorithms accept them
32+
through `solve`. These pathways use `rng` first, then `Xoshiro(seed)` if a
33+
seed is supplied, then `Random.default_rng()`.
34+
- StochasticDiffEq SDE/RODE `solve` and `init` follow the backend's RNG policy.
35+
An explicit RNG other than `TaskLocalRNG` takes priority over seeds. With
36+
no RNG or with `TaskLocalRNG`, a nonzero solve/init seed takes priority,
37+
followed by the underlying problem's nonzero seed, then a randomly seeded
38+
`Xoshiro`. `TaskLocalRNG` is converted to `Xoshiro`; `seed = 0` means no seed
39+
override on this pathway, whereas the JumpProcesses RNG resolver uses
40+
`Xoshiro(0)`.
41+
- **Breaking**: Solvers owned by JumpProcesses (`SSAStepper` and the
42+
OrdinaryDiffEq ODE/DAE pathways) no longer copy a `JumpProblem`'s jump state.
43+
On every thread they reuse the problem's aggregator and jump callbacks,
44+
re-initializing them at each `init`; previously, solves on threads other than
45+
thread 1 copied this state. A `JumpProblem` therefore supports one active solve
46+
or integrator at a time. Concurrent solves from threads or tasks, and
47+
integrators alive at the same time, must each use an independent copy, such as
48+
`deepcopy(jprob)`. Problems created with `remake` share the original's jump
49+
state, so they are not independent either. `EnsembleProblem` provides the
50+
needed copies: `EnsembleThreads` copies the problem once per spawned task, and
51+
`safetycopy = true` copies it for every trajectory. See the new tutorial on
52+
ensembles and problem reuse for patterns that balance performance and safety.
53+
- **Breaking**: The `alias_jump` keyword argument has been removed. Passing it to
54+
`solve`, `init`, `JumpProblem`, or a multi-trajectory `EnsembleGPUKernel`
55+
solve, or storing it on the problem wrapped by a `JumpProblem`, raises an
56+
error; remove it, and solve a copy of the problem when
57+
independent jump state is needed. StochasticDiffEq SDE/RODE solvers ignore the
58+
keyword and keep their own policy: pass
59+
`alias = SciMLBase.SDEAliasSpecifier(; alias_jumps = false)` (or
60+
`SciMLBase.RODEAliasSpecifier` for RODEs) to copy jump state, or
61+
`alias_jumps = true` to reuse it. When unspecified, those solvers reuse jump
62+
state on thread 1 and copy it on other threads.
63+
- **Breaking**: Callbacks stored on the problem wrapped by a `JumpProblem` are not
64+
supported. The `JumpProblem` constructor, and `remake` with a new wrapped problem,
65+
now raise an `ArgumentError` if that problem stores a `callback` (other than
66+
`nothing` or an empty `CallbackSet`). Previously, OrdinaryDiffEq solvers
67+
(including `FunctionMap`) ran such callbacks, while `SSAStepper` and
68+
StochasticDiffEq SDE/RODE solvers silently ignored them. Pass callbacks to the
69+
`JumpProblem` constructor or to `solve`/`init` instead:
70+
```julia
71+
# Before (no longer works):
72+
oprob = ODEProblem(f, u0, tspan, p; callback = cb)
73+
jprob = JumpProblem(oprob, Direct(), jump)
74+
# After:
75+
oprob = ODEProblem(f, u0, tspan, p)
76+
jprob = JumpProblem(oprob, Direct(), jump; callback = cb)
77+
```
78+
`remake(prob; callback = nothing)` removes callbacks from an existing problem.
79+
- `SSAStepper` now supports the common `alias` keyword argument for its `u0`, `p`,
80+
and `tstops` inputs, given as a `Bool`, a `SciMLBase.DiscreteAliasSpecifier`, or
81+
an `ODEAliasSpecifier` (which also controls `tstops`). The defaults match
82+
OrdinaryDiffEq: `u0` is copied, `p` is reused, and a caller's `tstops` array is
83+
never modified. `alias` never controls jump state.
84+
- `SSAStepper` now saves states, and evaluates `integrator(t)`, with
85+
`recursivecopy` as OrdinaryDiffEq does, so nested state types are saved as
86+
independent snapshots. `tstops` given as a number, a tuple, or an
87+
`AbstractVector` other than a `Vector` of the time type (such as a range or
88+
view) is copied into a `Vector` at `init`, so `add_tstop!` works with it.
89+
- **Breaking**: Evaluating an `SSAStepper` integrator, as `integrator(t)` or
90+
`integrator(out, t)`, at a time other than `integrator.t` now raises an error,
91+
unless the problem is solved with `SSAStepper(; save_uprev = true)`, which also
92+
allows times in the current step, `[integrator.tprev, integrator.t]`.
93+
Previously the current state was returned for any `t`, which was silently wrong
94+
for times before the most recent event, for example in a `SavingCallback` or
95+
`FunctionCallingCallback` with save times. Alternatively, pass such times as
96+
`tstops`, so that the integrator stops exactly at them. DiffEqCallbacks'
97+
integrating callbacks are not yet supported with `SSAStepper`.
98+
- New `SSAStepper(; save_uprev = true)` option: the integrator keeps the state at
99+
the start of each step in `integrator.uprev`, copying the state on every step,
100+
and provides `get_tmp_cache` for callbacks that evaluate it in place. The
101+
default, `SSAStepper()`, keeps no copy. `SSAStepper` is now a parametric type;
102+
`SSAStepper()` is unchanged and has type `SSAStepper{false}`. After `solve!`,
103+
`integrator.tprev` is the time of the last event or tstop.
104+
- Fixed `SSAStepper` saving pending `saveat` times before the end time after its
105+
final callback pass. A callback that changed the state at the end time altered the
106+
values saved at those earlier times, and the saved times came out of order. They
107+
are now saved before the final callback pass.
108+
- Fixed `SSAStepper` erroring with a `BoundsError` when saving only the final state:
109+
`save_end = true` with nothing saved earlier, for example with `save_start = false`,
110+
`save_positions = (false, false)`, and no `saveat`. The final state is now saved.
111+
- Fixed `init` on ODE, DAE, and `FunctionMap` integrators for `JumpProblem`s:
112+
callbacks stored in the `JumpProblem` (`JumpProblem(...; callback)`) were merged
113+
twice, so they ran twice per step, and were reintroduced even with
114+
`merge_callbacks = false`. `init` now combines stored and call-level callbacks
115+
exactly as `solve` does.
116+
- Stochastic `init` of a `JumpProblem` now reaches StochasticDiffEqCore's own
117+
per-algorithm `JumpProblem` initializers, so `init` followed by `solve!` matches
118+
`solve`. This requires a StochasticDiffEqCore release that supports JumpProcesses 10.
119+
- The state types `SSAStepper` supports are now documented: vectors and
120+
`SVector`s of integers or floats, species × sites matrices for spatial
121+
problems, and scalars for models without mass-action jumps. See "Supported
122+
state types" in the solver documentation.
123+
- **Breaking**: Multi-trajectory `EnsembleGPUKernel` solves reject `alias` values
124+
other than `nothing` and the `alias_jumps` keyword, whether passed to `solve` or
125+
stored on the problem, since kernels always build device-owned state. They
126+
also reject `SSAStepper(; save_uprev = true)`, since kernels never expose an
127+
integrator.
128+
- The minimum supported SciMLBase version is now 3.34.
129+
- `SSAIntegrator` now supports the `SciMLBase` RNG interface (`has_rng`,
130+
`get_rng`, `set_rng!`).
131+
- **Breaking**: Passing `seed` to a multi-trajectory `EnsembleGPUKernel`
132+
solve with a GPU device backend now raises `ArgumentError`, instead of
133+
accepting a seed that does not control the device RNG. This applies to
134+
`SSAStepper`, `SimpleTauLeaping`, and `SimpleExplicitTauLeaping`. Seed the
135+
device through the backend's API before solving (for example,
136+
`CUDA.seed!`). Multi-trajectory kernel solves also reject host `rng`
137+
objects and `rng_func`. The `CPU()` backend still supports `seed` using
138+
Julia's task-local RNG; repeatability requires the same thread count,
139+
workload/trajectory count, CPU backend settings, and Julia/package versions.
140+
Identical trajectories across thread counts are not guaranteed. With `trajectories = 1`,
141+
`EnsembleGPUKernel` retains its serial CPU fallback and that pathway's
142+
usual RNG/seed rules.
143+
- **Breaking**: The `scale_rates` and `useiszero` keyword arguments have been
144+
removed from `JumpProblem`. Set them on the `MassActionJump` directly:
145+
```julia
146+
# Before (no longer works):
147+
jprob = JumpProblem(dprob, Direct(), maj; scale_rates = false)
148+
# After:
149+
maj = MassActionJump(rates, reactant_stoch, net_stoch; scale_rates = false)
150+
jprob = JumpProblem(dprob, Direct(), maj)
151+
```
152+
- **Breaking**: Parameterized `MassActionJump`s (those constructed with
153+
`param_idxs` or a custom `param_mapper`) retain `scaled_rates === nothing`.
154+
Solvers and aggregators own the working rate buffers and fill them from
155+
current parameters at initialization and reset. `remake` no longer mutates
156+
shared rate coefficients in the jump definition. Fixed-rate and symbolic
157+
definitions retain their supported `scaled_rates` representation. This means:
158+
+ `update_parameters!` has been removed. Mass action rates are now
159+
automatically recomputed from the current parameter values whenever the
160+
aggregator reinitializes. After modifying parameters (e.g. in a
161+
callback), call `reset_aggregated_jumps!(integrator)` to trigger
162+
reinitialization with the updated parameter values.
163+
+ The `update_jump_params` keyword has been removed from
164+
`reset_aggregated_jumps!`; supplying either `true` or `false` raises an
165+
explanatory error. Rate refresh is automatic during reset.
166+
+ Custom parameter mappers (e.g. ModelingToolkitBase's
167+
`JumpSysMajParamMapper`) must replace the old extraction/mutation
168+
callables with `(mapper)(dest::AbstractVector, maj::MassActionJump, params)`
169+
and fill `dest` with the scaled rates. The built-in mapper honors
170+
`maj.rescale_rates_on_update`; custom mappers own their scaling policy
171+
and must avoid scaling already-scaled symbolic expressions twice.
172+
See the [custom mapper migration guide](docs/src/migration.md#custom-parameter-mappers)
173+
for an example.
174+
- Scalar `param_idxs` (e.g. `param_idxs = 1`) is now internally converted to
175+
a one-element vector. The scalar form continues to work as before.
4176

5177
## 9.33.1
6178

@@ -28,7 +200,6 @@
28200

29201
```julia
30202
using JumpProcesses
31-
32203
rate(u, p, t) = p[1] * u[1] / (1 + u[2])
33204
function affect!(integrator)
34205
integrator.u[1] -= 1
@@ -41,10 +212,8 @@
41212
urate = p[1] * uhigh[1] / (1 + ulow[2])
42213
)
43214
end
44-
45215
jump = ConstantRateJump(rate, affect!; bounds)
46216
prob = DiscreteProblem([10, 0], (0.0, 10.0), [1.0])
47-
48217
# Each species affects the rate of jump 1; jump 1 changes both species.
49218
vartojumps_map = [[1], [1]]
50219
jumptovars_map = [[1, 2]]
@@ -67,7 +236,7 @@
67236
- Added a default aggregator selection algorithm based on the number of passed
68237
in jumps. i.e. the following now auto-selects an aggregator (`Direct` in this
69238
case):
70-
239+
71240
```julia
72241
using JumpProcesses
73242
rate(u, p, t) = u[1]
@@ -82,10 +251,11 @@
82251
`ConstantRateJump`s, and bounded `VariableRateJump`s, one no longer needs to
83252
specify `SSAStepper()` when calling `solve`, i.e. the following now works for
84253
the previous example and is equivalent to manually passing `SSAStepper()`:
85-
254+
86255
```julia
87256
sol = solve(jprob)
88257
```
258+
89259
- Plotting a solution generated with `save_positions = (false, false)` now uses
90260
piecewise linear plots between any saved time points specified via `saveat`
91261
instead (previously the plots appeared piecewise constant even though each

‎LICENSE.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
The JumpDiffEq.jl package is licensed under the MIT "Expat" License:
22

33
> Copyright (c) 2017: ChrisRackauckas.
4-
>
4+
>
55
> Permission is hereby granted, free of charge, to any person obtaining a copy
66
> of this software and associated documentation files (the "Software"), to deal
77
> in the Software without restriction, including without limitation the rights
88
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
> copies of the Software, and to permit persons to whom the Software is
1010
> furnished to do so, subject to the following conditions:
11-
>
11+
>
1212
> The above copyright notice and this permission notice shall be included in all
1313
> copies or substantial portions of the Software.
14-
>
14+
>
1515
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

‎Project.toml‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "JumpProcesses"
22
uuid = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"
33
authors = ["Chris Rackauckas <accounts@chrisrackauckas.com>"]
4-
version = "9.33.1"
4+
version = "10.0.0"
55

66
[deps]
77
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
@@ -52,22 +52,35 @@ OrdinaryDiffEq = "7.0.0, 7"
5252
OrdinaryDiffEqCore = "4.3.0, 4"
5353
OrdinaryDiffEqFunctionMap = "2.0.0, 2"
5454
Pkg = "1"
55-
PoissonRandom = "0.4"
55+
PoissonRandom = "0.4.8"
5656
PrecompileTools = "1.2.1"
5757
Random = "1"
5858
RecursiveArrayTools = "4.3.0, 4"
5959
SafeTestsets = "0.1"
60-
SciMLBase = "3.18.0, 3.1"
60+
SciMLBase = "3.34"
6161
SciMLTesting = "2.8"
6262
SimpleNonlinearSolve = "2, 3"
6363
StableRNGs = "1"
6464
StaticArrays = "1.9.18"
6565
Statistics = "1"
6666
StochasticDiffEq = "7.0.0, 7"
67+
StochasticDiffEqCore = "2.2.3"
68+
StochasticDiffEqImplicit = "2.2.1"
69+
StochasticDiffEqLeaping = "2.1.0"
6770
SymbolicIndexingInterface = "0.3.48"
6871
Test = "1"
6972
julia = "1.10"
7073

74+
[sources]
75+
# Temporary: tests use a commit of the StochasticDiffEq branch for JumpProcesses 10
76+
# (OrdinaryDiffEq.jl#4836), which removes the need for a JumpProcesses StochasticDiffEqCore
77+
# extension. Implicit and Leaping are pinned too, since their registered versions do not
78+
# admit JumpProcesses 10. Remove when those releases are registered. Julia 1.10 ignores
79+
# [sources]; see test/test_groups.toml.
80+
StochasticDiffEqCore = {url = "https://github.com/isaacsas/OrdinaryDiffEq.jl.git", rev = "1de403a5e59474fab9a43bf60d376391734eb65b", subdir = "lib/StochasticDiffEqCore"}
81+
StochasticDiffEqImplicit = {url = "https://github.com/isaacsas/OrdinaryDiffEq.jl.git", rev = "1de403a5e59474fab9a43bf60d376391734eb65b", subdir = "lib/StochasticDiffEqImplicit"}
82+
StochasticDiffEqLeaping = {url = "https://github.com/isaacsas/OrdinaryDiffEq.jl.git", rev = "1de403a5e59474fab9a43bf60d376391734eb65b", subdir = "lib/StochasticDiffEqLeaping"}
83+
7184
[extras]
7285
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
7386
FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898"
@@ -83,7 +96,10 @@ SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283"
8396
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
8497
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
8598
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
99+
StochasticDiffEqCore = "19c5a474-6cd1-4a5f-be79-46dc34e54d7f"
100+
StochasticDiffEqImplicit = "5080b986-4c76-4669-b5dc-373a41579d5b"
101+
StochasticDiffEqLeaping = "aefaaa88-39f2-4e89-b162-d61b7e8cc81b"
86102
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
87103

88104
[targets]
89-
test = ["Adapt", "FastBroadcast", "ForwardDiff", "KernelAbstractions", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEq", "OrdinaryDiffEqFunctionMap", "Pkg", "SafeTestsets", "SciMLTesting", "StableRNGs", "Statistics", "StochasticDiffEq", "Test"]
105+
test = ["Adapt", "FastBroadcast", "ForwardDiff", "KernelAbstractions", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEq", "OrdinaryDiffEqFunctionMap", "Pkg", "SafeTestsets", "SciMLTesting", "StableRNGs", "Statistics", "StochasticDiffEq", "StochasticDiffEqCore", "StochasticDiffEqImplicit", "StochasticDiffEqLeaping", "Test"]

0 commit comments

Comments
 (0)