Skip to content

Commit acdffeb

Browse files
authored
Make adjoint and solve work for most factorizations (JuliaLang#40899)
* Add adjoint for Cholesky * Implement adjoint for BunchKaufman * Fix ldiv! for adjoints of Hessenbergs * Add adjoint of LDLt * Fix return for tall problems in fallback \ method for adjoint of Factorizations to make \ work for adjoint LQ. * Fix qr(A)'\b * Define adjoint for SVD * Improve promotion in fallback by defining general convert methods for Factorizations * Fix ldiv! for SVD * Restrict the general \ definition that handles over- and underdetermined systems to LAPACK factorizations * Remove redundant \ definitions in diagonal.jl * Add Factorization constructors for SVD * Disambiguate between the specialized \ for real lhs-complex rhs and then new \ for LAPACKFactorizations. * Adjustments based on review * Fixes for new pivoting syntax
1 parent 311ff56 commit acdffeb

File tree

17 files changed

+272
-75
lines changed

17 files changed

+272
-75
lines changed

stdlib/LinearAlgebra/src/LinearAlgebra.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,63 @@ const ⋅ = dot
397397
const × = cross
398398
export , ×
399399

400+
## convenience methods
401+
## return only the solution of a least squares problem while avoiding promoting
402+
## vectors to matrices.
403+
_cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x
404+
_cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X
405+
406+
## append right hand side with zeros if necessary
407+
_zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n))
408+
_zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2))
409+
410+
# General fallback definition for handling under- and overdetermined system as well as square problems
411+
# While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs
412+
# which is required by LAPACK but not SuiteSpase which allows real-complex solves in some cases. Hence,
413+
# we restrict this method to only the LAPACK factorizations in LinearAlgebra.
414+
# The definition is put here since it explicitly references all the Factorizion structs so it has
415+
# to be located after all the files that define the structs.
416+
const LAPACKFactorizations{T,S} = Union{
417+
BunchKaufman{T,S},
418+
Cholesky{T,S},
419+
LQ{T,S},
420+
LU{T,S},
421+
QR{T,S},
422+
QRCompactWY{T,S},
423+
QRPivoted{T,S},
424+
SVD{T,<:Real,S}}
425+
function (\)(F::Union{<:LAPACKFactorizations,Adjoint{<:Any,<:LAPACKFactorizations}}, B::AbstractVecOrMat)
426+
require_one_based_indexing(B)
427+
m, n = size(F)
428+
if m != size(B, 1)
429+
throw(DimensionMismatch("arguments must have the same number of rows"))
430+
end
431+
432+
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
433+
FF = Factorization{TFB}(F)
434+
435+
# For wide problem we (often) compute a minimum norm solution. The solution
436+
# is larger than the right hand side so we use size(F, 2).
437+
BB = _zeros(TFB, B, n)
438+
439+
if n > size(B, 1)
440+
# Underdetermined
441+
copyto!(view(BB, 1:m, :), B)
442+
else
443+
copyto!(BB, B)
444+
end
445+
446+
ldiv!(FF, BB)
447+
448+
# For tall problems, we compute a least squares solution so only part
449+
# of the rhs should be returned from \ while ldiv! uses (and returns)
450+
# the complete rhs
451+
return _cut_B(BB, 1:n)
452+
end
453+
# disambiguate
454+
(\)(F::LAPACKFactorizations{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} =
455+
invoke(\, Tuple{Factorization{T}, VecOrMat{Complex{T}}}, F, B)
456+
400457
"""
401458
LinearAlgebra.peakflops(n::Integer=2000; parallel::Bool=false)
402459

stdlib/LinearAlgebra/src/bunchkaufman.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,14 @@ julia> S.L*S.D*S.L' - A[S.p, S.p]
196196
bunchkaufman(A::AbstractMatrix{T}, rook::Bool=false; check::Bool = true) where {T} =
197197
bunchkaufman!(copy_oftype(A, typeof(sqrt(oneunit(T)))), rook; check = check)
198198

199-
convert(::Type{BunchKaufman{T}}, B::BunchKaufman{T}) where {T} = B
200-
convert(::Type{BunchKaufman{T}}, B::BunchKaufman) where {T} =
199+
BunchKaufman{T}(B::BunchKaufman) where {T} =
201200
BunchKaufman(convert(Matrix{T}, B.LD), B.ipiv, B.uplo, B.symmetric, B.rook, B.info)
202-
convert(::Type{Factorization{T}}, B::BunchKaufman{T}) where {T} = B
203-
convert(::Type{Factorization{T}}, B::BunchKaufman) where {T} = convert(BunchKaufman{T}, B)
201+
Factorization{T}(B::BunchKaufman) where {T} = BunchKaufman{T}(B)
204202

205203
size(B::BunchKaufman) = size(getfield(B, :LD))
206204
size(B::BunchKaufman, d::Integer) = size(getfield(B, :LD), d)
207205
issymmetric(B::BunchKaufman) = B.symmetric
208-
ishermitian(B::BunchKaufman) = !B.symmetric
206+
ishermitian(B::BunchKaufman{T}) where T = T<:Real || !B.symmetric
209207

210208
function _ipiv2perm_bk(v::AbstractVector{T}, maxi::Integer, uplo::AbstractChar, rook::Bool) where T
211209
require_one_based_indexing(v)
@@ -279,6 +277,14 @@ Base.propertynames(B::BunchKaufman, private::Bool=false) =
279277

280278
issuccess(B::BunchKaufman) = B.info == 0
281279

280+
function adjoint(B::BunchKaufman)
281+
if ishermitian(B)
282+
return B
283+
else
284+
throw(ArgumentError("adjoint not implemented for complex symmetric matrices"))
285+
end
286+
end
287+
282288
function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, B::BunchKaufman)
283289
if issuccess(B)
284290
summary(io, B); println(io)

stdlib/LinearAlgebra/src/cholesky.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,8 @@ Base.propertynames(F::CholeskyPivoted, private::Bool=false) =
529529

530530
issuccess(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0
531531

532+
adjoint(C::Union{Cholesky,CholeskyPivoted}) = C
533+
532534
function show(io::IO, mime::MIME{Symbol("text/plain")}, C::Cholesky{<:Any,<:AbstractMatrix})
533535
if issuccess(C)
534536
summary(io, C); println(io)

stdlib/LinearAlgebra/src/diagonal.jl

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -488,14 +488,6 @@ rdiv!(A::AbstractMatrix{T}, transD::Transpose{<:Any,<:Diagonal{T}}) where {T} =
488488
(/)(A::Union{StridedMatrix, AbstractTriangular}, D::Diagonal) =
489489
rdiv!((typeof(oneunit(eltype(D))/oneunit(eltype(A)))).(A), D)
490490

491-
(\)(F::Factorization, D::Diagonal) =
492-
ldiv!(F, Matrix{typeof(oneunit(eltype(D))/oneunit(eltype(F)))}(D))
493-
\(adjF::Adjoint{<:Any,<:Factorization}, D::Diagonal) =
494-
(F = adjF.parent; ldiv!(adjoint(F), Matrix{typeof(oneunit(eltype(D))/oneunit(eltype(F)))}(D)))
495-
(\)(A::Union{QR,QRCompactWY,QRPivoted}, B::Diagonal) =
496-
invoke(\, Tuple{Union{QR,QRCompactWY,QRPivoted}, AbstractVecOrMat}, A, B)
497-
498-
499491
@inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal)
500492
valA = A.diag; nA = length(valA)
501493
valB = B.diag; nB = length(valB)

stdlib/LinearAlgebra/src/factorization.jl

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f)
5959

6060
### General promotion rules
6161
Factorization{T}(F::Factorization{T}) where {T} = F
62+
# This is a bit odd since the return is not a Factorization but it works well in generic code
63+
Factorization{T}(A::Adjoint{<:Any,<:Factorization}) where {T} =
64+
adjoint(Factorization{T}(parent(A)))
6265
inv(F::Factorization{T}) where {T} = (n = size(F, 1); ldiv!(F, Matrix{T}(I, n, n)))
6366

6467
Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, 1:nfields(F); init=h)
@@ -96,40 +99,25 @@ function (/)(B::VecOrMat{Complex{T}}, F::Factorization{T}) where T<:BlasReal
9699
return copy(reinterpret(Complex{T}, x))
97100
end
98101

99-
function \(F::Factorization, B::AbstractVecOrMat)
102+
function \(F::Union{Factorization, Adjoint{<:Any,<:Factorization}}, B::AbstractVecOrMat)
100103
require_one_based_indexing(B)
101104
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
102105
BB = similar(B, TFB, size(B))
103106
copyto!(BB, B)
104107
ldiv!(F, BB)
105108
end
106-
function \(adjF::Adjoint{<:Any,<:Factorization}, B::AbstractVecOrMat)
107-
require_one_based_indexing(B)
108-
F = adjF.parent
109-
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
110-
BB = similar(B, TFB, size(B))
111-
copyto!(BB, B)
112-
ldiv!(adjoint(F), BB)
113-
end
114109

115-
function /(B::AbstractMatrix, F::Factorization)
110+
function /(B::AbstractMatrix, F::Union{Factorization, Adjoint{<:Any,<:Factorization}})
116111
require_one_based_indexing(B)
117112
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
118113
BB = similar(B, TFB, size(B))
119114
copyto!(BB, B)
120115
rdiv!(BB, F)
121116
end
122-
function /(B::AbstractMatrix, adjF::Adjoint{<:Any,<:Factorization})
123-
require_one_based_indexing(B)
124-
F = adjF.parent
125-
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
126-
BB = similar(B, TFB, size(B))
127-
copyto!(BB, B)
128-
rdiv!(BB, adjoint(F))
129-
end
130117
/(adjB::AdjointAbsVec, adjF::Adjoint{<:Any,<:Factorization}) = adjoint(adjF.parent \ adjB.parent)
131118
/(B::TransposeAbsVec, adjF::Adjoint{<:Any,<:Factorization}) = adjoint(adjF.parent \ adjoint(B))
132119

120+
133121
# support the same 3-arg idiom as in our other in-place A_*_B functions:
134122
function ldiv!(Y::AbstractVecOrMat, A::Factorization, B::AbstractVecOrMat)
135123
require_one_based_indexing(Y, B)

stdlib/LinearAlgebra/src/hessenberg.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -564,28 +564,30 @@ function AbstractMatrix(F::Hessenberg)
564564
end
565565
end
566566

567+
# adjoint(Q::HessenbergQ{<:Real})
568+
567569
lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
568570
LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
569-
rmul!(X::StridedMatrix{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} =
571+
rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} =
570572
LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
571573
lmul!(adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
572574
(Q = adjQ.parent; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X))
573-
rmul!(X::StridedMatrix{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} =
575+
rmul!(X::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} =
574576
(Q = adjQ.parent; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X))
575577

576578
lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
577579
LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.τ, X)
578-
rmul!(X::StridedMatrix{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} =
580+
rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} =
579581
LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.τ, X)
580582
lmul!(adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
581583
(Q = adjQ.parent; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X))
582-
rmul!(X::StridedMatrix{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} =
584+
rmul!(X::StridedVecOrMat{T}, adjQ::Adjoint{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} =
583585
(Q = adjQ.parent; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X))
584586

585587
lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')'
586-
rmul!(X::Adjoint{T,<:StridedMatrix{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')'
588+
rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')'
587589
lmul!(adjQ::Adjoint{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', adjQ')'
588-
rmul!(X::Adjoint{T,<:StridedMatrix{T}}, adjQ::Adjoint{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')'
590+
rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::Adjoint{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')'
589591

590592
# multiply x by the entries of M in the upper-k triangle, which contains
591593
# the entries of the upper-Hessenberg matrix H for k=-1

stdlib/LinearAlgebra/src/ldlt.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ function getproperty(F::LDLt, d::Symbol)
7777
end
7878
end
7979

80+
adjoint(F::LDLt{<:Real,<:SymTridiagonal}) = F
81+
adjoint(F::LDLt) = LDLt(copy(adjoint(F.data)))
82+
8083
function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LDLt)
8184
summary(io, F); println(io)
8285
println(io, "L factor:")

stdlib/LinearAlgebra/src/lq.jl

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ lq_eltype(::Type{T}) where {T} = typeof(zero(T) / sqrt(abs2(one(T))))
125125
copy(A::LQ) = LQ(copy(A.factors), copy(A.τ))
126126

127127
LQ{T}(A::LQ) where {T} = LQ(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ))
128-
Factorization{T}(A::LQ{T}) where {T} = A
129128
Factorization{T}(A::LQ) where {T} = LQ{T}(A)
129+
130130
AbstractMatrix(A::LQ) = A.L*A.Q
131131
AbstractArray(A::LQ) = AbstractMatrix(A)
132132
Matrix(A::LQ) = Array(AbstractArray(A))
@@ -194,7 +194,7 @@ function lmul!(A::LQ, B::StridedVecOrMat)
194194
end
195195
function *(A::LQ{TA}, B::StridedVecOrMat{TB}) where {TA,TB}
196196
TAB = promote_type(TA, TB)
197-
_cut_B(lmul!(Factorization{TAB}(A), copy_oftype(B, TAB)), 1:size(A,1))
197+
_cut_B(lmul!(convert(Factorization{TAB}, A), copy_oftype(B, TAB)), 1:size(A,1))
198198
end
199199

200200
## Multiplication by Q
@@ -318,17 +318,6 @@ _rightappdimmismatch(rowsorcols) =
318318
"or (2) the number of rows of that (LQPackedQ) matrix's internal representation ",
319319
"(the factorization's originating matrix's number of rows)")))
320320

321-
322-
function (\)(A::LQ{TA},B::StridedVecOrMat{TB}) where {TA,TB}
323-
S = promote_type(TA,TB)
324-
m, n = size(A)
325-
m n || throw(DimensionMismatch("LQ solver does not support overdetermined systems (more rows than columns)"))
326-
m == size(B,1) || throw(DimensionMismatch("Both inputs should have the same number of rows"))
327-
AA = Factorization{S}(A)
328-
X = _zeros(S, B, n)
329-
X[1:size(B, 1), :] = B
330-
return ldiv!(AA, X)
331-
end
332321
# With a real lhs and complex rhs with the same precision, we can reinterpret
333322
# the complex rhs as a real rhs with twice the number of columns
334323
function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal
@@ -342,12 +331,25 @@ function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal
342331
end
343332

344333

345-
function ldiv!(A::LQ{T}, B::StridedVecOrMat{T}) where T
334+
function ldiv!(A::LQ, B::StridedVecOrMat)
346335
require_one_based_indexing(B)
336+
m, n = size(A)
337+
m n || throw(DimensionMismatch("LQ solver does not support overdetermined systems (more rows than columns)"))
338+
347339
ldiv!(LowerTriangular(A.L), view(B, 1:size(A,1), axes(B,2)))
348340
return lmul!(adjoint(A.Q), B)
349341
end
350342

343+
function ldiv!(Fadj::Adjoint{<:Any,<:LQ}, B::StridedVecOrMat)
344+
require_one_based_indexing(B)
345+
m, n = size(Fadj)
346+
m >= n || throw(DimensionMismatch("solver does not support underdetermined systems (more columns than rows)"))
347+
348+
F = parent(Fadj)
349+
lmul!(F.Q, B)
350+
ldiv!(UpperTriangular(adjoint(F.L)), view(B, 1:size(F,1), axes(B,2)))
351+
return B
352+
end
351353

352354
# In LQ factorization, `Q` is expressed as the product of the adjoint of the
353355
# reflectors. Thus, `det` has to be conjugated.

stdlib/LinearAlgebra/src/qr.jl

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ end
472472
Base.propertynames(F::QRPivoted, private::Bool=false) =
473473
(:R, :Q, :p, :P, (private ? fieldnames(typeof(F)) : ())...)
474474

475+
adjoint(F::Union{QR,QRPivoted,QRCompactWY}) = Adjoint(F)
476+
475477
abstract type AbstractQ{T} <: AbstractMatrix{T} end
476478

477479
inv(Q::AbstractQ) = Q'
@@ -939,28 +941,35 @@ function ldiv!(A::QRPivoted, B::StridedMatrix)
939941
B
940942
end
941943

942-
# convenience methods
943-
## return only the solution of a least squares problem while avoiding promoting
944-
## vectors to matrices.
945-
_cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x
946-
_cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X
947-
948-
## append right hand side with zeros if necessary
949-
_zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n))
950-
_zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2))
944+
function _apply_permutation!(F::QRPivoted, B::AbstractVecOrMat)
945+
# Apply permutation but only to the top part of the solution vector since
946+
# it's padded with zeros for underdetermined problems
947+
B[1:length(F.p), :] = B[F.p, :]
948+
return B
949+
end
950+
_apply_permutation!(F::Factorization, B::AbstractVecOrMat) = B
951951

952-
function (\)(A::Union{QR{TA},QRCompactWY{TA},QRPivoted{TA}}, B::AbstractVecOrMat{TB}) where {TA,TB}
952+
function ldiv!(Fadj::Adjoint{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat)
953953
require_one_based_indexing(B)
954-
S = promote_type(TA,TB)
955-
m, n = size(A)
956-
m == size(B,1) || throw(DimensionMismatch("Both inputs should have the same number of rows"))
954+
m, n = size(Fadj)
957955

958-
AA = Factorization{S}(A)
956+
# We don't allow solutions overdetermined systems
957+
if m > n
958+
throw(DimensionMismatch("overdetermined systems are not supported"))
959+
end
960+
if n != size(B, 1)
961+
throw(DimensionMismatch("inputs should have the same number of rows"))
962+
end
963+
F = parent(Fadj)
959964

960-
X = _zeros(S, B, n)
961-
X[1:size(B, 1), :] = B
962-
ldiv!(AA, X)
963-
return _cut_B(X, 1:n)
965+
B = _apply_permutation!(F, B)
966+
967+
# For underdetermined system, the triangular solve should only be applied to the top
968+
# part of B that contains the rhs. For square problems, the view corresponds to B itself
969+
ldiv!(LowerTriangular(adjoint(F.R)), view(B, 1:size(F.R, 2), :))
970+
lmul!(F.Q, B)
971+
972+
return B
964973
end
965974

966975
# With a real lhs and complex rhs with the same precision, we can reinterpret the complex

stdlib/LinearAlgebra/src/svd.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ function SVD{T}(U::AbstractArray, S::AbstractVector{Tr}, Vt::AbstractArray) wher
7272
convert(AbstractArray{T}, Vt))
7373
end
7474

75+
SVD{T}(F::SVD) where {T} = SVD(
76+
convert(AbstractMatrix{T}, F.U),
77+
convert(AbstractVector{real(T)}, F.S),
78+
convert(AbstractMatrix{T}, F.Vt))
79+
Factorization{T}(F::SVD) where {T} = SVD{T}(F)
7580

7681
# iteration for destructuring into components
7782
Base.iterate(S::SVD) = (S.U, Val(:S))
@@ -235,10 +240,11 @@ svdvals(A::AbstractVector{<:BlasFloat}) = [norm(A)]
235240
svdvals(x::Number) = abs(x)
236241
svdvals(S::SVD{<:Any,T}) where {T} = (S.S)::Vector{T}
237242

238-
# SVD least squares
243+
### SVD least squares ###
239244
function ldiv!(A::SVD{T}, B::StridedVecOrMat) where T
245+
m, n = size(A)
240246
k = searchsortedlast(A.S, eps(real(T))*A.S[1], rev=true)
241-
view(A.Vt,1:k,:)' * (view(A.S,1:k) .\ (view(A.U,:,1:k)' * B))
247+
return mul!(view(B, 1:n, :), view(A.Vt, 1:k, :)', view(A.S, 1:k) .\ (view(A.U, :, 1:k)' * _cut_B(B, 1:m)))
242248
end
243249

244250
function inv(F::SVD{T}) where T
@@ -252,6 +258,10 @@ end
252258
size(A::SVD, dim::Integer) = dim == 1 ? size(A.U, dim) : size(A.Vt, dim)
253259
size(A::SVD) = (size(A, 1), size(A, 2))
254260

261+
function adjoint(F::SVD)
262+
return SVD(F.Vt', F.S, F.U')
263+
end
264+
255265
function show(io::IO, mime::MIME{Symbol("text/plain")}, F::SVD{<:Any,<:Any,<:AbstractArray})
256266
summary(io, F); println(io)
257267
println(io, "U factor:")

0 commit comments

Comments
 (0)