Issue with ForwardDiff custom AD rule

I have the following issue:

import ForwardDiff, DiffRules, IrrationalConstants, SpecialFunctions
∂logerfcx(x) = 2 * (x - inv(SpecialFunctions.erfcx(x)) / IrrationalConstants.sqrtπ)
DiffRules.@define_diffrule SpecialFunctions.logerfcx(x) = :(∂logerfcx($x))
ForwardDiff.derivative(SpecialFunctions.logerfcx, 4)

This gives the following error:

ERROR: MethodError: no method matching _logerfcx(::ForwardDiff.Dual{ForwardDiff.Tag{typeof(SpecialFunctions.logerfcx), Int64}, Float64, 1})
Closest candidates are:
_logerfcx(::Union{Float32, Float64, BigFloat}) at ~/.julia/packages/SpecialFunctions/NBIqR/src/erf.jl:552
Stacktrace:
[1] logerfcx(x::ForwardDiff.Dual{ForwardDiff.Tag{typeof(SpecialFunctions.logerfcx), Int64}, Int64, 1})
@ SpecialFunctions ~/.julia/packages/SpecialFunctions/NBIqR/src/erf.jl:550
[2] derivative(f::typeof(SpecialFunctions.logerfcx), x::Int64)
@ ForwardDiff ~/.julia/packages/ForwardDiff/tZ5o1/src/derivative.jl:14
[3] top-level scope
@ REPL[15]:1

Why is ForwardDiff not picking up the custom rule I defined?

Related: Broadcast gradient error · Issue #1132 · FluxML/Zygote.jl · GitHub

The rules for ForwardDiff are generated at using time. You need to add the rules to DiffRules.jl directly.

1 Like

You mean in a pull request to the DiffRules.jl package?

yes

Ok done https://github.com/JuliaDiff/DiffRules.jl/pull/74.
Hope it gets merged quick.

Is there a hacky way to do it? Like defining a method for ::Dual args?

Yes, see https://github.com/JuliaDiff/ForwardDiff.jl/blob/ab0e239452cfa059e3c0d7467d6bb489e57ea1ab/src/dual.jl#L402.

1 Like

Ah thanks @kristoffer.carlsson that works! For future reference,

∂logerfcx(x) = 2 * (x - inv(SpecialFunctions.erfcx(x)) / IrrationalConstants.sqrtπ)
DiffRules.@define_diffrule SpecialFunctions.logerfcx(x) = :(∂logerfcx($x))
eval(ForwardDiff.unary_dual_definition(:SpecialFunctions, :logerfcx))

And now ForwardDiff.derivative(SpecialFunctions.logerfcx, 4) works :slightly_smiling_face: