ForwardDiff.jl and `@printf`

What are best practices regarding automatic differentiation of functions that print some intermediate results using ForwardDiff.jl? For example, using Julia v1.6.0-rc3 and ForwardDiff.jl v0.10.17, I get

julia> using ForwardDiff, Printf

julia> function foo(x)
           @printf("x β‰ˆ %.2e\n", x)
           return sin(x)
       end
foo (generic function with 1 method)

julia> ForwardDiff.derivative(foo, 0.0)
ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{typeof(foo), Float64}, Float64, 1})
Closest candidates are:
  (::Type{T})(::Real, ::RoundingMode) where T<:AbstractFloat at rounding.jl:200
  (::Type{T})(::T) where T<:Number at boot.jl:760
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} at char.jl:50
  ...
Stacktrace:
 [1] tofloat(x::ForwardDiff.Dual{ForwardDiff.Tag{typeof(foo), Float64}, Float64, 1})
   @ Printf /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Printf/src/Printf.jl:385
 [2] fmt
   @ /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Printf/src/Printf.jl:398 [inlined]
 [3] format
   @ /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Printf/src/Printf.jl:698 [inlined]
 [4] format(io::Base.TTY, f::Printf.Format{Base.CodeUnits{UInt8, String}, Tuple{Printf.Spec{Val{'e'}}}}, args::ForwardDiff.Dual{ForwardDiff.Tag{typeof(foo), Float64}, Float64, 1})
   @ Printf /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/Printf/src/Printf.jl:781
 [5] foo(x::ForwardDiff.Dual{ForwardDiff.Tag{typeof(foo), Float64}, Float64, 1})
   @ Main ./REPL[3]:2
 [6] derivative(f::typeof(foo), x::Float64)
   @ ForwardDiff ~/.julia/packages/ForwardDiff/sqhTO/src/derivative.jl:14
 [7] top-level scope
   @ REPL[4]:1

A hacky solution would be to use ForwardDiff.value, but this does not seem to be a practical solution.

julia> function foo_maybe_dual(x)
           @printf("x β‰ˆ %.2e\n", ForwardDiff.value(x))
           return sin(x)
       end
foo_maybe_dual (generic function with 1 method)

julia> foo_maybe_dual(0.0)
x β‰ˆ 0.00e+00
0.0

julia> ForwardDiff.derivative(foo_maybe_dual, 0.0)
x β‰ˆ 0.00e+00
1.0

Are there better solutions out there or should a specialization of Printf.tofloat (from Julia v1.6) be added to ForwardDiff.jl?
Edit: I submitted a PR to ForwardDiff.jl.

For reference: In Julia v1.5.3 and ForwardDiff v0.10.17, I get

julia> using ForwardDiff, Printf

julia> function foo(x)
           @printf("x β‰ˆ %.2e\n", x)
           return sin(x)
       end
foo (generic function with 1 method)

julia> ForwardDiff.derivative(foo, 0.0)
x β‰ˆ ERROR: StackOverflowError:
Stacktrace:
 [1] ini_dec(::ForwardDiff.Dual{ForwardDiff.Tag{typeof(foo),Float64},Float64,1}, ::Int64, ::Array{UInt8,1}) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.5/Printf/src/Printf.jl:1013 (repeats 79984 times)

Why not? This is what I use all the time.

It works, of course, but it requires people to depend on ForwardDiff.jl even if they don’t use it explicitly in their package. That’s what I don’t like about it.

I see (I thought that the code was under your control). In that case, a method for Printf.tofloat is indeed the best solution.

1 Like