Skip to content

Commit

Permalink
deprecate orthogonal decomposition keyword "thin" to "full" (#24279)
Browse files Browse the repository at this point in the history
* Deprecate keyword argument "thin" in favor of "full" in svd methods.
* Deprecate keyword argument "thin" in favor of "full" in qr methods.
* Deprecate keyword argument "thin" in favor of "full" in lq methods.
  • Loading branch information
Sacha0 authored Oct 28, 2017
1 parent 5bc1101 commit 2044957
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 75 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ Deprecated or removed
* The `cholfact`/`cholfact!` methods that accepted an `uplo` symbol have been deprecated
in favor of using `Hermitian` (or `Symmetric`) views ([#22187], [#22188]).

* The `thin` keyword argument for orthogonal decomposition methods has
been deprecated in favor of `full`, which has the opposite meaning:
`thin == true` if and only if `full == false` ([#24279]).

* `isposdef(A::AbstractMatrix, UL::Symbol)` and `isposdef!(A::AbstractMatrix, UL::Symbol)`
have been deprecated in favor of `isposdef(Hermitian(A, UL))` and `isposdef!(Hermitian(A, UL))`
respectively ([#22245]).
Expand Down
5 changes: 5 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,11 @@ end
@deprecate strwidth textwidth
@deprecate charwidth textwidth

# TODO: after 0.7, remove thin keyword argument and associated logic from...
# (1) base/linalg/svd.jl
# (2) base/linalg/qr.jl
# (3) base/linalg/lq.jl

@deprecate find(x::Number) find(!iszero, x)
@deprecate findnext(A, v, i::Integer) findnext(equalto(v), A, i)
@deprecate findfirst(A, v) findfirst(equalto(v), A)
Expand Down
20 changes: 18 additions & 2 deletions base/linalg/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,27 @@ similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spze

#Singular values
svdvals!(M::Bidiagonal{<:BlasReal}) = LAPACK.bdsdc!(M.uplo, 'N', M.dv, M.ev)[1]
function svdfact!(M::Bidiagonal{<:BlasReal}; thin::Bool=true)
function svdfact!(M::Bidiagonal{<:BlasReal}; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact!(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact!(A; full = $(!thin))`."), :svdfact!)
full::Bool = !thin
end
d, e, U, Vt, Q, iQ = LAPACK.bdsdc!(M.uplo, 'I', M.dv, M.ev)
SVD(U, d, Vt)
end
svdfact(M::Bidiagonal; thin::Bool=true) = svdfact!(copy(M),thin=thin)
function svdfact(M::Bidiagonal; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact(A; full = $(!thin))`."), :svdfact)
full::Bool = !thin
end
return svdfact!(copy(M), full = full)
end

####################
# Generic routines #
Expand Down
4 changes: 2 additions & 2 deletions base/linalg/dense.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ function pinv(A::StridedMatrix{T}, tol::Real) where T
return B
end
end
SVD = svdfact(A, thin=true)
SVD = svdfact(A, full = false)
Stype = eltype(SVD.S)
Sinv = zeros(Stype, length(SVD.S))
index = SVD.S .> tol*maximum(SVD.S)
Expand Down Expand Up @@ -1305,7 +1305,7 @@ julia> nullspace(M)
function nullspace(A::StridedMatrix{T}) where T
m, n = size(A)
(m == 0 || n == 0) && return eye(T, n)
SVD = svdfact(A, thin = false)
SVD = svdfact(A, full = true)
indstart = sum(SVD.S .> max(m,n)*maximum(SVD.S)*eps(eltype(SVD.S))) + 1
return SVD.Vt[indstart:end,:]'
end
Expand Down
29 changes: 18 additions & 11 deletions base/linalg/lq.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,31 @@ lqfact(A::StridedMatrix{<:BlasFloat}) = lqfact!(copy(A))
lqfact(x::Number) = lqfact(fill(x,1,1))

"""
lq(A; [thin=true]) -> L, Q
lq(A; full = false) -> L, Q
Perform an LQ factorization of `A` such that `A = L*Q`. The default is to compute
a "thin" factorization. The LQ factorization is the QR factorization of `A.'`.
`L` is not extendedwith zeros if the explicit, square form of `Q`
is requested via `thin = false`.
Perform an LQ factorization of `A` such that `A = L*Q`. The default (`full = false`)
computes a factorization with possibly-rectangular `L` and `Q`, commonly the "thin"
factorization. The LQ factorization is the QR factorization of `A.'`. If the explicit,
full/square form of `Q` is requested via `full = true`, `L` is not extended with zeros.
!!! note
While in QR factorization the "thin" factorization is so named due to yielding
either a square or "tall"/"thin" factor `Q`, in LQ factorization the "thin"
factorization somewhat confusingly produces either a square or "short"/"wide"
factor `Q`. "Thin" factorizations more broadly are also (more descriptively)
referred to as "truncated" or "reduced" factorizatons.
either a square or "tall"/"thin" rectangular factor `Q`, in LQ factorization the
"thin" factorization somewhat confusingly produces either a square or "short"/"wide"
rectangular factor `Q`. "Thin" factorizations more broadly are also
referred to as "reduced" factorizatons.
"""
function lq(A::Union{Number,AbstractMatrix}; thin::Bool = true)
function lq(A::Union{Number,AbstractMatrix}; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `lq(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `lq(A; full = $(!thin))`."), :lq)
full::Bool = !thin
end
F = lqfact(A)
L, Q = F[:L], F[:Q]
return L, thin ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 2)))
return L, !full ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 2)))
end

copy(A::LQ) = LQ(copy(A.factors), copy(A.τ))
Expand Down
29 changes: 19 additions & 10 deletions base/linalg/qr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ The following functions are available for the `QR` objects: [`inv`](@ref), [`siz
and [`\\`](@ref). When `A` is rectangular, `\\` will return a least squares
solution and if the solution is not unique, the one with smallest norm is returned.
Multiplication with respect to either thin or full `Q` is allowed, i.e. both `F[:Q]*F[:R]`
Multiplication with respect to either full/square or non-full/square `Q` is allowed, i.e. both `F[:Q]*F[:R]`
and `F[:Q]*A` are supported. A `Q` matrix can be converted into a regular matrix with
[`Matrix`](@ref).
Expand Down Expand Up @@ -305,24 +305,33 @@ end
qrfact(x::Number) = qrfact(fill(x,1,1))

"""
qr(A, pivot=Val(false); thin::Bool=true) -> Q, R, [p]
qr(A, pivot=Val(false); full::Bool = false) -> Q, R, [p]
Compute the (pivoted) QR factorization of `A` such that either `A = Q*R` or `A[:,p] = Q*R`.
Also see [`qrfact`](@ref).
The default is to compute a thin factorization. Note that `R` is not
extended with zeros when the full `Q` is requested.
The default is to compute a "thin" factorization. Note that `R` is not
extended with zeros when a full/square orthogonal factor `Q` is requested (via `full = true`).
"""
qr(A::Union{Number, AbstractMatrix}, pivot::Union{Val{false}, Val{true}}=Val(false); thin::Bool=true) =
_qr(A, pivot, thin=thin)
function _qr(A::Union{Number, AbstractMatrix}, ::Val{false}; thin::Bool=true)
function qr(A::Union{Number,AbstractMatrix}, pivot::Union{Val{false},Val{true}} = Val(false);
full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `qr(A, pivot; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `qr(A, pivot; full = $(!thin))`."), :qr)
full::Bool = !thin
end
return _qr(A, pivot, full = full)
end
function _qr(A::Union{Number,AbstractMatrix}, ::Val{false}; full::Bool = false)
F = qrfact(A, Val(false))
Q, R = getq(F), F[:R]::Matrix{eltype(F)}
return (thin ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 1)))), R
return (!full ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 1)))), R
end
function _qr(A::Union{Number, AbstractMatrix}, ::Val{true}; thin::Bool=true)
function _qr(A::Union{Number, AbstractMatrix}, ::Val{true}; full::Bool = false)
F = qrfact(A, Val(true))
Q, R, p = getq(F), F[:R]::Matrix{eltype(F)}, F[:p]::Vector{BlasInt}
return (thin ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 1)))), R, p
return (!full ? Array(Q) : A_mul_B!(Q, eye(eltype(Q), size(Q.factors, 1)))), R, p
end

"""
Expand Down
86 changes: 67 additions & 19 deletions base/linalg/svd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,30 @@ 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!(A; full::Bool = false) -> 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
function svdfact!(A::StridedMatrix{T}; full::Bool = false, thin::Union{Bool,Void} = nothing) where T<:BlasFloat
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact!(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact!(A; full = $(!thin))`."), :svdfact!)
full::Bool = !thin
end
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))
u,s,vt = (eye(T, m, full ? m : n), real(zeros(T,0)), eye(T,n,n))
else
u,s,vt = LAPACK.gesdd!(thin ? 'S' : 'A', A)
u,s,vt = LAPACK.gesdd!(full ? 'A' : 'S', A)
end
SVD(u,s,vt)
end

"""
svdfact(A; thin::Bool=true) -> SVD
svdfact(A; full::Bool = false) -> SVD
Compute the singular value decomposition (SVD) of `A` and return an `SVD` object.
Expand All @@ -36,9 +43,9 @@ Compute the singular value decomposition (SVD) of `A` and return an `SVD` object
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.
If `full = false` (default), a "thin" SVD is returned. For a
``M \\times N`` matrix `A`, `U` is ``M \\times M`` for a "full" SVD (`full = true`) and
``M \\times \\min(M, N)`` for a "thin" SVD.
# Examples
```jldoctest
Expand All @@ -59,22 +66,47 @@ julia> F[:U] * Diagonal(F[:S]) * F[:Vt]
0.0 2.0 0.0 0.0 0.0
```
"""
function svdfact(A::StridedVecOrMat{T}; thin::Bool = true) where T
function svdfact(A::StridedVecOrMat{T}; full::Bool = false, thin::Union{Bool,Void} = nothing) where T
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact(A; full = $(!thin))`."), :svdfact)
full::Bool = !thin
end
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
svdfact!(copy_oftype(A, S), thin = thin)
svdfact!(copy_oftype(A, S), full = full)
end
function svdfact(x::Number; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact(A; full = $(!thin))`."), :svdfact)
full::Bool = !thin
end
return SVD(x == 0 ? fill(one(x), 1, 1) : fill(x/abs(x), 1, 1), [abs(x)], fill(one(x), 1, 1))
end
function svdfact(x::Integer; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svdfact(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svdfact(A; full = $(!thin))`."), :svdfact)
full::Bool = !thin
end
return svdfact(float(x), full = full)
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
svd(A; full::Bool = false) -> U, S, V
Computes the SVD of `A`, returning `U`, vector `S`, and `V` such that
`A == U * Diagonal(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.
If `full = false` (default), a "thin" SVD is returned. For a ``M \\times N`` matrix
`A`, `U` is ``M \\times M`` for a "full" SVD (`full = true`) 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
Expand All @@ -99,11 +131,27 @@ julia> U * Diagonal(S) * V'
0.0 2.0 0.0 0.0 0.0
```
"""
function svd(A::AbstractArray; thin::Bool=true)
F = svdfact(A, thin=thin)
function svd(A::AbstractArray; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svd(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g `svd(A; full = $(!thin))`."), :svd)
full::Bool = !thin
end
F = svdfact(A, full = full)
F.U, F.S, F.Vt'
end
svd(x::Number; thin::Bool=true) = first.(svd(fill(x, 1, 1)))
function svd(x::Number; full::Bool = false, thin::Union{Bool,Void} = nothing)
# DEPRECATION TODO: remove deprecated thin argument and associated logic after 0.7
if thin != nothing
Base.depwarn(string("the `thin` keyword argument in `svd(A; thin = $(thin))` has ",
"been deprecated in favor of `full`, which has the opposite meaning, ",
"e.g. `svd(A; full = $(!thin))`."), :svd)
full::Bool = !thin
end
return first.(svd(fill(x, 1, 1)))
end

function getindex(F::SVD, d::Symbol)
if d == :U
Expand Down
Loading

0 comments on commit 2044957

Please sign in to comment.