@@ -735,68 +735,86 @@ end
735
735
return (Dual {T} (sd, cd * π * partials (d)), Dual {T} (cd, - sd * π * partials (d)))
736
736
end
737
737
738
- # Symmetric eigvals #
738
+ # eigen values and vectors of Hermitian matrices #
739
739
# -------------------#
740
740
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 )
749
751
end
752
+ _structured_partials (A:: SymTridiagonal{Dual{T,V,N}} , j:: Int ) where {T,V,N} = SymTridiagonal (partials .(A. dv, j), partials .(A. ev, j))
750
753
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
+ )
755
763
end
756
764
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)
761
779
end
762
780
763
- # A ./ (λ' .- λ) but with diag special cased
781
+ # A ./ (λ' .- λ) but with diagonal elements zeroed out
764
782
# Default out-of-place method
765
- function _lyap_div !! (A:: AbstractMatrix , λ:: AbstractVector )
783
+ function _lyap_div_zero_diag !! (A:: AbstractMatrix , λ:: AbstractVector )
766
784
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 ,
768
786
A,
769
787
λ' .- λ,
770
788
CartesianIndices (A),
771
789
)
772
790
end
773
791
# 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 )
776
794
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
778
798
A[k,j] /= μ - λ
779
799
end
780
800
end
781
801
A
782
802
end
783
803
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))
800
818
end
801
819
802
820
# Functions in SpecialFunctions which return tuples #
0 commit comments