From 3f20e1c9bdb256274819b1b7400fec4e4b060820 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 16 Oct 2026 07:07:57 -0400 Subject: [PATCH 1/2] Compare GPU block-argmin objective up to FMA rounding `sol.objective` is `f` recomputed on the host at the chosen position, while the particles' `best_cost` comes from the GPU kernel, where multiply-adds may be contracted to FMA. The two differ by 1 ulp on CUDA CI. Check the chosen candidate exactly and the objective with rtol = 8eps(). Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Agent-Harness: Claude Code Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_011aK5Xvp9NpdkzKuEwtiMGd --- test/gpu.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/gpu.jl b/test/gpu.jl index 529b160..655aa07 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -100,8 +100,10 @@ end ) cache = init(prob, opt_f(n)) sol = solve!(cache; maxiters = 5) - best = minimum(p.best_cost for p in Array(cache.particles)) - @test sol.objective == best + best = argmin(p -> p.best_cost, Array(cache.particles)) + @test sol.u == best.best_position + # host recomputes f, while the kernel's best_cost may be FMA-contracted + @test isapprox(sol.objective, best.best_cost; rtol = 8eps()) end end From 2846e8f7804f0bb2697628b444a92cef026f72c2 Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 16 Oct 2026 07:37:24 -0400 Subject: [PATCH 2/2] Derive argmin test tolerance from the summation error bound Each ordering of an n-term sum of nonnegative squares has relative error at most about n*eps/2, so host and FMA-contracted kernel values differ by at most about n*eps. The sampled 4-ulp maximum was not a bound: x = (1, y, ..., y) with y = sqrt(8.5eps) gives a 9eps gap, which passes rtol = 10eps. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Agent-Harness: Claude Code Agent-Model: claude-opus-5-5[1m] Agent-Session: https://claude.ai/code/session_011aK5Xvp9NpdkzKuEwtiMGd --- test/gpu.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/gpu.jl b/test/gpu.jl index 655aa07..89ee3f5 100644 --- a/test/gpu.jl +++ b/test/gpu.jl @@ -102,8 +102,8 @@ end sol = solve!(cache; maxiters = 5) best = argmin(p -> p.best_cost, Array(cache.particles)) @test sol.u == best.best_position - # host recomputes f, while the kernel's best_cost may be FMA-contracted - @test isapprox(sol.objective, best.best_cost; rtol = 8eps()) + # host f vs FMA-contracted kernel cost: each n-term sum of squares is within n*eps/2 + @test isapprox(sol.objective, best.best_cost; rtol = length(sol.u) * eps()) end end