From 726b8de066644d36d7b4e76ebaac1b43fdff5f36 Mon Sep 17 00:00:00 2001 From: Fastaxx Date: Thu, 12 Nov 2026 23:38:17 +0100 Subject: [PATCH 1/3] Add a rule for three-argument hypot --- src/rules.jl | 2 ++ test/runtests.jl | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/rules.jl b/src/rules.jl index e2dd233..c32e6dd 100644 --- a/src/rules.jl +++ b/src/rules.jl @@ -96,6 +96,8 @@ _abs_deriv(x) = signbit(x) ? -one(x) : one(x) # trinary # #---------# +@define_diffrule Base.hypot(x, y, z) = :( $x / hypot($x, $y, $z) ), :( $y / hypot($x, $y, $z) ), :( $z / hypot($x, $y, $z) ) + #= @define_diffrule Base.muladd(x, y, z) = :($y), :($x), :(one($z)) diff --git a/test/runtests.jl b/test/runtests.jl index 92b0866..c3b13ad 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -338,6 +338,22 @@ end rules = DiffRules.diffrules(; filter_modules=(:Base, :LogExpFunctions)) @test Set(M for (M, _, _) in rules) == Set((:Base, :LogExpFunctions)) end + + # Test the ternary hypot separately + @testset "hypot(x, y, z)" begin + @test DiffRules.hasdiffrule(:Base, :hypot, 3) + derivs = DiffRules.diffrule(:Base, :hypot, :foo, :bar, :goo) + for T in (Float32, Float64) + @eval let + foo, bar, goo = rand($T, 3) .+ $T(0.5) + dx, dy, dz = $(derivs[1]), $(derivs[2]), $(derivs[3]) + @test dx ≈ finitediff(x -> hypot(x, bar, goo), foo) rtol=1e-2 atol=1e-3 + @test dy ≈ finitediff(y -> hypot(foo, y, goo), bar) rtol=1e-2 atol=1e-3 + @test dz ≈ finitediff(z -> hypot(foo, bar, z), goo) rtol=1e-2 atol=1e-3 + @test promote_type(typeof(dx), $T) === $T + end + end + end end # Test ifelse separately as first argument is boolean From 3dcf1bdf5591871b43346f4e2c8334db78ca518e Mon Sep 17 00:00:00 2001 From: Fastaxx Date: Fri, 13 Nov 2026 15:27:08 +0100 Subject: [PATCH 2/3] Test ternary rules in the generic check-rules loop --- test/runtests.jl | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index c3b13ad..460be51 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -88,23 +88,32 @@ non_diffeable_arg_functions = [(:Base, :rem2pi, 2), (:Base, :ldexp, 2), (:Base, end end elseif arity == 3 - #= @test DiffRules.hasdiffrule(M, f, 3) derivs = DiffRules.diffrule(M, f, :foo, :bar, :goo) @eval begin - foo, bar, goo = randn(3) - dx, dy, dz = $(derivs[1]), $(derivs[2]), $(derivs[3]) - if !(isnan(dx)) - @test isapprox(dx, finitediff(x -> $M.$f(x, bar, goo), foo), rtol=0.05) - end - if !(isnan(dy)) - @test isapprox(dy, finitediff(y -> $M.$f(foo, y, goo), bar), rtol=0.05) - end - if !(isnan(dz)) - @test isapprox(dz, finitediff(z -> $M.$f(foo, bar, z), goo), rtol=0.05) + let + foo, bar, goo = rand($T), rand($T), rand($T) + dx, dy, dz = $(derivs[1]), $(derivs[2]), $(derivs[3]) + if !(isnan(dx)) + @test dx ≈ finitediff(z -> $M.$f(z, bar, goo), foo) rtol=1e-2 atol=1e-3 + + # Check type, if applicable. + @test promote_type(typeof(real(dx)), $T) === $T + end + if !(isnan(dy)) + @test dy ≈ finitediff(z -> $M.$f(foo, z, goo), bar) rtol=1e-2 atol=1e-3 + + # Check type, if applicable. + @test promote_type(typeof(real(dy)), $T) === $T + end + if !(isnan(dz)) + @test dz ≈ finitediff(z -> $M.$f(foo, bar, z), goo) rtol=1e-2 atol=1e-3 + + # Check type, if applicable. + @test promote_type(typeof(real(dz)), $T) === $T + end end end - =# end end end @@ -339,21 +348,6 @@ end @test Set(M for (M, _, _) in rules) == Set((:Base, :LogExpFunctions)) end - # Test the ternary hypot separately - @testset "hypot(x, y, z)" begin - @test DiffRules.hasdiffrule(:Base, :hypot, 3) - derivs = DiffRules.diffrule(:Base, :hypot, :foo, :bar, :goo) - for T in (Float32, Float64) - @eval let - foo, bar, goo = rand($T, 3) .+ $T(0.5) - dx, dy, dz = $(derivs[1]), $(derivs[2]), $(derivs[3]) - @test dx ≈ finitediff(x -> hypot(x, bar, goo), foo) rtol=1e-2 atol=1e-3 - @test dy ≈ finitediff(y -> hypot(foo, y, goo), bar) rtol=1e-2 atol=1e-3 - @test dz ≈ finitediff(z -> hypot(foo, bar, z), goo) rtol=1e-2 atol=1e-3 - @test promote_type(typeof(dx), $T) === $T - end - end - end end # Test ifelse separately as first argument is boolean From 1d1768d8851038ae2c74ef85a3ada0a5b6089e61 Mon Sep 17 00:00:00 2001 From: Fastaxx Date: Fri, 13 Nov 2026 15:54:09 +0100 Subject: [PATCH 3/3] Ternary checks: rtol only, and drop a stray blank line (review) --- test/runtests.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 460be51..5f91905 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -95,19 +95,19 @@ non_diffeable_arg_functions = [(:Base, :rem2pi, 2), (:Base, :ldexp, 2), (:Base, foo, bar, goo = rand($T), rand($T), rand($T) dx, dy, dz = $(derivs[1]), $(derivs[2]), $(derivs[3]) if !(isnan(dx)) - @test dx ≈ finitediff(z -> $M.$f(z, bar, goo), foo) rtol=1e-2 atol=1e-3 + @test dx ≈ finitediff(z -> $M.$f(z, bar, goo), foo) rtol=1e-2 # Check type, if applicable. @test promote_type(typeof(real(dx)), $T) === $T end if !(isnan(dy)) - @test dy ≈ finitediff(z -> $M.$f(foo, z, goo), bar) rtol=1e-2 atol=1e-3 + @test dy ≈ finitediff(z -> $M.$f(foo, z, goo), bar) rtol=1e-2 # Check type, if applicable. @test promote_type(typeof(real(dy)), $T) === $T end if !(isnan(dz)) - @test dz ≈ finitediff(z -> $M.$f(foo, bar, z), goo) rtol=1e-2 atol=1e-3 + @test dz ≈ finitediff(z -> $M.$f(foo, bar, z), goo) rtol=1e-2 # Check type, if applicable. @test promote_type(typeof(real(dz)), $T) === $T @@ -347,7 +347,6 @@ end rules = DiffRules.diffrules(; filter_modules=(:Base, :LogExpFunctions)) @test Set(M for (M, _, _) in rules) == Set((:Base, :LogExpFunctions)) end - end # Test ifelse separately as first argument is boolean