Skip to content

Commit eb046c6

Browse files
committed
QR fixes
1 parent e4e3f39 commit eb046c6

File tree

3 files changed

+44
-55
lines changed

3 files changed

+44
-55
lines changed

src/qr.jl

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,54 @@
1-
_thin_must_hold(thin) =
2-
thin || throw(ArgumentError("For the sake of type stability, `thin = true` must hold."))
1+
# define our own struct since LinearAlgebra.QR are restricted to Matrix
2+
struct QR{Q,R}
3+
Q::Q
4+
R::R
5+
end
36

4-
"""
5-
qr(A::StaticMatrix, pivot=Val{false}; thin=true) -> Q, R, [p]
7+
# iteration for destructuring into components
8+
Base.iterate(S::QR) = (S.Q, Val(:R))
9+
Base.iterate(S::QR, ::Val{:R}) = (S.R, Val(:done))
10+
Base.iterate(S::QR, ::Val{:done}) = nothing
611

7-
Compute the QR factorization of `A` such that `A = Q*R` or `A[:,p] = Q*R`, see [`qr`](@ref).
8-
This function does not support `thin=false` keyword option due to type inference instability.
9-
To use this option call `qr(A, pivot, Val{false})` instead.
1012
"""
11-
@inline function qr(A::StaticMatrix, pivot::Union{Val{false}, Val{true}} = Val(false); thin::Bool=true)
12-
_thin_must_hold(thin)
13-
return _qr(Size(A), A, pivot, Val(true))
14-
end
13+
qr(A::StaticMatrix, pivot=Val(false))
1514
15+
Compute the QR factorization of `A`. The factors can be obtain by iteration:
1616
17-
@inline qr(A::StaticMatrix, pivot::Union{Val{false}, Val{true}}, thin::Union{Val{false}, Val{true}}) = _qr(Size(A), A, pivot, thin)
17+
```julia
18+
julia> A = @SMatrix rand(3,4);
1819
20+
julia> Q, R = qr(A);
1921
20-
_qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T))))
22+
julia> Q * R ≈ A
23+
true
24+
```
2125
26+
or by using `getfield`:
2227
23-
@generated function _qr(::Size{sA}, A::StaticMatrix{<:Any, <:Any, TA}, pivot::Union{Val{false}, Val{true}} = Val(false), thin::Union{Val{false}, Val{true}} = Val(true)) where {sA, TA}
28+
```julia
29+
julia> F = qr(A);
2430
25-
isthin = thin == Val(true)
31+
julia> F.Q * F.R ≈ A
32+
true
33+
```
34+
"""
35+
@inline function qr(A::StaticMatrix, pivot::Union{Val{false}, Val{true}} = Val(false))
36+
Q, R = _qr(Size(A), A, pivot)
37+
return QR(Q, R)
38+
end
2639

27-
SizeQ = Size( sA[1], isthin ? diagsize(Size(A)) : sA[1] )
40+
_qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T))))
41+
42+
43+
@generated function _qr(::Size{sA}, A::StaticMatrix{<:Any, <:Any, TA}, pivot::Union{Val{false}, Val{true}} = Val(false)) where {sA, TA}
44+
45+
SizeQ = Size( sA[1], diagsize(Size(A)) )
2846
SizeR = Size( diagsize(Size(A)), sA[2] )
2947

3048
if pivot == Val(true)
3149
return quote
3250
@_inline_meta
33-
Q0, R0, p0 = qr(Matrix(A), pivot, thin=$isthin)
51+
Q0, R0, p0 = qr(Matrix(A), pivot)
3452
T = _qreltype(TA)
3553
return similar_type(A, T, $(SizeQ))(Q0),
3654
similar_type(A, T, $(SizeR))(R0),
@@ -40,14 +58,13 @@ _qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T))))
4058
if (sA[1]*sA[1] + sA[1]*sA[2])÷2 * diagsize(Size(A)) < 17*17*17
4159
return quote
4260
@_inline_meta
43-
return qr_unrolled(Size(A), A, pivot, thin)
61+
return qr_unrolled(Size(A), A, pivot)
4462
end
4563
else
4664
return quote
4765
@_inline_meta
4866
Q0R0 = qr(Matrix(A), pivot)
49-
Q0 = Q0R0.Q
50-
R0 = Q0R0.R
67+
Q0, R0 = Matrix(Q0R0.Q), Q0R0.R
5168
T = _qreltype(TA)
5269
return similar_type(A, T, $(SizeQ))(Q0),
5370
similar_type(A, T, $(SizeR))(R0)
@@ -60,11 +77,8 @@ end
6077
# Compute the QR decomposition of `A` such that `A = Q*R`
6178
# by Householder reflections without pivoting.
6279
#
63-
# `thin=true` (reduced) method will produce `Q` and `R` in truncated form,
64-
# in the case of `thin=false` Q is full, but R is still reduced, see [`qr`](@ref).
65-
#
6680
# For original source code see below.
67-
@generated function qr_unrolled(::Size{sA}, A::StaticMatrix{<:Any, <:Any, TA}, pivot::Val{false}, thin::Union{Val{false}, Val{true}} = Val(true)) where {sA, TA}
81+
@generated function qr_unrolled(::Size{sA}, A::StaticMatrix{<:Any, <:Any, TA}, pivot::Val{false}) where {sA, TA}
6882
m, n = sA[1], sA[2]
6983

7084
Q = [Symbol("Q_$(i)_$(j)") for i = 1:m, j = 1:m]
@@ -124,11 +138,7 @@ end
124138
end
125139

126140
# truncate Q and R sizes in LAPACK consilient way
127-
if thin == Val(true)
128-
mQ, nQ = m, min(m, n)
129-
else
130-
mQ, nQ = m, m
131-
end
141+
mQ, nQ = m, min(m, n)
132142
mR, nR = min(m, n), n
133143

134144
return quote

test/qr.jl

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,20 @@ srand(42)
1313

1414
T = eltype(arr)
1515

16-
# thin=true case
1716
QR = @inferred qr(arr)
18-
@test QR isa Tuple
19-
@test length(QR) == 2
20-
Q, R = QR
17+
@test QR isa StaticArrays.QR
18+
Q, R = QR # destructing via iteration
2119
@test Q isa StaticMatrix
2220
@test R isa StaticMatrix
2321
@test eltype(Q) == eltype(R) == typeof((one(T)*zero(T) + zero(T))/norm([one(T)]))
2422

25-
Q_ref,R_ref = qr(Matrix(arr))
26-
@test abs.(Q) abs.(Q_ref) # QR is unique up to diag(Q) signs
23+
Q_ref, R_ref = qr(Matrix(arr))
24+
@test abs.(Q) abs.(Matrix(Q_ref)) # QR is unique up to diag(Q) signs
2725
@test abs.(R) abs.(R_ref)
2826
@test Q*R arr
2927
@test Q'*Q one(Q'*Q)
3028
@test istriu(R)
3129

32-
# fat (thin=false) case
33-
QR = @inferred qr(arr, Val(false), Val(false))
34-
@test QR isa Tuple
35-
@test length(QR) == 2
36-
Q, R = QR
37-
@test Q isa StaticMatrix
38-
@test R isa StaticMatrix
39-
@test eltype(Q) == eltype(R) == typeof((one(T)*zero(T) + zero(T))/norm([one(T)]))
40-
41-
Q_ref,R_ref = qr(Matrix(arr))
42-
@test abs.(Q) abs.(Q_ref) # QR is unique up to diag(Q) signs
43-
@test abs.(R) abs.(R_ref)
44-
R0 = vcat(R, @SMatrix(zeros(size(arr)[1]-size(R)[1], size(R)[2])) )
45-
@test Q*R0 arr
46-
@test Q'*Q one(Q'*Q)
47-
@test istriu(R)
48-
4930
# # pivot=true cases are not released yet
5031
# pivot = Val(true)
5132
# QRp = @inferred qr(arr, pivot)
@@ -62,8 +43,6 @@ srand(42)
6243
# @test p == p_ref
6344
end
6445

65-
@test_throws ArgumentError qr(@SMatrix randn(1,2); thin=false)
66-
6746
for eltya in (Float32, Float64, BigFloat, Int),
6847
rel in (real, complex),
6948
sz in [(3,3), (3,4), (4,3)]

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ include("expm.jl")
3737
include("sqrtm.jl")
3838
include("lyap.jl")
3939
include("lu.jl")
40-
# srand(42); include("qr.jl") # hmm ...
40+
srand(42); include("qr.jl")
4141
srand(42); include("chol.jl") # hermitian_type(::Type{Any}) for block algorithm
4242
include("deque.jl")
4343
include("io.jl")

0 commit comments

Comments
 (0)