Skip to content

Commit e09e9c5

Browse files
committed
Merge pull request #2713 from jiahao/bdsqr
Provides Bidiagonal matrix type and specialized SVD
2 parents 2c50f21 + 57ae2f9 commit e09e9c5

File tree

8 files changed

+299
-19
lines changed

8 files changed

+299
-19
lines changed

base/exports.jl

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export
2222
Array,
2323
Associative,
2424
AsyncStream,
25+
Bidiagonal,
2526
BitArray,
2627
BigFloat,
2728
BigInt,

base/linalg.jl

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export
88
BunchKaufman,
99
SymTridiagonal,
1010
Tridiagonal,
11+
Bidiagonal,
1112
Woodbury,
1213
Factorization,
1314
BunchKaufman,
@@ -166,6 +167,7 @@ include("linalg/triangular.jl")
166167
include("linalg/hermitian.jl")
167168
include("linalg/woodbury.jl")
168169
include("linalg/tridiag.jl")
170+
include("linalg/bidiag.jl")
169171
include("linalg/diagonal.jl")
170172
include("linalg/rectfullpacked.jl")
171173

base/linalg/bidiag.jl

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#### Specialized matrix types ####
2+
3+
## Bidiagonal matrices
4+
5+
6+
type Bidiagonal{T} <: AbstractMatrix{T}
7+
dv::Vector{T} # diagonal
8+
ev::Vector{T} # sub/super diagonal
9+
isupper::Bool # is upper bidiagonal (true) or lower (false)
10+
function Bidiagonal{T}(dv::Vector{T}, ev::Vector{T}, isupper::Bool)
11+
if length(ev)!=length(dv)-1 error("dimension mismatch") end
12+
new(dv, ev, isupper)
13+
end
14+
end
15+
16+
Bidiagonal{T<:BlasFloat}(dv::Vector{T}, ev::Vector{T}, isupper::Bool)=Bidiagonal{T}(copy(dv), copy(ev), isupper)
17+
Bidiagonal{T}(dv::Vector{T}, ev::Vector{T}) = error("Did you want an upper or lower Bidiagonal? Try again with an additional true (upper) or false (lower) argument.")
18+
19+
20+
#Convert from BLAS uplo flag to boolean internal
21+
function Bidiagonal{T<:BlasFloat}(dv::Vector{T}, ev::Vector{T}, uplo::BlasChar)
22+
if uplo=='U'
23+
isupper = true
24+
elseif uplo=='L'
25+
isupper = false
26+
else
27+
error(string("Bidiagonal can only be upper 'U' or lower 'L' but you said '", uplo, "'"))
28+
end
29+
Bidiagonal{T}(copy(dv), copy(ev), isupper)
30+
end
31+
32+
function Bidiagonal{Td<:Number,Te<:Number}(dv::Vector{Td}, ev::Vector{Te}, isupper::Bool)
33+
T = promote(Td,Te)
34+
Bidiagonal(convert(Vector{T}, dv), convert(Vector{T}, ev), isupper)
35+
end
36+
37+
Bidiagonal(A::AbstractMatrix, isupper::Bool)=Bidiagonal(diag(A), diag(A, isupper?1:-1), isupper)
38+
39+
#Converting from Bidiagonal to dense Matrix
40+
full{T}(M::Bidiagonal{T}) = convert(Matrix{T}, M)
41+
convert{T}(::Type{Matrix{T}}, A::Bidiagonal{T})=diagm(A.dv) + diagm(A.ev, A.isupper?1:-1)
42+
promote_rule{T}(::Type{Matrix{T}}, ::Type{Bidiagonal{T}})=Matrix{T}
43+
promote_rule{T,S}(::Type{Matrix{T}}, ::Type{Bidiagonal{S}})=Matrix{promote_type(T,S)}
44+
45+
#Converting from Bidiagonal to Tridiagonal
46+
Tridiagonal{T}(M::Bidiagonal{T}) = convert(Tridiagonal{T}, M)
47+
function convert{T}(::Type{Tridiagonal{T}}, A::Bidiagonal{T})
48+
z = zeros(T, size(A)[1]-1)
49+
A.isupper ? Tridiagonal(A.ev, A.dv, z) : Tridiagonal(z, A.dv, A.ev)
50+
end
51+
promote_rule{T}(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{T}})=Tridiagonal{T}
52+
promote_rule{T,S}(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}})=Tridiagonal{promote_type(T,S)}
53+
54+
55+
function show(io::IO, M::Bidiagonal)
56+
println(io, summary(M), ":")
57+
print(io, "diag: ")
58+
print_matrix(io, (M.dv)')
59+
print(io, M.isupper?"\n sup: ":"\n sub: ")
60+
print_matrix(io, (M.ev)')
61+
end
62+
63+
size(M::Bidiagonal) = (length(M.dv), length(M.dv))
64+
size(M::Bidiagonal, d::Integer) = d<1 ? error("dimension out of range") : (d<=2 ? length(M.dv) : 1)
65+
66+
#Elementary operations
67+
copy(M::Bidiagonal) = Bidiagonal(copy(M.dv), copy(M.ev), copy(M.isupper))
68+
round(M::Bidiagonal) = Bidiagonal(round(M.dv), round(M.ev), M.isupper)
69+
iround(M::Bidiagonal) = Bidiagonal(iround(M.dv), iround(M.ev), M.isupper)
70+
71+
conj(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), M.isupper)
72+
transpose(M::Bidiagonal) = Bidiagonal(M.dv, M.ev, !M.isupper)
73+
ctranspose(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), !M.isupper)
74+
75+
function +(A::Bidiagonal, B::Bidiagonal)
76+
if A.isupper==B.isupper
77+
Bidiagonal(A.dv+B.dv, A.ev+B.ev, A.isupper)
78+
else #return tridiagonal
79+
if A.isupper #&& !B.isupper
80+
Tridiagonal(B.ev,A.dv+B.dv,A.ev)
81+
else
82+
Tridiagonal(A.ev,A.dv+B.dv,B.ev)
83+
end
84+
end
85+
end
86+
87+
function -(A::Bidiagonal, B::Bidiagonal)
88+
if A.isupper==B.isupper
89+
Bidiagonal(A.dv-B.dv, A.ev-B.ev, A.isupper)
90+
else #return tridiagonal
91+
if A.isupper #&& !B.isupper
92+
Tridiagonal(-B.ev,A.dv-B.dv,A.ev)
93+
else
94+
Tridiagonal(A.ev,A.dv-B.dv,-B.ev)
95+
end
96+
end
97+
end
98+
99+
-(A::Bidiagonal)=Bidiagonal(-A.dv,-A.ev)
100+
#XXX Returns dense matrix but really should be banded
101+
*(A::Bidiagonal, B::Bidiagonal) = full(A)*full(B)
102+
==(A::Bidiagonal, B::Bidiagonal) = (A.dv==B.dv) && (A.ev==B.ev) && (A.isupper==B.isupper)
103+
104+
# solver uses tridiagonal gtsv!
105+
function \{T<:BlasFloat}(M::Bidiagonal{T}, rhs::StridedVecOrMat{T})
106+
if stride(rhs, 1) == 1
107+
z = zeros(size(M)[1])
108+
if M.isupper
109+
return LAPACK.gtsv!(z, copy(M.dv), copy(M.ev), copy(rhs))
110+
else
111+
return LAPACK.gtsv!(copy(M.ev), copy(M.dv), z, copy(rhs))
112+
end
113+
end
114+
solve(M, rhs) # use the Julia "fallback"
115+
end
116+
117+
#Wrap bdsdc to compute singular values and vectors
118+
svdvals{T<:Real}(M::Bidiagonal{T})=LAPACK.bdsdc!(M.isupper?'U':'L', 'N', copy(M.dv), copy(M.ev))
119+
svd {T<:Real}(M::Bidiagonal{T})=LAPACK.bdsdc!(M.isupper?'U':'L', 'I', copy(M.dv), copy(M.ev))
120+

base/linalg/lapack.jl

+109
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,115 @@ for (syconv, syev, sysv, sytrf, sytri, sytrs, elty, relty) in
16591659
end
16601660
end
16611661

1662+
1663+
1664+
#Find the leading dimension
1665+
ld = x->max(1,stride(x,2))
1666+
function validate(uplo)
1667+
if !(uplo=='U' || uplo=='L')
1668+
error(string("Invalid UPLO: must be 'U' or 'L' but you said", uplo))
1669+
end
1670+
end
1671+
## (BD) Bidiagonal matrices - singular value decomposition
1672+
for (bdsqr, relty, elty) in
1673+
((:dbdsqr_,:Float64,:Float64),
1674+
(:sbdsqr_,:Float32,:Float32),
1675+
(:zbdsqr_,:Float64,:Complex128),
1676+
(:cbdsqr_,:Float32,:Complex64))
1677+
@eval begin
1678+
#*> DBDSQR computes the singular values and, optionally, the right and/or
1679+
#*> left singular vectors from the singular value decomposition (SVD) of
1680+
#*> a real N-by-N (upper or lower) bidiagonal matrix B using the implicit
1681+
#*> zero-shift QR algorithm.
1682+
function bdsqr!(uplo::BlasChar, d::Vector{$relty}, e_::Vector{$relty},
1683+
vt::StridedMatrix{$elty}, u::StridedMatrix{$elty}, c::StridedMatrix{$elty})
1684+
1685+
validate(uplo)
1686+
n = length(d)
1687+
if length(e_) != n-1 throw(DimensionMismatch("bdsqr!")) end
1688+
ncvt, nru, ncc = size(vt)[2], size(u)[1], size(c)[2]
1689+
ldvt, ldu, ldc = ld(vt), ld(u), ld(c)
1690+
work=Array($elty, 4n)
1691+
info=Array(BlasInt,1)
1692+
1693+
ccall(($(string(bdsqr)),liblapack), Void,
1694+
(Ptr{BlasChar}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt},
1695+
Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty},
1696+
Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}),
1697+
&uplo, &n, ncvt, &nru, &ncc,
1698+
d, e_, vt, &ldvt, u,
1699+
&ldu, c, &ldc, work, info)
1700+
1701+
if info[1] != 0 throw(LAPACKException(info[1])) end
1702+
d, vt, u, c #singular values in descending order, P**T * VT, U * Q, Q**T * C
1703+
end
1704+
end
1705+
end
1706+
1707+
#Defined only for real types
1708+
for (bdsdc, elty) in
1709+
((:dbdsdc_,:Float64),
1710+
(:sbdsdc_,:Float32))
1711+
@eval begin
1712+
#* DBDSDC computes the singular value decomposition (SVD) of a real
1713+
#* N-by-N (upper or lower) bidiagonal matrix B: B = U * S * VT,
1714+
#* using a divide and conquer method
1715+
#* .. Scalar Arguments ..
1716+
# CHARACTER COMPQ, UPLO
1717+
# INTEGER INFO, LDU, LDVT, N
1718+
#* ..
1719+
#* .. Array Arguments ..
1720+
# INTEGER IQ( * ), IWORK( * )
1721+
# DOUBLE PRECISION D( * ), E( * ), Q( * ), U( LDU, * ),
1722+
# $ VT( LDVT, * ), WORK( * )
1723+
function bdsdc!(uplo::BlasChar, compq::BlasChar, d::Vector{$elty}, e_::Vector{$elty})
1724+
validate(uplo)
1725+
n, ldiq, ldq, ldu, ldvt = length(d), 1, 1, 1, 1
1726+
if compq == 'N'
1727+
lwork = 4n
1728+
elseif compq == 'P'
1729+
warn("COMPQ='P' is not tested")
1730+
#TODO turn this into an actual LAPACK call
1731+
#smlsiz=ilaenv(9, $elty==:Float64 ? 'dbdsqr' : 'sbdsqr', string(uplo, compq), n,n,n,n)
1732+
smlsiz=100 #For now, completely overkill
1733+
ldq = n*(11+2*smlsiz+8*int(log((n/(smlsiz+1)))/log(2)))
1734+
ldiq = n*(3+3*int(log(n/(smlsiz+1))/log(2)))
1735+
lwork = 6n
1736+
elseif compq == 'I'
1737+
ldvt=ldu=max(1, n)
1738+
lwork=3*n^2 + 4n
1739+
else
1740+
error(string("Invalid COMPQ. Valid choices are 'N', 'P' or 'I' but you said '",compq,"'"))
1741+
end
1742+
u = Array($elty, (ldu, n))
1743+
vt= Array($elty, (ldvt, n))
1744+
q = Array($elty, ldq)
1745+
iq= Array(BlasInt, ldiq)
1746+
work =Array($elty, lwork)
1747+
iwork=Array(BlasInt, 7n)
1748+
info =Array(BlasInt, 1)
1749+
ccall(($(string(bdsdc)),liblapack), Void,
1750+
(Ptr{BlasChar}, Ptr{BlasChar}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty},
1751+
Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt},
1752+
Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}),
1753+
&uplo, &compq, &n, d, e_,
1754+
u, &ldu, vt, &ldvt,
1755+
q, iq, work, iwork, info)
1756+
1757+
if info[1] != 0 throw(LAPACKException(info[1])) end
1758+
if compq == 'N'
1759+
d
1760+
elseif compq == 'P'
1761+
d, q, iq
1762+
else #compq == 'I'
1763+
u, d, vt'
1764+
end
1765+
end
1766+
end
1767+
end
1768+
1769+
1770+
16621771
# New symmetric eigen solver
16631772
for (syevr, elty) in
16641773
((:dsyevr_,:Float64),

base/linalg/tridiag.jl

+5-13
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,9 @@ function SymTridiagonal{Td<:Number,Te<:Number}(dv::Vector{Td}, ev::Vector{Te})
2323
SymTridiagonal(convert(Vector{T}, dv), convert(Vector{T}, ev))
2424
end
2525

26-
SymTridiagonal(A::AbstractMatrix) = SymTridiagonal(diag(A), diag(A,1))
27-
26+
SymTridiagonal(M::AbstractMatrix) = diag(A,1)==diag(A,-1)?SymTridiagonal(diag(A), diag(A,1)):error("Matrix is not symmetric, cannot convert to SymTridiagonal")
2827
full{T}(M::SymTridiagonal{T}) = convert(Matrix{T}, M)
29-
function convert{T}(::Type{Matrix{T}}, S::SymTridiagonal{T})
30-
M = diagm(S.dv)
31-
for i in 1:length(S.ev)
32-
j = i + 1
33-
M[i,j] = M[j,i] = S.ev[i]
34-
end
35-
M
36-
end
28+
convert{T}(::Type{Matrix{T}}, M::SymTridiagonal{T})=diagm(M.dv)+diagm(M.ev,-1)+diagm(M.ev,1)
3729

3830
function show(io::IO, S::SymTridiagonal)
3931
println(io, summary(S), ":")
@@ -96,8 +88,8 @@ end
9688

9789
function Tridiagonal{T<:Number}(dl::Vector{T}, d::Vector{T}, du::Vector{T})
9890
N = length(d)
99-
if length(dl) != N-1 || length(du) != N-1
100-
error("The sub- and super-diagonals must have length N-1")
91+
if (length(dl) != N-1 || length(du) != N-1)
92+
error(string("Cannot make Tridiagonal from incompatible lengths of subdiagonal, diagonal and superdiagonal: (", length(dl), ", ", length(d), ", ", length(du),")"))
10193
end
10294
M = Tridiagonal{T}(N)
10395
M.dl = copy(dl)
@@ -160,7 +152,7 @@ ctranspose(M::Tridiagonal) = conj(transpose(M))
160152
==(A::SymTridiagonal, B::SymTridiagonal) = B==A
161153

162154
# Elementary operations that mix Tridiagonal and SymTridiagonal matrices
163-
Tridiagonal(A::SymTridiagonal) = Tridiagonal(A.dv, A.ev, A.dv)
155+
convert(::Type{Tridiagonal}, A::SymTridiagonal) = Tridiagonal(A.ev, A.dv, A.ev)
164156
+(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl+B.ev, A.d+B.dv, A.du+B.ev)
165157
+(A::SymTridiagonal, B::Tridiagonal) = Tridiagonal(A.ev+B.dl, A.dv+B.d, A.ev+B.du)
166158
-(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl-B.ev, A.d-B.dv, A.du-B.ev)

doc/helpdb.jl

+7
Original file line numberDiff line numberDiff line change
@@ -5094,6 +5094,13 @@
50945094
50955095
"),
50965096

5097+
("Linear Algebra","","Bidiagonal","Bidiagonal(dv, ev, isupper)
5098+
5099+
Construct an upper (isupper=true) or lower (isupper=false) bidiagonal matrix
5100+
from the given diagonal (dv) and off-diagonal (ev) vectors.
5101+
5102+
"),
5103+
50975104
("Linear Algebra","","Woodbury","Woodbury(A, U, C, V)
50985105
50995106
Construct a matrix in a form suitable for applying the Woodbury

doc/stdlib/linalg.rst

+5
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ Linear algebra functions in Julia are largely implemented by calling functions f
175175

176176
Construct a tridiagonal matrix from the lower diagonal, diagonal, and upper diagonal
177177

178+
.. function:: Bidiagonal(dv, ev, isupper)
179+
180+
Constructs an upper (isupper=true) or lower (isupper=false) bidiagonal matrix
181+
using the given diagonal (dv) and off-diagonal (ev) vectors
182+
178183
.. function:: Woodbury(A, U, C, V)
179184

180185
Construct a matrix in a form suitable for applying the Woodbury matrix identity

0 commit comments

Comments
 (0)