Skip to content

fix: per-trajectory error norm for EnsembleGPUArray - #571

Open
SebastianM-C wants to merge 1 commit into
SciML:masterfrom
SebastianM-C:smc/gpuarray-traj-norm
Open

SebastianM-C wants to merge 1 commit into
SciML:masterfrom
SebastianM-C:smc/gpuarray-traj-norm

Conversation

@SebastianM-C

Copy link
Copy Markdown
Member

EnsembleGPUArray solves all trajectories as one batched problem, and used diffeqgpunorm as its internalnorm: an RMS over all N·ntraj components. A trajectory that needs small steps is outvoted by the easy ones and silently misses its tolerance, by more the larger the batch. The norm was also passed after kwargs..., so a user-supplied internalnorm couldn't override it.

Example: u' = ω cos(ω t) on [0, 10], Tsit5, abstol = reltol = 1e-8, trajectory 1 at ω = 50 and the rest at ω = 1. Trajectory 1's error at t = 10 is 1.6e-9 solved alone and 5.0e-7 inside a batch of 1000, with no warning.

This PR adds TrajectoryNorm(len), which takes the RMS of each trajectory's own components and returns the largest. It handles N×ntraj state arrays and their vectorized form, host and device arrays, and Dual-valued arrays. With it the error above is 1.6e-9 at any batch size. The default is now set before kwargs..., so internalnorm passed to solve replaces it. The EnsembleGPUArray docstring gains a Step-size control section that describes this and the override.

Behaviour change: the shared step is now the one the hardest trajectory needs, so heterogeneous batches take more steps. Lorenz, the EnsembleGPUArray docstring example (Float32, random p, saveat = 1, CUDA):

  • Tsit5, 10_000 trajectories on [0, 100]: naccept 1076 → 2092, nreject 0 → 6
  • Rosenbrock23, 1_000 trajectories on [0, 10]: naccept 265 → 797, nreject 15 → 128

Those extra steps are the cost of every trajectory actually meeting its tolerance. To get the old behaviour back, pass internalnorm = DiffEqGPU.diffeqgpunorm.

Tests: test/ensemblegpuarray_trajectory_norm.jl: the norm on host, device and Dual arrays, and the ω = 50 trajectory meeting its tolerance in batches of 2 and 1000. On an RTX 4080 SUPER (CUDA group): 12/12, and 12/12 in the CPU group. The existing EnsembleGPUArray test files still pass with the new default: ensemblegpuarray.jl 6/6, ensemblegpuarray_scalar_batch.jl 55/55, ensemblegpuarray_sde.jl 1/1, reduction.jl 3/3, and ensemblegpuarray_oop.jl, ensemblegpuarray_inputtypes.jl, lower_level_api.jl run without errors.


🤖 Generated with Claude Code · model: claude-opus-5-5[1m]
Review: unreviewed

The batched solve used `diffeqgpunorm`, an RMS over all N·ntraj components, as
`internalnorm`, and passed it after `kwargs...` so it could not be overridden. A
trajectory that needs small steps is outvoted by the easy ones and silently misses its
tolerance, by more the larger the batch. For u' = ω cos(ω t) on [0, 10] with Tsit5 at
abstol = reltol = 1e-8, trajectory 1 at ω = 50 and the rest at ω = 1, its error at
t = 10 is 1.6e-9 solved alone and 5.0e-7 inside a batch of 1000.

`TrajectoryNorm(len)` takes the RMS of each trajectory's own components and returns the
largest, for N×ntraj state arrays and their vectorized form, on host and device arrays,
and for Dual-valued arrays. With it the error above is 1.6e-9 for any batch size. The
default is set before `kwargs...`, so `internalnorm` passed to `solve` replaces it.

The shared step is now the one the hardest trajectory needs, so batches take more steps.
Lorenz, the EnsembleGPUArray docstring example (Float32, random p, saveat = 1, CUDA):
- Tsit5, 10_000 trajectories on [0, 100]: naccept 1076 -> 2092, nreject 0 -> 6
- Rosenbrock23, 1_000 trajectories on [0, 10]: naccept 265 -> 797, nreject 15 -> 128

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant