You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Compatibility with Base linear algebra functions #111
Hi,
I am interested in calculating the hessian of a function that requires calls to multiple other built-in and local functions. What is otherwise a run-able function quickly becomes incompatible with the Hessian type. Here is a simple example illustrating the issue:
LoadError: MethodError: eigvals! has no method matching eigvals!(::Array{ForwardDiff.HessianNumber{3,Float64,Tuple{Float64,Float64,Float64}},2})
while loading In[331], in expression starting on line 1
in eigvals at linalg/eigen.jl:92
in TestFun at In[329]:3
in _calc_hessian at C:\Users....julia\v0.4\ForwardDiff\src\api\hessian.jl:98
in hessian at C:\Users....julia\v0.4\ForwardDiff\src\api\hessian.jl:27
In this case, it is easy enough to avoid the call to eigvals by hardcoding the equations for the eigenvalues since Sigma is just a 2x2 matrix, but this is far from ideal for larger matrices and doesn't address the underlying difficulty of passing along Hessian type variables.
I am curious if anyone has thoughts on solutions/best practices. Any help is greatly appreciated.
Thanks!
Thanks for reporting this! Technically this isn't a case we support yet. ForwardDiff only works with native Julia code, and Julia's built-in eigvals implementation doesn't include a generic Julia version - it wraps LAPACK. This means that we can't pass ForwardDiff's special Julia number types through the function to collect derivative information.
Work is being done on a native Julia implementation for all these linear algebra functions (see LinearAlgebra.jl, cc @andreasnoack), but it's not finished yet. Once it is, we should be able to re-export that package from ForwardDiff so that these functions work for end-users without any extra effort.
If you have another way to get the Hessian of eigvals(Sigma) for your specific use case, #89 might be helpful to you.
It should be possible to find the Jacobian of eigenvalues of a symmetric matrix: I believe if A(c) is a symmetric matrix depending on a vector c then (generically) each eigenvalue λ_k satisfies
dλ_k/dc_j =q_k(c)'(dA/dc_j)*q_k(c)
where q_k(c) is the eigenvector associated to λ_k (This follows from differentiating q_k(c)'q_k(c) = 1 and λ_k = q_k(c)'A(c)*q_k(c).
I could try to make a PR to support this so that the following code would work:
julia> λ = c ->eigvals(Symmetric([c[1] 1.0; 1.0 c[2]]))
#3 (generic function with 1 method)
julia>λ([0.1,0.2])
2-element Array{Float64,1}:-0.85124921972503911.1512492197250395
julia> ForwardDiff.jacobian(λ, [0.1,0.2])
ERROR: MethodError: no method matching eigvals!(::Symmetric{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},2}})
Closest candidates are:eigvals!(::SymTridiagonal{#s664,V}where V<:AbstractArray{#s664,1}where#s664<:Union{Float32, Float64}) at /Users/solver/Projects/julia-1.4/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:245eigvals!(::SymTridiagonal{#s664,V}where V<:AbstractArray{#s664,1}where#s664<:Union{Float32, Float64}, ::UnitRange) at /Users/solver/Projects/julia-1.4/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:248eigvals!(::SymTridiagonal{#s664,V}where V<:AbstractArray{#s664,1}where#s664<:Union{Float32, Float64}, ::Real, ::Real) at /Users/solver/Projects/julia-1.4/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/tridiag.jl:253...
Stacktrace:
[1] eigvals(::Symmetric{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},2}}) at /Users/solver/Projects/julia-1.4/usr/share/julia/stdlib/v1.4/LinearAlgebra/src/symmetric.jl:723
[2] (::var"#3#4")(::Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},1}) at ./REPL[2]:1
[3] vector_mode_dual_eval(::var"#3#4", ::Array{Float64,1}, ::ForwardDiff.JacobianConfig{ForwardDiff.Tag{var"#3#4",Float64},Float64,2,Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},1}}) at /Users/solver/.julia/packages/ForwardDiff/cXTw0/src/apiutils.jl:37
[4] vector_mode_jacobian(::var"#3#4", ::Array{Float64,1}, ::ForwardDiff.JacobianConfig{ForwardDiff.Tag{var"#3#4",Float64},Float64,2,Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},1}}) at /Users/solver/.julia/packages/ForwardDiff/cXTw0/src/jacobian.jl:140
[5] jacobian(::Function, ::Array{Float64,1}, ::ForwardDiff.JacobianConfig{ForwardDiff.Tag{var"#3#4",Float64},Float64,2,Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},1}}, ::Val{true}) at /Users/solver/.julia/packages/ForwardDiff/cXTw0/src/jacobian.jl:17
[6] jacobian(::Function, ::Array{Float64,1}, ::ForwardDiff.JacobianConfig{ForwardDiff.Tag{var"#3#4",Float64},Float64,2,Array{ForwardDiff.Dual{ForwardDiff.Tag{var"#3#4",Float64},Float64,2},1}}) at /Users/solver/.julia/packages/ForwardDiff/cXTw0/src/jacobian.jl:15 (repeats 2 times)
[7] top-level scope at REPL[4]:1
# A ./ (λ - λ') but with diag special casedfunction_lyap_div!(A, λ)
for (j,μ) inenumerate(λ), (k,λ) inenumerate(λ)
if k ≠ j
A[k,j] /= μ - λ
endend
A
endfunctioneigen(A::Symmetric{<:Dual{Tg,T,N}}) where {Tg,T<:Real,N}
λ =eigvals(A)
_,Q =eigen(Symmetric(value.(parent(A))))
parts =ntuple(j -> Q*_lyap_div!(Q'*getindex.(partials.(A), j) * Q -Diagonal(getindex.(partials.(λ), j)), value.(λ)), N)
Eigen(λ,Dual{Tg}.(Q, tuple.(parts...)))
end
E.g.:
julia> A = c ->Symmetric([c[1] c[1]; 0 c[2]+2c[3]]);
julia> Q = c ->vec(eigen(A(c)).vectors);
julia>jacobian(Q, [0.1,0.2,0.3])
4×3 Array{Float64,2}:0.20936-0.02617-0.05234011.49484-0.186856-0.3737111.49484-0.186856-0.373711-0.209360.026170.0523401
julia> h =0.00001; (Q([0.1+h,0.2,0.3]) -Q([0.1,0.2,0.3]))/h
4-element Array{Float64,1}:0.209372786796890841.49485106765712121.4948510676571212-0.20937278679689084
Activity
[-]ForwardDiff.HessianNumber compatability [/-][+]Compatibility with Base linear algebra functions[/+]jrevels commentedon Mar 28, 2016
Thanks for reporting this! Technically this isn't a case we support yet. ForwardDiff only works with native Julia code, and Julia's built-in
eigvalsimplementation doesn't include a generic Julia version - it wraps LAPACK. This means that we can't pass ForwardDiff's special Julia number types through the function to collect derivative information.Work is being done on a native Julia implementation for all these linear algebra functions (see LinearAlgebra.jl, cc @andreasnoack), but it's not finished yet. Once it is, we should be able to re-export that package from ForwardDiff so that these functions work for end-users without any extra effort.
If you have another way to get the Hessian of
eigvals(Sigma)for your specific use case, #89 might be helpful to you.baggepinnen commentedon Dec 15, 2017
I tried
eigvals!from LinearAlgebra.jl but encounteredNot sure if the error is here or there, but I found this already opened issue so decided to try here first =)
ChrisRackauckas commentedon Dec 15, 2017
That is somewhat of a duplicate of #157
dlfivefifty commentedon Jun 1, 2020
It should be possible to find the Jacobian of eigenvalues of a symmetric matrix: I believe if
A(c)is a symmetric matrix depending on a vectorcthen (generically) each eigenvalueλ_ksatisfieswhere
q_k(c)is the eigenvector associated toλ_k(This follows from differentiatingq_k(c)'q_k(c) = 1andλ_k = q_k(c)'A(c)*q_k(c).I could try to make a PR to support this so that the following code would work:
dlfivefifty commentedon Jun 1, 2020
Though is this package the right place for this? I seem to remember there's a ForwardDiff.jl replacement in the works...
dlfivefifty commentedon Jun 3, 2020
It turned out to be pretty easy to implement:
This seems to work:
Thanks @marcusdavidwebb for the pointer on differentiating eigenvalues.
Would a PR into ForwardDiff.jl be appropriate?
dlfivefifty commentedon Jun 3, 2020
And this gives the eigenvectors:
E.g.:
rlk8 commentedon Sep 4, 2020
Elegant solution.
Lightup1 commentedon May 31, 2022
Is there a way to calculate differentiation of
f(eigvals(::Hermitian))wheref(x)is pure julia? @dlfivefiftydlfivefifty commentedon May 31, 2022
It should just work?
Lightup1 commentedon Jun 1, 2022
I just got this error message:
I use
dualcacheto cache the large matrix, and its diagonal element is depend on a real parametera2Dispatch a Hermitian `Matrix` to the `Symmetric` methods
Test the general path for JuliaDiff#111, and complex eigenvectors bey…