@@ -735,68 +735,86 @@ end
735735 return (Dual {T} (sd, cd * π * partials (d)), Dual {T} (cd, - sd * π * partials (d)))
736736end
737737
738- # Symmetric eigvals #
738+ # eigen values and vectors of Hermitian matrices #
739739# -------------------#
740740
741- # To be able to reuse this default definition in the StaticArrays extension
742- # (has to be re-defined to avoid method ambiguity issues)
743- # we forward the call to an internal method that can be shared and reused
744- LinearAlgebra. eigvals (A:: Symmetric{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N} = _eigvals (A)
745- function _eigvals (A:: Symmetric{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N}
746- λ,Q = eigen (Symmetric (value .(parent (A))))
747- parts = ntuple (j -> diag (Q' * getindex .(partials .(A), j) * Q), N)
748- Dual {Tg} .(λ, tuple .(parts... ))
741+ # Extract structured matrices of primal values and partials
742+ _structured_value (A:: Symmetric{Dual{T,V,N}} ) where {T,V,N} = Symmetric (map (value, parent (A)), A. uplo === ' U' ? :U : :L )
743+ _structured_value (A:: Hermitian{Dual{T,V,N}} ) where {T,V,N} = Hermitian (map (value, parent (A)), A. uplo === ' U' ? :U : :L )
744+ _structured_value (A:: Hermitian{Complex{Dual{T,V,N}}} ) where {T,V,N} = Hermitian (map (z -> splat (complex)(map (value, reim (z))), parent (A)), A. uplo === ' U' ? :U : :L )
745+ _structured_value (A:: SymTridiagonal{Dual{T,V,N}} ) where {T,V,N} = SymTridiagonal (map (value, A. dv), map (value, A. ev))
746+
747+ _structured_partials (A:: Symmetric{Dual{T,V,N}} , j:: Int ) where {T,V,N} = Symmetric (partials .(parent (A), j), A. uplo === ' U' ? :U : :L )
748+ _structured_partials (A:: Hermitian{Dual{T,V,N}} , j:: Int ) where {T,V,N} = Hermitian (partials .(parent (A), j), A. uplo === ' U' ? :U : :L )
749+ function _structured_partials (A:: Hermitian{Complex{Dual{T,V,N}}} , j:: Int ) where {T,V,N}
750+ return Hermitian (complex .(partials .(real .(parent (A)), j), partials .(imag .(parent (A)), j)), A. uplo === ' U' ? :U : :L )
749751end
752+ _structured_partials (A:: SymTridiagonal{Dual{T,V,N}} , j:: Int ) where {T,V,N} = SymTridiagonal (partials .(A. dv, j), partials .(A. ev, j))
750753
751- function LinearAlgebra. eigvals (A:: Hermitian{<:Complex{<:Dual{Tg,T,N}}} ) where {Tg,T<: Real ,N}
752- λ,Q = eigen (Hermitian (value .(real .(parent (A))) .+ im .* value .(imag .(parent (A)))))
753- parts = ntuple (j -> diag (real .(Q' * (getindex .(partials .(real .(A)) .+ im .* partials .(imag .(A)), j)) * Q)), N)
754- Dual {Tg} .(λ, tuple .(parts... ))
754+ # Convert arrays of primal values and partials to arrays of Duals
755+ function _to_duals (:: Val{T} , values:: AbstractArray{<:Real} , partials:: Tuple{Vararg{AbstractArray{<:Real}}} ) where {T}
756+ return Dual {T} .(values, tuple .(partials... ))
757+ end
758+ function _to_duals (:: Val{T} , values:: AbstractArray{<:Complex} , partials:: Tuple{Vararg{AbstractArray{<:Complex}}} ) where {T}
759+ return complex .(
760+ Dual {T} .(real .(values), Base. Fix1 (map, real).(tuple .(partials... ))),
761+ Dual {T} .(imag .(values), Base. Fix1 (map, imag).(tuple .(partials... ))),
762+ )
755763end
756764
757- function LinearAlgebra. eigvals (A:: SymTridiagonal{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N}
758- λ,Q = eigen (SymTridiagonal (value .(parent (A). dv),value .(parent (A). ev)))
759- parts = ntuple (j -> diag (Q' * getindex .(partials .(A), j) * Q), N)
760- Dual {Tg} .(λ, tuple .(parts... ))
765+ # We forward the call to an internal method that can be shared and reused
766+ LinearAlgebra. eigvals (A:: Symmetric{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigvals_hermitian (A)
767+ LinearAlgebra. eigvals (A:: Hermitian{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigvals_hermitian (A)
768+ LinearAlgebra. eigvals (A:: Hermitian{Complex{Dual{T,V,N}}} ) where {T,V<: Real ,N} = _eigvals_hermitian (A)
769+ LinearAlgebra. eigvals (A:: SymTridiagonal{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigvals_hermitian (A)
770+
771+ # Eigenvalues of Hermitian-structured matrices
772+ const DualMatrixRealComplex{T,V<: Real ,N} = Union{AbstractMatrix{Dual{T,V,N}}, AbstractMatrix{Complex{Dual{T,V,N}}}}
773+ function _eigvals_hermitian (A:: DualMatrixRealComplex{T,<:Real,N} ) where {T,N}
774+ F = eigen (_structured_value (A))
775+ λ = F. values
776+ Q = F. vectors
777+ parts = ntuple (j -> real (diag (Q' * _structured_partials (A, j) * Q)), N)
778+ return _to_duals (Val (T), λ, parts)
761779end
762780
763- # A ./ (λ' .- λ) but with diag special cased
781+ # A ./ (λ' .- λ) but with diagonal elements zeroed out
764782# Default out-of-place method
765- function _lyap_div !! (A:: AbstractMatrix , λ:: AbstractVector )
783+ function _lyap_div_zero_diag !! (A:: AbstractMatrix , λ:: AbstractVector )
766784 return map (
767- (a, b, idx) -> a / ( idx[1 ] == idx[2 ] ? oneunit (b) : b) ,
785+ (a, b, idx) -> idx[1 ] == idx[2 ] ? zero (a) / oneunit (b) : a / b ,
768786 A,
769787 λ' .- λ,
770788 CartesianIndices (A),
771789 )
772790end
773791# For `Matrix` (and e.g. `StaticArrays.MMatrix`) we can use an in-place method
774- _lyap_div !! (A:: Matrix , λ:: AbstractVector ) = _lyap_div ! (A, λ)
775- function _lyap_div ! (A:: AbstractMatrix , λ:: AbstractVector )
792+ _lyap_div_zero_diag !! (A:: Matrix , λ:: AbstractVector ) = _lyap_div_zero_diag ! (A, λ)
793+ function _lyap_div_zero_diag ! (A:: AbstractMatrix , λ:: AbstractVector )
776794 for (j,μ) in enumerate (λ), (k,λ) in enumerate (λ)
777- if k ≠ j
795+ if k == j
796+ A[k, j] = zero (A[k, j])
797+ else
778798 A[k,j] /= μ - λ
779799 end
780800 end
781801 A
782802end
783803
784- # To be able to reuse this default definition in the StaticArrays extension
785- # (has to be re-defined to avoid method ambiguity issues)
786- # we forward the call to an internal method that can be shared and reused
787- LinearAlgebra. eigen (A:: Symmetric{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N} = _eigen (A)
788- function _eigen (A:: Symmetric{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N}
789- λ = eigvals (A)
790- _,Q = eigen (Symmetric (value .(parent (A))))
791- parts = ntuple (j -> Q* _lyap_div!! (Q' * getindex .(partials .(A), j) * Q - Diagonal (getindex .(partials .(λ), j)), value .(λ)), N)
792- Eigen (λ,Dual {Tg} .(Q, tuple .(parts... )))
793- end
794-
795- function LinearAlgebra. eigen (A:: SymTridiagonal{<:Dual{Tg,T,N}} ) where {Tg,T<: Real ,N}
796- λ = eigvals (A)
797- _,Q = eigen (SymTridiagonal (value .(parent (A))))
798- parts = ntuple (j -> Q* _lyap_div!! (Q' * getindex .(partials .(A), j) * Q - Diagonal (getindex .(partials .(λ), j)), value .(λ)), N)
799- Eigen (λ,Dual {Tg} .(Q, tuple .(parts... )))
804+ # We forward the call to an internal method that can be shared and reused
805+ LinearAlgebra. eigen (A:: Symmetric{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigen_hermitian (A)
806+ LinearAlgebra. eigen (A:: Hermitian{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigen_hermitian (A)
807+ LinearAlgebra. eigen (A:: Hermitian{Complex{Dual{T,V,N}}} ) where {T,V<: Real ,N} = _eigen_hermitian (A)
808+ LinearAlgebra. eigen (A:: SymTridiagonal{Dual{T,V,N}} ) where {T,V<: Real ,N} = _eigen_hermitian (A)
809+
810+ function _eigen_hermitian (A:: DualMatrixRealComplex{T,<:Real,N} ) where {T,N}
811+ F = eigen (_structured_value (A))
812+ λ = F. values
813+ Q = F. vectors
814+ Qt_∂A_Q = ntuple (j -> Q' * _structured_partials (A, j) * Q, N)
815+ λ_partials = map (real ∘ diag, Qt_∂A_Q)
816+ Q_partials = map (Qt_∂Aj_Q -> Q* _lyap_div_zero_diag!! (Qt_∂Aj_Q, λ), Qt_∂A_Q)
817+ return Eigen (_to_duals (Val (T), λ, λ_partials), _to_duals (Val (T), Q, Q_partials))
800818end
801819
802820# Functions in SpecialFunctions which return tuples #
0 commit comments