From cb0e8ac26aeb5662a9dbae050b8ca5ce1d41c8af Mon Sep 17 00:00:00 2001 From: ChrisRackauckas-Claude Date: Fri, 3 Jul 2026 14:03:34 -0400 Subject: [PATCH] Fix flaky gamma_inc partials test The partials assertions compared ForwardDiff's analytic gamma_inc derivative against Calculus.jl central differences of the V-precision, ind-approximation function. Two problems: for V === Float32 the finite difference of a Float32 function has error comparable to the 5f-4 tolerance, and for ind = 1/2 gamma_inc itself is a reduced-accuracy approximation whose intrinsic noise the finite difference amplifies far beyond any tolerance. With random `a`/`PRIMAL` this failed sporadically (~4% of random draws; roughly one CI job per master run). ForwardDiff's rule is exp(-x)x^(a-1)/gamma(a), independent of `ind`, so compare against a finite difference of the full-accuracy (ind = 0) Float64 evaluation instead, at the *base* tolerance (the ind-dependent loosening is only needed for the value comparison, which keeps it). Measured over 300000 random (a, PRIMAL, PARTIALS) draws x all ind variants (2.4M assertions): old reference fails ~4% of draws, new reference fails 0 - while every observed old-reference failure had the Float32 dual derivative within 4e-8 of the true derivative, i.e. the reference, not ForwardDiff, was wrong. Co-Authored-By: Chris Rackauckas Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Vx7zQ96NYk4VV4ML2s3kAC --- test/DualTest.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/test/DualTest.jl b/test/DualTest.jl index df9f9dcb..1633f43b 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -632,10 +632,19 @@ ForwardDiff.:≺(::Type{OuterTestTag}, ::Type{TestTag}) = false # We have to adjust tolerances if lower accuracy is requested # Therefore we don't use `dual_isapprox` tol = V === Float32 ? 5f-4 : 1e-5 - tol = tol^(one(tol) / 2^(isempty(ind) ? 0 : first(ind))) + tolval = tol^(one(tol) / 2^(isempty(ind) ? 0 : first(ind))) for i in 1:2 - @test value(pq[i]) ≈ gamma_inc(a, 1 + PRIMAL, ind...)[i] rtol=tol - @test partials(pq[i]) ≈ PARTIALS * Calculus.derivative(x -> gamma_inc(a, x, ind...)[i], 1 + PRIMAL) rtol=tol + @test value(pq[i]) ≈ gamma_inc(a, 1 + PRIMAL, ind...)[i] rtol=tolval + # ForwardDiff computes the analytic derivative (independent of + # `ind`), so compare against a finite difference of the + # full-accuracy (`ind = 0`) Float64 evaluation: differencing the + # reduced-accuracy `ind` variants (or the V-precision function for + # V === Float32) puts the reference's own error at or above `tol`, + # which makes this test flake for random `a`/`PRIMAL`. + # Float64(1 + PRIMAL) keeps the evaluation point exactly the + # primal of `fdnum` (convert after the V-precision addition). + der = Calculus.derivative(x -> gamma_inc(Float64(a), x, 0)[i], Float64(1 + PRIMAL)) + @test partials(pq[i]) ≈ PARTIALS * der rtol=tol end end end