# This file is a part of Julia. License is MIT: https://julialang.org/license # Singular Value Decomposition struct SVD{T,Tr,M<:AbstractArray} <: Factorization{T} U::M S::Vector{Tr} Vt::M SVD{T,Tr,M}(U::AbstractArray{T}, S::Vector{Tr}, Vt::AbstractArray{T}) where {T,Tr,M} = new(U, S, Vt) end SVD(U::AbstractArray{T}, S::Vector{Tr}, Vt::AbstractArray{T}) where {T,Tr} = SVD{T,Tr,typeof(U)}(U, S, Vt) """ svdfact!(A, thin::Bool=true) -> SVD `svdfact!` is the same as [`svdfact`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. """ function svdfact!(A::StridedMatrix{T}; thin::Bool=true) where T<:BlasFloat m,n = size(A) if m == 0 || n == 0 u,s,vt = (eye(T, m, thin ? n : m), real(zeros(T,0)), eye(T,n,n)) else u,s,vt = LAPACK.gesdd!(thin ? 'S' : 'A', A) end SVD(u,s,vt) end """ svdfact(A; thin::Bool=true) -> SVD Compute the singular value decomposition (SVD) of `A` and return an `SVD` object. `U`, `S`, `V` and `Vt` can be obtained from the factorization `F` with `F[:U]`, `F[:S]`, `F[:V]` and `F[:Vt]`, such that `A = U*diagm(S)*Vt`. The algorithm produces `Vt` and hence `Vt` is more efficient to extract than `V`. The singular values in `S` are sorted in descending order. If `thin=true` (default), a thin SVD is returned. For a ``M \\times N`` matrix `A`, `U` is ``M \\times M`` for a full SVD (`thin=false`) and ``M \\times \\min(M, N)`` for a thin SVD. # Example ```jldoctest julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] 4×5 Array{Float64,2}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> F = svdfact(A) Base.LinAlg.SVD{Float64,Float64,Array{Float64,2}}([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.0 … -0.0 0.0; 0.447214 0.0 … 0.0 0.894427; -0.0 1.0 … -0.0 0.0; 0.0 0.0 … 1.0 0.0]) julia> F[:U] * diagm(F[:S]) * F[:Vt] 4×5 Array{Float64,2}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 ``` """ function svdfact(A::StridedVecOrMat{T}; thin::Bool = true) where T S = promote_type(Float32, typeof(one(T)/norm(one(T)))) svdfact!(copy_oftype(A, S), thin = thin) end svdfact(x::Number; thin::Bool=true) = SVD(x == 0 ? fill(one(x), 1, 1) : fill(x/abs(x), 1, 1), [abs(x)], fill(one(x), 1, 1)) svdfact(x::Integer; thin::Bool=true) = svdfact(float(x), thin=thin) """ svd(A; thin::Bool=true) -> U, S, V Computes the SVD of `A`, returning `U`, vector `S`, and `V` such that `A == U*diagm(S)*V'`. The singular values in `S` are sorted in descending order. If `thin=true` (default), a thin SVD is returned. For a ``M \\times N`` matrix `A`, `U` is ``M \\times M`` for a full SVD (`thin=false`) and ``M \\times \\min(M, N)`` for a thin SVD. `svd` is a wrapper around [`svdfact`](@ref), extracting all parts of the `SVD` factorization to a tuple. Direct use of `svdfact` is therefore more efficient. # Example ```jldoctest julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] 4×5 Array{Float64,2}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> U, S, V = svd(A) ([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.447214 -0.0 0.0; 0.0 0.0 1.0 0.0; … ; -0.0 0.0 -0.0 1.0; 0.0 0.894427 0.0 0.0]) julia> U*diagm(S)*V' 4×5 Array{Float64,2}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 ``` """ function svd(A::Union{Number, AbstractArray}; thin::Bool=true) F = svdfact(A, thin=thin) F.U, F.S, F.Vt' end function getindex(F::SVD, d::Symbol) if d == :U return F.U elseif d == :S return F.S elseif d == :Vt return F.Vt elseif d == :V return F.Vt' else throw(KeyError(d)) end end """ svdvals!(A) Returns the singular values of `A`, saving space by overwriting the input. See also [`svdvals`](@ref). """ svdvals!(A::StridedMatrix{T}) where {T<:BlasFloat} = findfirst(size(A), 0) > 0 ? zeros(T, 0) : LAPACK.gesdd!('N', A)[2] svdvals(A::AbstractMatrix{<:BlasFloat}) = svdvals!(copy(A)) """ svdvals(A) Returns the singular values of `A` in descending order. # Example ```jldoctest julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] 4×5 Array{Float64,2}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> svdvals(A) 4-element Array{Float64,1}: 3.0 2.23607 2.0 0.0 ``` """ function svdvals(A::AbstractMatrix{T}) where T S = promote_type(Float32, typeof(one(T)/norm(one(T)))) svdvals!(copy_oftype(A, S)) end svdvals(x::Number) = abs(x) svdvals(S::SVD{<:Any,T}) where {T} = (S[:S])::Vector{T} # SVD least squares function A_ldiv_B!{T}(A::SVD{T}, B::StridedVecOrMat) k = searchsortedlast(A.S, eps(real(T))*A.S[1], rev=true) view(A.Vt,1:k,:)' * (view(A.S,1:k) .\ (view(A.U,:,1:k)' * B)) end # Generalized svd struct GeneralizedSVD{T,S} <: Factorization{T} U::S V::S Q::S a::Vector b::Vector k::Int l::Int R::S function GeneralizedSVD{T,S}(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T}, a::Vector, b::Vector, k::Int, l::Int, R::AbstractMatrix{T}) where {T,S} new(U, V, Q, a, b, k, l, R) end end function GeneralizedSVD(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T}, a::Vector, b::Vector, k::Int, l::Int, R::AbstractMatrix{T}) where T GeneralizedSVD{T,typeof(U)}(U, V, Q, a, b, k, l, R) end """ svdfact!(A, B) -> GeneralizedSVD `svdfact!` is the same as [`svdfact`](@ref), but modifies the arguments `A` and `B` in-place, instead of making copies. """ function svdfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 if LAPACK.laver() < (3, 6, 0) U, V, Q, a, b, k, l, R = LAPACK.ggsvd!('U', 'V', 'Q', A, B) else U, V, Q, a, b, k, l, R = LAPACK.ggsvd3!('U', 'V', 'Q', A, B) end GeneralizedSVD(U, V, Q, a, b, Int(k), Int(l), R) end svdfact(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = svdfact!(copy(A),copy(B)) """ svdfact(A, B) -> GeneralizedSVD Compute the generalized SVD of `A` and `B`, returning a `GeneralizedSVD` factorization object `F`, such that `A = F[:U]*F[:D1]*F[:R0]*F[:Q]'` and `B = F[:V]*F[:D2]*F[:R0]*F[:Q]'`. For an M-by-N matrix `A` and P-by-N matrix `B`, - `F[:U]` is a M-by-M orthogonal matrix, - `F[:V]` is a P-by-P orthogonal matrix, - `F[:Q]` is a N-by-N orthogonal matrix, - `F[:R0]` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is nonsingular upper block triangular, - `F[:D1]` is a M-by-(K+L) diagonal matrix with 1s in the first K entries, - `F[:D2]` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal, `K+L` is the effective numerical rank of the matrix `[A; B]`. The entries of `F[:D1]` and `F[:D2]` are related, as explained in the LAPACK documentation for the [generalized SVD](http://www.netlib.org/lapack/lug/node36.html) and the [xGGSVD3](http://www.netlib.org/lapack/explore-html/d6/db3/dggsvd3_8f.html) routine which is called underneath (in LAPACK 3.6.0 and newer). """ function svdfact(A::StridedMatrix{TA}, B::StridedMatrix{TB}) where {TA,TB} S = promote_type(Float32, typeof(one(TA)/norm(one(TA))),TB) return svdfact!(copy_oftype(A, S), copy_oftype(B, S)) end """ svd(A, B) -> U, V, Q, D1, D2, R0 Wrapper around [`svdfact`](@ref) extracting all parts of the factorization to a tuple. Direct use of `svdfact` is therefore generally more efficient. The function returns the generalized SVD of `A` and `B`, returning `U`, `V`, `Q`, `D1`, `D2`, and `R0` such that `A = U*D1*R0*Q'` and `B = V*D2*R0*Q'`. """ function svd(A::AbstractMatrix, B::AbstractMatrix) F = svdfact(A, B) F[:U], F[:V], F[:Q], F[:D1], F[:D2], F[:R0] end function getindex(obj::GeneralizedSVD{T}, d::Symbol) where T if d == :U return obj.U elseif d == :V return obj.V elseif d == :Q return obj.Q elseif d == :alpha || d == :a return obj.a elseif d == :beta || d == :b return obj.b elseif d == :vals || d == :S return obj.a[1:obj.k + obj.l] ./ obj.b[1:obj.k + obj.l] elseif d == :D1 m = size(obj.U, 1) if m - obj.k - obj.l >= 0 return [eye(T, obj.k) zeros(T, obj.k, obj.l); zeros(T, obj.l, obj.k) diagm(obj.a[obj.k + 1:obj.k + obj.l]); zeros(T, m - obj.k - obj.l, obj.k + obj.l)] else return [eye(T, m, obj.k) [zeros(T, obj.k, m - obj.k); diagm(obj.a[obj.k + 1:m])] zeros(T, m, obj.k + obj.l - m)] end elseif d == :D2 m = size(obj.U, 1) p = size(obj.V, 1) if m - obj.k - obj.l >= 0 return [zeros(T, obj.l, obj.k) diagm(obj.b[obj.k + 1:obj.k + obj.l]); zeros(T, p - obj.l, obj.k + obj.l)] else return [zeros(T, p, obj.k) [diagm(obj.b[obj.k + 1:m]); zeros(T, obj.k + p - m, m - obj.k)] [zeros(T, m - obj.k, obj.k + obj.l - m); eye(T, obj.k + p - m, obj.k + obj.l - m)]] end elseif d == :R return obj.R elseif d == :R0 n = size(obj.Q, 1) return [zeros(T, obj.k + obj.l, n - obj.k - obj.l) obj.R] else throw(KeyError(d)) end end function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 if LAPACK.laver() < (3, 6, 0) _, _, _, a, b, k, l, _ = LAPACK.ggsvd!('N', 'N', 'N', A, B) else _, _, _, a, b, k, l, _ = LAPACK.ggsvd3!('N', 'N', 'N', A, B) end a[1:k + l] ./ b[1:k + l] end svdvals(A::StridedMatrix{T},B::StridedMatrix{T}) where {T<:BlasFloat} = svdvals!(copy(A),copy(B)) """ svdvals(A, B) Return the generalized singular values from the generalized singular value decomposition of `A` and `B`. See also [`svdfact`](@ref). """ function svdvals(A::StridedMatrix{TA}, B::StridedMatrix{TB}) where {TA,TB} S = promote_type(Float32, typeof(one(TA)/norm(one(TA))), TB) return svdvals!(copy_oftype(A, S), copy_oftype(B, S)) end # Conversion convert(::Type{AbstractMatrix}, F::SVD) = (F.U * Diagonal(F.S)) * F.Vt convert(::Type{AbstractArray}, F::SVD) = convert(AbstractMatrix, F) convert(::Type{Matrix}, F::SVD) = convert(Array, convert(AbstractArray, F)) convert(::Type{Array}, F::SVD) = convert(Matrix, F) full(F::SVD) = convert(AbstractArray, F)