ForwardDiff: How to get the derivative part of a dual number

How do I get the derivative part b (or whatever it is called) of a dual number given by a+b\epsilon, \quad \epsilon^2=0, defined using ForwardDiff package?

In particular, with the dual number 3+1\epsilon given by

julia> using ForwardDiff

julia> a = ForwardDiff.Dual(3.0,1.0)
Dual{Nothing}(3.0,1.0)

the real part can be obtained as

julia> a.value
3.0

but accessing the derivative part does not work using the dot notation

julia> a.partials
1-element ForwardDiff.Partials{1,Float64}:
Error showing value of type ForwardDiff.Partials{1,Float64}:
ERROR: MethodError: no method matching getindex(::Tuple{Float64}, ::Int64, ::Int64)
Closest candidates are:
  getindex(::Tuple, ::Int64) at tuple.jl:24
  getindex(::Tuple, ::Real) at tuple.jl:25
  getindex(::Tuple, ::AbstractUnitRange{#s66} where #s66<:Real) at range.jl:290
  ...
Stacktrace:
 [1] getindex at /home/hurak/.julia/packages/ForwardDiff/vt5F1/src/partials.jl:23 [inlined]
 [2] isassigned(::ForwardDiff.Partials{1,Float64}, ::Int64, ::Int64) at ./abstractarray.jl:405
 [3] alignment(::IOContext{REPL.Terminals.TTYTerminal}, ::ForwardDiff.Partials{1,Float64}, ::UnitRange{Int64}, ::UnitRange{Int64}, ::Int64, ::Int64, ::Int64) at ./arrayshow.jl:67
 [4] print_matrix(::IOContext{REPL.Terminals.TTYTerminal}, ::ForwardDiff.Partials{1,Float64}, ::String, ::String, ::String, ::String, ::String, ::String, ::Int64, ::Int64) at ./arrayshow.jl:186
 [5] print_matrix at ./arrayshow.jl:159 [inlined]
 [6] print_array at ./arrayshow.jl:308 [inlined]
 [7] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::ForwardDiff.Partials{1,Float64}) at ./arrayshow.jl:345
 [8] display(::REPL.REPLDisplay, ::MIME{Symbol("text/plain")}, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:132
 [9] display(::REPL.REPLDisplay, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:136
 [10] display(::Any) at ./multimedia.jl:323
 [11] #invokelatest#1 at ./essentials.jl:709 [inlined]
 [12] invokelatest at ./essentials.jl:708 [inlined]
 [13] print_response(::IO, ::Any, ::Bool, ::Bool, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:156
 [14] print_response(::REPL.AbstractREPL, ::Any, ::Bool, ::Bool) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:141
 [15] (::REPL.var"#do_respond#38"{Bool,REPL.var"#48#57"{REPL.LineEditREPL,REPL.REPLHistoryProvider},REPL.LineEditREPL,REPL.LineEdit.Prompt})(::Any, ::Any, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:719
 [16] #invokelatest#1 at ./essentials.jl:709 [inlined]
 [17] invokelatest at ./essentials.jl:708 [inlined]
 [18] run_interface(::REPL.Terminals.TextTerminal, ::REPL.LineEdit.ModalInterface, ::REPL.LineEdit.MIState) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/LineEdit.jl:2306
 [19] run_frontend(::REPL.LineEditREPL, ::REPL.REPLBackendRef) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:1045
 [20] run_repl(::REPL.AbstractREPL, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:201
 [21] (::Base.var"#770#772"{Bool,Bool,Bool,Bool})(::Module) at ./client.jl:382
 [22] #invokelatest#1 at ./essentials.jl:709 [inlined]
 [23] invokelatest at ./essentials.jl:708 [inlined]
 [24] run_main_repl(::Bool, ::Bool, ::Bool, ::Bool, ::Bool) at ./client.jl:366
 [25] exec_options(::Base.JLOptions) at ./client.jl:304
 [26] _start() at ./client.jl:460

Apparently, it is just a matter of printing the output, because

julia> show(a.partials)
Partials(1.0,)

ForwardDiff.partials(x)

1 Like

@edit ForwardDiff.partials(x) shows

@inline partials(d::Dual) = d.partials

So it still throws an error at the REPL. Either way, throwing an error instead of doing anything else is a bug.

x.partials.values does work at the REPL

1 Like

Yeah, that printing should get fixed. I’m surprised I don’t remember running into that :man_shrugging:, but using the accessor functions ForwardDiff.value and ForwardDiff.partials is how it’s supposed to be done.

I had a fix lying around that I had forgotten to upstream, see Restrict `getindex` for partials to a single Int by timholy · Pull Request #437 · JuliaDiff/ForwardDiff.jl · GitHub

3 Likes