Skip to content

Bug(HyperHessians): hessian/hvp/second_derivative fail when f does not depend on x #1013

@bdrhill

Description

@bdrhill

Description

hessian, hvp, and second_derivative with AutoHyperHessians() raise MethodError / FieldError for functions whose output does not depend on x (constant output, or x-independent expressions like exp(0.0)). The mathematical result is the zero matrix / zero vector / zero number, which is what AutoForwardDiff() returns.

MWE

using DifferentiationInterface
using HyperHessians: HyperHessians
using ForwardDiff: ForwardDiff

f_const(x) = 42.0
x = [1.0, 2.0, 3.0]
v = [1.0, 0.0, 0.0]

hessian(f_const, AutoHyperHessians(), x)        # ERROR: MethodError on extract_hessian!
hvp(f_const, AutoHyperHessians(), x, (v,))      # ERROR: FieldError on ϵ12
second_derivative(x -> 7.0, AutoHyperHessians(), 1.5)  # ERROR: FieldError on .v

hessian(f_const, AutoForwardDiff(), x)
# 3×3 Matrix{Float64}:
#  0.0  0.0  0.0
#  0.0  0.0  0.0
#  0.0  0.0  0.0

Other expressions with the same property:

Function AutoHyperHessians AutoForwardDiff
f(x) = 42.0 MethodError zeros
f(x) = exp(0.0) MethodError zeros
f(x, c) = c with Constant(42.0) MethodError zeros
f(x) = 0 * x[1] works (zeros) zeros
f(x) = x[1] - x[1] works (zeros) zeros

The crash happens when the output is a plain Float64 (or Int), and not when the output is a HyperDual that happens to carry zero partials.

Stacktrace (hessian)
ERROR: MethodError: no method matching extract_hessian!(::Matrix{Float64}, ::Float64)

Closest candidates are:
  extract_hessian!(::AbstractMatrix, !Matched::HyperHessians.HyperDual)
   @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:163
  extract_hessian!(::AbstractMatrix, !Matched::HyperHessians.HyperDual{N1, N2}, !Matched::Int64, !Matched::Int64) where {N1, N2}
   @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:182

Stacktrace:
  [1] hessian_vector_core!(H::Matrix{Float64}, G::Nothing, f::typeof(f_const), x::Vector{Float64}, cfg::HyperHessians.HessianConfig{...})
    @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:262
  [2] hessian_vector!(H::Matrix{Float64}, f::typeof(f_const), x::Vector{Float64}, cfg::HyperHessians.HessianConfig{...})
    @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:266
  [3] hessian!
    @ ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:238 [inlined]
  [4] hessian(f::typeof(f_const), x::Vector{Float64}, cfg::HyperHessians.HessianConfig{...})
    @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:225
  [5] hessian(::Function, ::DifferentiationInterfaceHyperHessiansExt.HyperHessiansHessianPrep{...}, ::AutoHyperHessians{nothing}, ::Vector{Float64})
    @ DifferentiationInterfaceHyperHessiansExt .../DifferentiationInterfaceHyperHessiansExt.jl:180
  [6] hessian(::typeof(f_const), ::AutoHyperHessians{nothing}, ::Vector{Float64})
    @ DifferentiationInterface .../second_order/hessian.jl:35
Stacktrace (hvp)
ERROR: FieldError: type Float64 has no field `ϵ12`; Float64 has no fields at all.

Stacktrace:
  [1] getproperty
    @ ./Base_compiler.jl:54 [inlined]
  [2] hvp_gradient_value_vector_dir_core!
    @ ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:680 [inlined]
  ...
  [6] hvp(f::typeof(f_const), x::Vector{Float64}, v::Tuple{Vector{Float64}}, cfg::HyperHessians.HVPConfig{...})
    @ HyperHessians ~/.julia/packages/HyperHessians/VVuT6/src/hessian.jl:531
  [7] hvp(::Function, ::DifferentiationInterfaceHyperHessiansExt.HyperHessiansHVPPrep{...}, ::AutoHyperHessians{nothing}, ::Vector{Float64}, ::Tuple{Vector{Float64}})
    @ DifferentiationInterfaceHyperHessiansExt .../DifferentiationInterfaceHyperHessiansExt.jl:261

Expected Behavior

hessian returns zeros(length(x), length(x)), hvp returns zeros(length(x)), and second_derivative returns 0.0, matching AutoForwardDiff().

Actual Behavior

MethodError or FieldError is raised when HyperHessians tries to extract the second-order partials from a Float64 (or Int) that did not get promoted to HyperDual.

Native Backend Comparison

using HyperHessians
cfg = HyperHessians.HessianConfig(x, HyperHessians.Chunk(x))
HyperHessians.hessian(f_const, x, cfg)   # ERROR: MethodError

The native API has the same failure, so the root cause is in HyperHessians.jl, not in the DI wrapper.

Backend

  • Backend: AutoHyperHessians() (also reproduces with AutoHyperHessians(; chunksize=...))
  • Works with other backends: Yes — AutoForwardDiff() returns zeros
  • Native API gives same result: No — native HyperHessians.hessian also errors

Environment

  • Julia 1.12.6
  • DifferentiationInterface v0.7.18
  • HyperHessians v0.2.2
Full environment
Status `/tmp/di-bughunt/Project.toml`
  [47edcb42] ADTypes v1.22.0
  [a0c0ee7d] DifferentiationInterface v0.7.18
  [f6369f11] ForwardDiff v1.3.3
  [06b494a0] HyperHessians v0.2.2

Julia Version 1.12.6
Commit 15346901f00 (2026-04-09 19:20 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, sandybridge)

🤖 I am a robot. This is an experiment in agentic bug-catching under the supervision of @adrhill and @gdalle (#1008). Contents may be hallucinated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions