Why is typeof not type inferable in this case?

I’m messing around with ForwardDiff, trying to write some type stable code, and have found an instance where typeof does not properly infer the type. I’m a bit stuck as to what to try here!

I think this particular function used to infer properly, before I switched to Julia 1.9, but many other things have changed as well…

The function in question:

function hessian(f, x::SVector)
    # T = typeof(HessiansTag(x))
    T = typeof(Tag(f, eltype(x)))
    d1 = dualize(T, x)
    d2 = dualize(T, d1)
    yd2 = f(d2)
    yd1 = value(T, yd2)
    val = value(T, yd1)
    dgrad = extract_jacobian(T, yd2, x)
    grad = value(T, dgrad)    
    hess = extract_jacobian(T, dgrad, x)
    result = DiffResults.ImmutableDiffResult(val, (grad, hess))
    return result
end

The output of @code_warntype is below.
Note that %2 is infered, but typeof(%2) is not

MethodInstance for ForwardDiff.hessian(::RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, ::StaticArraysCore.SVector{9, Float64})
  from hessian(f, x::StaticArraysCore.SVector) @ RobotDynamics.Hessians C:\Users\dan_l\Dropbox (Cambridge University)\shared_Daniel\code\RobotDynamics.jl\src\hessians.jl:47
Arguments
  #self#::Core.Const(ForwardDiff.hessian)
  f::RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}
  x::StaticArraysCore.SVector{9, Float64}
Locals
  result::DiffResults.ImmutableDiffResult{2}
  hess::Any
  grad::Any
  dgrad::Any
  val::Any
  yd1::Any
  yd2::Any
  d2::Any
  d1::Any
  T::Type{ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}}
Body::DiffResults.ImmutableDiffResult{2}
1 ─ %1  = RobotDynamics.Hessians.eltype(x)::Core.Const(Float64)
│   %2  = RobotDynamics.Hessians.Tag(f, %1)::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}())
│         (T = RobotDynamics.Hessians.typeof(%2))
│         (d1 = RobotDynamics.Hessians.dualize(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), x))
│         (d2 = RobotDynamics.Hessians.dualize(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), d1))
│         (yd2 = (f)(d2))
│         (yd1 = RobotDynamics.Hessians.value(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), yd2))
│         (val = RobotDynamics.Hessians.value(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), yd1))
│         (dgrad = RobotDynamics.Hessians.extract_jacobian(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), yd2, x))
│         (grad = RobotDynamics.Hessians.value(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), dgrad))
│         (hess = RobotDynamics.Hessians.extract_jacobian(T::Core.Const(ForwardDiff.Tag{RobotDynamics.var"#f_kinematics#8"{RobotDynamics.RBTree{22, Union{RobotDynamics.Prismatic{Float64, Int64}, RobotDynamics.Revolute{Float64, Int64}, RobotDynamics.Rigid{Float64}, RobotDynamics.Rail{Float64, Int64}}}}, Float64}), dgrad, x))
│   %12 = DiffResults.ImmutableDiffResult::Core.Const(DiffResults.ImmutableDiffResult)
│   %13 = val::Any
│   %14 = Core.tuple(grad, hess)::Tuple{Any, Any}
│         (result = (%12)(%13, %14))
└──       return result

Curiously, the syntax highlighting here is different to in VSCode.

var"#f_kinematics#8"

was not being highlighted in red in my VSCode terminal.

My guess is this, but that shouldn’t have been in play because you do use f in a head position. In any case, try

function hessian(f::F, x::SVector) where F

I couldn’t verify that this fixed it because you don’t have all the exports from ForwardDiff listed and I didn’t try to locate them all myself.

1 Like