Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Add singular value decomposition #16

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/BlockArraysExtensions/BlockArraysExtensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -598,3 +598,28 @@
@capture(expr, array_[indices__])
return :(view!($(esc(array)), $(esc.(indices)...)))
end

# SVD additions
# -------------
using LinearAlgebra: Algorithm
using BlockArrays: BlockedMatrix

# svd first calls `eigencopy_oftype` to create something that can be in-place SVD'd
# Here, we hijack this system to determine if there is any structure we can exploit
# default: SVD is most efficient with BlockedArray
function eigencopy_oftype(A::AbstractBlockArray, S)
return BlockedMatrix{S}(A)

Check warning on line 611 in src/BlockArraysExtensions/BlockArraysExtensions.jl

View check run for this annotation

Codecov / codecov/patch

src/BlockArraysExtensions/BlockArraysExtensions.jl#L610-L611

Added lines #L610 - L611 were not covered by tests
end

function svd!(A::BlockedMatrix; full::Bool=false, alg::Algorithm=default_svd_alg(A))
F = svd!(parent(A); full, alg)

Check warning on line 615 in src/BlockArraysExtensions/BlockArraysExtensions.jl

View check run for this annotation

Codecov / codecov/patch

src/BlockArraysExtensions/BlockArraysExtensions.jl#L614-L615

Added lines #L614 - L615 were not covered by tests

# restore block pattern
m = length(F.S)
bax1, bax2, bax3 = axes(A, 1), blockedrange([m]), axes(A, 2)

Check warning on line 619 in src/BlockArraysExtensions/BlockArraysExtensions.jl

View check run for this annotation

Codecov / codecov/patch

src/BlockArraysExtensions/BlockArraysExtensions.jl#L618-L619

Added lines #L618 - L619 were not covered by tests

u = BlockedArray(F.U, (bax1, bax2))
s = BlockedVector(F.S, (bax2,))
vt = BlockedArray(F.Vt, (bax2, bax3))
return SVD(u, s, vt)

Check warning on line 624 in src/BlockArraysExtensions/BlockArraysExtensions.jl

View check run for this annotation

Codecov / codecov/patch

src/BlockArraysExtensions/BlockArraysExtensions.jl#L621-L624

Added lines #L621 - L624 were not covered by tests
end
14 changes: 14 additions & 0 deletions src/BlockSparseArrays.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
module BlockSparseArrays

# factorizations
include("factorizations/svd.jl")

# possible upstream contributions
include("BlockArraysExtensions/BlockArraysExtensions.jl")

# interface functions that don't have to specialize
include("blocksparsearrayinterface/blocksparsearrayinterface.jl")
include("blocksparsearrayinterface/linearalgebra.jl")
include("blocksparsearrayinterface/getunstoredblock.jl")
Expand All @@ -8,6 +15,8 @@ include("blocksparsearrayinterface/map.jl")
include("blocksparsearrayinterface/arraylayouts.jl")
include("blocksparsearrayinterface/views.jl")
include("blocksparsearrayinterface/cat.jl")

# functions defined for any abstractblocksparsearray
include("abstractblocksparsearray/abstractblocksparsearray.jl")
include("abstractblocksparsearray/wrappedabstractblocksparsearray.jl")
include("abstractblocksparsearray/abstractblocksparsematrix.jl")
Expand All @@ -19,7 +28,12 @@ include("abstractblocksparsearray/broadcast.jl")
include("abstractblocksparsearray/map.jl")
include("abstractblocksparsearray/linearalgebra.jl")
include("abstractblocksparsearray/cat.jl")

# functions specifically for BlockSparseArray
include("blocksparsearray/defaults.jl")
include("blocksparsearray/blocksparsearray.jl")
include("blocksparsearray/blockdiagonalarray.jl")

include("BlockArraysSparseArraysBaseExt/BlockArraysSparseArraysBaseExt.jl")

end
21 changes: 21 additions & 0 deletions src/abstractblocksparsearray/abstractblocksparsematrix.jl
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
const AbstractBlockSparseMatrix{T} = AbstractBlockSparseArray{T,2}

# SVD is implemented by trying to
# 1. Attempt to find a block-diagonal implementation by permuting
# 2. Fallback to AbstractBlockArray implementation via BlockedArray
function svd(

Check warning on line 6 in src/abstractblocksparsearray/abstractblocksparsematrix.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractblocksparsearray/abstractblocksparsematrix.jl#L6

Added line #L6 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I see this is designed around a private BlockSparseArrays.svd function, is the plan to overload LinearAlgebra.svd? Probably we should define this as:

@interface ::AbstractBlockSparseArrayInterface function svd(...)

(I would like to make the AbstractArrayInterface interface types take an ndims parameter so it could be AbstractBlockSparseMatrixInterface but that isn't supported yet) and then derive that function for LinearAlgebra.svd(::AnyAbstractBlockSparseMatrix) in the style of Derive.jl.

A::AbstractBlockSparseMatrix; full::Bool=false, alg::Algorithm=default_svd_alg(A)
)
T = LinearAlgebra.eigtype(eltype(A))
A′ = try_to_blockdiagonal(A)

Check warning on line 10 in src/abstractblocksparsearray/abstractblocksparsematrix.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractblocksparsearray/abstractblocksparsematrix.jl#L9-L10

Added lines #L9 - L10 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it confusing that this is call A′ but if it is a blocked permutation it is actually a composite object of the block diagonal matrix and the block permutations, we should make that clearer in the variable name, i.e. A′_and_blockperms.


if isnothing(A′)

Check warning on line 12 in src/abstractblocksparsearray/abstractblocksparsematrix.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractblocksparsearray/abstractblocksparsematrix.jl#L12

Added line #L12 was not covered by tests
# not block-diagonal, fall back to dense case
Adense = eigencopy_oftype(A, T)
return svd!(Adense; full, alg)

Check warning on line 15 in src/abstractblocksparsearray/abstractblocksparsematrix.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractblocksparsearray/abstractblocksparsematrix.jl#L14-L15

Added lines #L14 - L15 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this output an SVD object?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I see from the definition below that it does.)

end

# compute block-by-block and permute back
A″, (I, J) = A′
F = svd!(eigencopy_oftype(A″, T); full, alg)
return SVD(F.U[Block.(I), Block.(J)], F.S, F.Vt)

Check warning on line 21 in src/abstractblocksparsearray/abstractblocksparsematrix.jl

View check run for this annotation

Codecov / codecov/patch

src/abstractblocksparsearray/abstractblocksparsematrix.jl#L19-L21

Added lines #L19 - L21 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I and J already be designed as lists of Block?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it looks like that is already the case so it seems like Block.(...) may not be necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by this code, I would have though it would be something like:

SVD(F.U[I, :], F.S, F.Vt[:, J])

i.e. I and J are block permutations of the outer spaces but maybe I have the wrong mental model for how this function is being designed.

end
59 changes: 59 additions & 0 deletions src/blocksparsearray/blockdiagonalarray.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# type alias for block-diagonal
using LinearAlgebra: Diagonal

const BlockDiagonal{T,A,Axes,V<:AbstractVector{A}} = BlockSparseMatrix{
T,A,Diagonal{A,V},Axes
}

function BlockDiagonal(blocks::AbstractVector{<:AbstractMatrix})
return BlockSparseArray(

Check warning on line 9 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L8-L9

Added lines #L8 - L9 were not covered by tests
Diagonal(blocks), (blockedrange(size.(blocks, 1)), blockedrange(size.(blocks, 2)))
)
end

# Cast to block-diagonal implementation if permuted-blockdiagonal
function try_to_blockdiagonal_perm(A)
inds = map(x -> Int.(Tuple(x)), vec(collect(block_stored_indices(A))))
I = first.(inds)
allunique(I) || return nothing
J = last.(inds)
p = sortperm(J)
Jsorted = J[p]
allunique(Jsorted) || return nothing
return Block.(I[p], Jsorted)

Check warning on line 23 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L15-L23

Added lines #L15 - L23 were not covered by tests
end

"""
try_to_blockdiagonal(A)

Attempt to find a permutation of blocks that makes `A` blockdiagonal. If unsuccesful,
returns nothing, otherwise returns both the blockdiagonal `B` as well as the permutation `I, J`.
"""
function try_to_blockdiagonal(A::AbstractBlockSparseMatrix)
perm = try_to_blockdiagonal_perm(A)
isnothing(perm) && return perm
I = first.(Tuple.(perm))
J = last.(Tuple.(perm))
diagblocks = map(invperm(I), J) do i, j
return A[Block(i, j)]

Check warning on line 38 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L32-L38

Added lines #L32 - L38 were not covered by tests
end
return BlockDiagonal(diagblocks), perm

Check warning on line 40 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L40

Added line #L40 was not covered by tests
end

# SVD implementation
function eigencopy_oftype(A::BlockDiagonal, S)
diag = map(Base.Fix2(eigencopy_oftype, S), A.blocks.diag)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make a proper function/API to access A.blocks.diag.

Some ideas are:

  1. eachstoredblock
  2. DiagonalArrays.diagview(blocks(a)) (from https://github.com/ITensor/DiagonalArrays.jl)

Copy link
Member

@mtfishman mtfishman Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ideally we would have eachstoredblock special cased to output the same thing as diagview(blocks(a)) in the case of block diagonal arrays, though that may be a more involved thing beyond this PR. It may just require defining storedvalues(a::AbstractDiagonalArray/Diagonal) = diagview(a), and then eachstoredblock(a) is already defined as storedvalues(blocks(a)) so then eachstoredblock(a) would output diagview(blocks(a)).

return BlockDiagonal(diag)

Check warning on line 46 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L44-L46

Added lines #L44 - L46 were not covered by tests
end

function svd(A::BlockDiagonal; kwargs...)
return svd!(eigencopy_oftype(A, LinearAlgebra.eigtype(eltype(A))); kwargs...)

Check warning on line 50 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L49-L50

Added lines #L49 - L50 were not covered by tests
end
function svd!(A::BlockDiagonal; full::Bool=false, alg::Algorithm=default_svd_alg(A))

Check warning on line 52 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L52

Added line #L52 was not covered by tests
# TODO: handle full
F = map(a -> svd!(a; full, alg), blocks(A).diag)
Us = map(Base.Fix2(getproperty, :U), F)
Ss = map(Base.Fix2(getproperty, :S), F)
Vts = map(Base.Fix2(getproperty, :Vt), F)
return SVD(BlockDiagonal(Us), mortar(Ss), BlockDiagonal(Vts))

Check warning on line 58 in src/blocksparsearray/blockdiagonalarray.jl

View check run for this annotation

Codecov / codecov/patch

src/blocksparsearray/blockdiagonalarray.jl#L54-L58

Added lines #L54 - L58 were not covered by tests
end
206 changes: 206 additions & 0 deletions src/factorizations/svd.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using LinearAlgebra:
LinearAlgebra, Factorization, Algorithm, default_svd_alg, Adjoint, Transpose
using BlockArrays: AbstractBlockMatrix, BlockedArray, BlockedMatrix, BlockedVector
using BlockArrays: BlockLayout

# Singular Value Decomposition:
# need new type to deal with U and V having possible different types
# this is basically a carbon copy of the LinearAlgebra implementation.
# additionally, by default we implement a fallback to the LinearAlgebra implementation
# in hope to support as many foreign types as possible that chose to extend those methods.

# TODO: add this to MatrixFactorizations
# TODO: decide where this goes
# TODO: decide whether or not to restrict types to be blocked.
"""
SVD <: Factorization

Matrix factorization type of the singular value decomposition (SVD) of a matrix `A`.
This is the return type of [`svd(_)`](@ref), the corresponding matrix factorization function.

If `F::SVD` is the factorization object, `U`, `S`, `V` and `Vt` can be obtained
via `F.U`, `F.S`, `F.V` and `F.Vt`, such that `A = U * Diagonal(S) * Vt`.
The singular values in `S` are sorted in descending order.

Iterating the decomposition produces the components `U`, `S`, and `V`.

# Examples
```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 Matrix{Float64}:
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 = svd(A)
SVD{Float64, Float64, Matrix{Float64}, Vector{Float64}}
U factor:
4×4 Matrix{Float64}:
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
singular values:
4-element Vector{Float64}:
3.0
2.23606797749979
2.0
0.0
Vt factor:
4×5 Matrix{Float64}:
-0.0 0.0 1.0 -0.0 0.0
0.447214 0.0 0.0 0.0 0.894427
0.0 -1.0 0.0 0.0 0.0
0.0 0.0 0.0 1.0 0.0

julia> F.U * Diagonal(F.S) * F.Vt
4×5 Matrix{Float64}:
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 = F; # destructuring via iteration

julia> u == F.U && s == F.S && v == F.V
true
```
"""
struct SVD{T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr},N<:AbstractArray{T}} <:
Factorization{T}
U::M
S::C
Vt::N
function SVD{T,Tr,M,C,N}(

Check warning on line 75 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L75

Added line #L75 was not covered by tests
U, S, Vt
) where {T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr},N<:AbstractArray{T}}
Base.require_one_based_indexing(U, S, Vt)
return new{T,Tr,M,C,N}(U, S, Vt)

Check warning on line 79 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L78-L79

Added lines #L78 - L79 were not covered by tests
end
end
function SVD(U::AbstractArray{T}, S::AbstractVector{Tr}, Vt::AbstractArray{T}) where {T,Tr}
return SVD{T,Tr,typeof(U),typeof(S),typeof(Vt)}(U, S, Vt)

Check warning on line 83 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L82-L83

Added lines #L82 - L83 were not covered by tests
end
function SVD{T}(U::AbstractArray, S::AbstractVector{Tr}, Vt::AbstractArray) where {T,Tr}
return SVD(

Check warning on line 86 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L85-L86

Added lines #L85 - L86 were not covered by tests
convert(AbstractArray{T}, U),
convert(AbstractVector{Tr}, S),
convert(AbstractArray{T}, Vt),
)
end

function SVD{T}(F::SVD) where {T}
return SVD(

Check warning on line 94 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L93-L94

Added lines #L93 - L94 were not covered by tests
convert(AbstractMatrix{T}, F.U),
convert(AbstractVector{real(T)}, F.S),
convert(AbstractMatrix{T}, F.Vt),
)
end
LinearAlgebra.Factorization{T}(F::SVD) where {T} = SVD{T}(F)

Check warning on line 100 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L100

Added line #L100 was not covered by tests

# iteration for destructuring into components
Base.iterate(S::SVD) = (S.U, Val(:S))
Base.iterate(S::SVD, ::Val{:S}) = (S.S, Val(:V))
Base.iterate(S::SVD, ::Val{:V}) = (S.V, Val(:done))
Base.iterate(::SVD, ::Val{:done}) = nothing

Check warning on line 106 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L103-L106

Added lines #L103 - L106 were not covered by tests

function Base.getproperty(F::SVD, d::Symbol)
if d === :V
return getfield(F, :Vt)'

Check warning on line 110 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L108-L110

Added lines #L108 - L110 were not covered by tests
else
return getfield(F, d)

Check warning on line 112 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L112

Added line #L112 was not covered by tests
end
end

function Base.propertynames(F::SVD, private::Bool=false)
return private ? (:V, fieldnames(typeof(F))...) : (:U, :S, :V, :Vt)

Check warning on line 117 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L116-L117

Added lines #L116 - L117 were not covered by tests
end

Base.size(A::SVD, dim::Integer) = dim == 1 ? size(A.U, dim) : size(A.Vt, dim)
Base.size(A::SVD) = (size(A, 1), size(A, 2))

Check warning on line 121 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L120-L121

Added lines #L120 - L121 were not covered by tests

function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, F::SVD)
summary(io, F)
println(io)
println(io, "U factor:")
show(io, mime, F.U)
println(io, "\nsingular values:")
show(io, mime, F.S)
println(io, "\nVt factor:")
return show(io, mime, F.Vt)

Check warning on line 131 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L123-L131

Added lines #L123 - L131 were not covered by tests
end

Base.adjoint(usv::SVD) = SVD(adjoint(usv.Vt), usv.S, adjoint(usv.U))
Base.transpose(usv::SVD) = SVD(transpose(usv.Vt), usv.S, transpose(usv.U))

Check warning on line 135 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L134-L135

Added lines #L134 - L135 were not covered by tests

# Conversion
Base.AbstractMatrix(F::SVD) = (F.U * Diagonal(F.S)) * F.Vt
Base.AbstractArray(F::SVD) = AbstractMatrix(F)
Base.Matrix(F::SVD) = Array(AbstractArray(F))
Base.Array(F::SVD) = Matrix(F)
SVD(usv::SVD) = usv
SVD(usv::LinearAlgebra.SVD) = SVD(usv.U, usv.S, usv.Vt)

Check warning on line 143 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L138-L143

Added lines #L138 - L143 were not covered by tests

# functions default to LinearAlgebra
# ----------------------------------
"""
svd!(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> SVD

`svd!` is the same as [`svd`](@ref), but saves space by
overwriting the input `A`, instead of creating a copy. See documentation of [`svd`](@ref) for details.
"""
svd!(A; kwargs...) = SVD(LinearAlgebra.svd!(A; kwargs...))

Check warning on line 153 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L153

Added line #L153 was not covered by tests

"""
svd(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> 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 * Diagonal(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.

Iterating the decomposition produces the components `U`, `S`, and `V`.

If `full = false` (default), a "thin" SVD is returned. For an ``M
\\times N`` matrix `A`, in the full factorization `U` is ``M \\times M``
and `V` is ``N \\times N``, while in the thin factorization `U` is ``M
\\times K`` and `V` is ``N \\times K``, where ``K = \\min(M,N)`` is the
number of singular values.

`alg` specifies which algorithm and LAPACK method to use for SVD:
- `alg = DivideAndConquer()` (default): Calls `LAPACK.gesdd!`.
- `alg = QRIteration()`: Calls `LAPACK.gesvd!` (typically slower but more accurate) .

!!! compat "Julia 1.3"
The `alg` keyword argument requires Julia 1.3 or later.

# Examples
```jldoctest
julia> A = rand(4,3);

julia> F = svd(A); # Store the Factorization Object

julia> A ≈ F.U * Diagonal(F.S) * F.Vt
true

julia> U, S, V = F; # destructuring via iteration

julia> A ≈ U * Diagonal(S) * V'
true

julia> Uonly, = svd(A); # Store U only

julia> Uonly == U
true
```
"""
svd(A; kwargs...) =

Check warning on line 200 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L200

Added line #L200 was not covered by tests
SVD(svd!(eigencopy_oftype(A, LinearAlgebra.eigtype(eltype(A))); kwargs...))

LinearAlgebra.svdvals(usv::SVD{<:Any,T}) where {T} = (usv.S)::Vector{T}

Check warning on line 203 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L203

Added line #L203 was not covered by tests

# Added here to avoid type-piracy
eigencopy_oftype(A, S) = LinearAlgebra.eigencopy_oftype(A, S)

Check warning on line 206 in src/factorizations/svd.jl

View check run for this annotation

Codecov / codecov/patch

src/factorizations/svd.jl#L206

Added line #L206 was not covered by tests
Loading
Loading