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)