Incorrect Hessian when using `exp` function with complex input

Incorrect Hessian by exp function · Issue #677 · JuliaDiff/ForwardDiff.jl (github.com)
I was trying to calculate the Hessian of a simple function such as

using ForwardDiff
function busPQ(Ybus, Vθ)
    n = length(Vθ)
    V=Vθ[1:Int(n/2)]
    θ=Vθ[Int(n/2)+1:n]
    Vbus=V.*exp.(im .*θ)
    # Vbus=V.*(cos.(θ)+im .*sin.(θ))
    # print(Vbus-Vbus)
    I = Ybus.*Vbus
    return vcat(reim(Vbus.*conj(I))...)
end

Ybus=[1 - 3im]
Vm=[1]
Va=[0]
Vθ = vcat(Vm,Va)
function Hessian(f, x)
    n = length(x)
    out = ForwardDiff.jacobian(x -> ForwardDiff.jacobian(f, x), x)
    return reshape(out, n, n, n)
end
H(y) = Hessian(x-> busPQ(Ybus, x), y)

At point [1,0], the above codes produce the wrong Hessian. However, if I set very small perturbation to , then the results are correct.

julia> H([1,0])
2×2×2 Array{Float64, 3}:
[:, :, 1] =
 2.0  0.0
 6.0  0.0

[:, :, 2] =
 0.0  2.0
 0.0  6.0

julia> H([1,1e-10])
2×2×2 Array{Float64, 3}:
[:, :, 1] =
 2.0  0.0
 6.0  0.0

[:, :, 2] =
 0.0  0.0
 0.0  0.0

julia> H([1,-1e-10])
2×2×2 Array{Float64, 3}:
[:, :, 1] =
 2.0  0.0
 6.0  0.0

[:, :, 2] =
 0.0  0.0
 0.0  0.0

It is found to be more robust to use cos.(θ)+im .*sin.(θ), which functioned correctly at point [1,0], instead of exp.(im .*θ).
I think the problem has something in common with #653.

The difference between cos.(θ)+im .*sin.(θ) and exp.(im .*θ) is printed as follows

Complex{ForwardDiff.Dual{ForwardDiff.Tag{var"#21#22", ForwardDiff.Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}, Int64, 2}}, ForwardDiff.Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}, Float64, 2}, 2}}[Dual{ForwardDiff.Tag{var"#21#22", ForwardDiff.Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}, Int64, 2}}}(Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,0.0),Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,0.0),Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,1.0)) + Dual{ForwardDiff.Tag{var"#21#22", ForwardDiff.Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}, Int64, 2}}}(Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,0.0),Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,0.0),Dual{ForwardDiff.Tag{var"#19#20"{var"#21#22"}, Int64}}(0.0,0.0,0.0))*im]2×2×2 Array{Float64, 3}:

It is clear that the above Dual is not all zero.

The dependencies are ForwardDiff v0.10.36 and Julia V1.9.3.

Xref issue 677, this appears to be solved on the unreleased v0.11.

1 Like