• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37527

pending completion
#37527

push

local

web-flow
make `IRShow.method_name` inferrable (#49607)

18 of 18 new or added lines in 3 files covered. (100.0%)

68710 of 81829 relevant lines covered (83.97%)

33068903.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.48
/stdlib/LinearAlgebra/src/abstractq.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
abstract type AbstractQ{T} end
4

5
struct AdjointQ{T,S<:AbstractQ{T}} <: AbstractQ{T}
6
    Q::S
3,184✔
7
end
8

9
parent(adjQ::AdjointQ) = adjQ.Q
77✔
10
eltype(::Type{<:AbstractQ{T}}) where {T} = T
7,338✔
11
ndims(::AbstractQ) = 2
2✔
12

13
# inversion/adjoint/transpose
14
inv(Q::AbstractQ) = Q'
6✔
15
adjoint(Q::AbstractQ) = AdjointQ(Q)
3,183✔
16
transpose(Q::AbstractQ{<:Real}) = AdjointQ(Q)
1✔
17
transpose(Q::AbstractQ) = error("transpose not implemented for $(typeof(Q)). Consider using adjoint instead of transpose.")
1✔
18
adjoint(adjQ::AdjointQ) = adjQ.Q
28✔
19

20
# promotion with AbstractMatrix, at least for equal eltypes
21
promote_rule(::Type{<:AbstractMatrix{T}}, ::Type{<:AbstractQ{T}}) where {T} =
×
22
    (@inline; Union{AbstractMatrix{T},AbstractQ{T}})
×
23

24
# conversion
25
# the following eltype promotion should be defined for each subtype `QType`
26
# convert(::Type{AbstractQ{T}}, Q::QType) where {T} = QType{T}(Q)
27
# and then care has to be taken that
28
# QType{T}(Q::QType{T}) where T = ...
29
# is implemented as a no-op
30

31
# the following conversion method ensures functionality when the above method is not defined
32
# (as for HessenbergQ), but no eltype conversion is required either (say, in multiplication)
33
convert(::Type{AbstractQ{T}}, Q::AbstractQ{T}) where {T} = Q
20✔
34
convert(::Type{AbstractQ{T}}, adjQ::AdjointQ{T}) where {T} = adjQ
1,314✔
35
convert(::Type{AbstractQ{T}}, adjQ::AdjointQ) where {T} = convert(AbstractQ{T}, adjQ.Q)'
243✔
36

37
# ... to matrix
38
Matrix{T}(Q::AbstractQ) where {T} = convert(Matrix{T}, Q*I) # generic fallback, yields square matrix
12✔
39
Matrix{T}(adjQ::AdjointQ{S}) where {T,S} = convert(Matrix{T}, lmul!(adjQ, Matrix{S}(I, size(adjQ))))
4✔
40
Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q)
266✔
41
Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q)
230✔
42
Array(Q::AbstractQ) = Matrix(Q)
130✔
43
convert(::Type{T}, Q::AbstractQ) where {T<:AbstractArray} = T(Q)
124✔
44
# legacy
45
@deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T},
46
    convert(LinearAlgebra.AbstractQ{T}, Q))
47

48
function size(Q::AbstractQ, dim::Integer)
172✔
49
    if dim < 1
172✔
50
        throw(BoundsError())
40✔
51
    elseif dim <= 2 # && 1 <= dim
132✔
52
        return size(Q)[dim]
52✔
53
    else # 2 < dim
54
        return 1
80✔
55
    end
56
end
57
size(adjQ::AdjointQ) = reverse(size(adjQ.Q))
280✔
58

59
# pseudo-array behaviour, required for indexing with `begin` or `end`
60
axes(Q::AbstractQ) = map(Base.oneto, size(Q))
303✔
61
axes(Q::AbstractQ, d::Integer) = d in (1, 2) ? axes(Q)[d] : Base.OneTo(1)
×
62

63
copymutable(Q::AbstractQ{T}) where {T} = lmul!(Q, Matrix{T}(I, size(Q)))
53✔
64
copy(Q::AbstractQ) = copymutable(Q)
6✔
65

66
# getindex
67
@inline function getindex(Q::AbstractQ, inds...)
279✔
68
    @boundscheck Base.checkbounds_indices(Bool, axes(Q), inds) || Base.throw_boundserror(Q, inds)
291✔
69
    return _getindex(Q, inds...)
529✔
70
end
71
@inline getindex(Q::AbstractQ, ::Colon) = copymutable(Q)[:]
6✔
72
@inline getindex(Q::AbstractQ, ::Colon, ::Colon) = copy(Q)
6✔
73

74
@inline _getindex(Q::AbstractQ, inds...) = @inbounds copymutable(Q)[inds...]
201✔
75
@inline function _getindex(Q::AbstractQ, ::Colon, J::AbstractVector{<:Integer})
12✔
76
    Y = zeros(eltype(Q), size(Q, 2), length(J))
160✔
77
    @inbounds for (i,j) in enumerate(J)
24✔
78
        Y[j,i] = oneunit(eltype(Q))
32✔
79
    end
52✔
80
    lmul!(Q, Y)
12✔
81
end
82
@inline _getindex(Q::AbstractQ, I::AbstractVector{Int}, J::AbstractVector{Int}) = @inbounds Q[:,J][I,:]
4✔
83
@inline function _getindex(Q::AbstractQ, ::Colon, j::Int)
112✔
84
    y = zeros(eltype(Q), size(Q, 2))
396✔
85
    y[j] = oneunit(eltype(Q))
112✔
86
    lmul!(Q, y)
112✔
87
end
88
@inline _getindex(Q::AbstractQ, i::Int, j::Int) = @inbounds Q[:,j][i]
334✔
89

90
# needed because AbstractQ does not subtype AbstractMatrix
91
qr(Q::AbstractQ{T}, arg...; kwargs...) where {T} = qr!(Matrix{_qreltype(T)}(Q), arg...; kwargs...)
8✔
92
lq(Q::AbstractQ{T}, arg...; kwargs...) where {T} = lq!(Matrix{lq_eltype(T)}(Q), arg...; kwargs...)
×
93
hessenberg(Q::AbstractQ{T}) where {T} = hessenberg!(Matrix{eigtype(T)}(Q))
×
94

95
# needed when used interchangeably with AbstractMatrix (analogous to views of ranges)
96
view(A::AbstractQ, I...) = getindex(A, I...)
8✔
97

98
# specialization avoiding the fallback using slow `getindex`
99
function copyto!(dest::AbstractMatrix, src::AbstractQ)
38✔
100
    copyto!(dest, I)
38✔
101
    lmul!(src, dest)
38✔
102
end
103
# needed to resolve method ambiguities
104
function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,perm}
12✔
105
    if perm == (1, 2)
12✔
106
        copyto!(parent(dest), src)
6✔
107
    else
108
        @assert perm == (2, 1) # there are no other permutations of two indices
6✔
109
        if T <: Real
6✔
110
            copyto!(parent(dest), I)
3✔
111
            lmul!(src', parent(dest))
3✔
112
        else
113
            # LAPACK does not offer inplace lmul!(transpose(Q), B) for complex Q
114
            tmp = similar(parent(dest))
3✔
115
            copyto!(tmp, I)
3✔
116
            rmul!(tmp, src)
3✔
117
            permutedims!(parent(dest), tmp, (2, 1))
3✔
118
        end
119
    end
120
    return dest
12✔
121
end
122

123
function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ)
261✔
124
    print(io, Base.dims2string(size(Q)), ' ', summary(Q))
261✔
125
end
126

127
# multiplication
128
(*)(Q::AbstractQ, J::UniformScaling) = Q*J.λ
1,348✔
129
function (*)(Q::AbstractQ, b::Number)
1,356✔
130
    T = promote_type(eltype(Q), typeof(b))
1,356✔
131
    lmul!(convert(AbstractQ{T}, Q), Matrix{T}(b*I, size(Q)))
1,356✔
132
end
133
function (*)(A::AbstractQ, B::AbstractVecOrMat)
864✔
134
    T = promote_type(eltype(A), eltype(B))
864✔
135
    lmul!(convert(AbstractQ{T}, A), copy_similar(B, T))
864✔
136
end
137

138
(*)(J::UniformScaling, Q::AbstractQ) = J.λ*Q
14✔
139
function (*)(a::Number, Q::AbstractQ)
22✔
140
    T = promote_type(typeof(a), eltype(Q))
22✔
141
    rmul!(Matrix{T}(a*I, size(Q)), convert(AbstractQ{T}, Q))
22✔
142
end
143
*(a::AbstractVector, Q::AbstractQ) = reshape(a, length(a), 1) * Q
188✔
144
function (*)(A::AbstractMatrix, Q::AbstractQ)
556✔
145
    T = promote_type(eltype(A), eltype(Q))
556✔
146
    return rmul!(copy_similar(A, T), convert(AbstractQ{T}, Q))
556✔
147
end
148
(*)(u::AdjointAbsVec, Q::AbstractQ) = (Q'u')'
10✔
149

150
### Q*Q (including adjoints)
151
*(Q::AbstractQ, P::AbstractQ) = Q * (P*I)
48✔
152

153
### mul!
154
function mul!(C::AbstractVecOrMat{T}, Q::AbstractQ{T}, B::Union{AbstractVecOrMat{T},AbstractQ{T}}) where {T}
234✔
155
    require_one_based_indexing(C, B)
234✔
156
    mB = size(B, 1)
234✔
157
    mC = size(C, 1)
234✔
158
    if mB < mC
234✔
159
        inds = CartesianIndices(axes(B))
125✔
160
        copyto!(view(C, inds), B)
250✔
161
        C[CartesianIndices((mB+1:mC, axes(C, 2)))] .= zero(T)
250✔
162
        return lmul!(Q, C)
125✔
163
    else
164
        return lmul!(Q, copyto!(C, B))
109✔
165
    end
166
end
167
mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat{T}, Q::AbstractQ{T}) where {T} = rmul!(copyto!(C, A), Q)
58✔
168
mul!(C::AbstractVecOrMat{T}, adjQ::AdjointQ{T}, B::AbstractVecOrMat{T}) where {T} = lmul!(adjQ, copyto!(C, B))
135✔
169
mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat{T}, adjQ::AdjointQ{T}) where {T} = rmul!(copyto!(C, A), adjQ)
58✔
170

171
### division
172
\(Q::AbstractQ, A::AbstractVecOrMat) = Q'*A
4✔
173
/(A::AbstractVecOrMat, Q::AbstractQ) = A*Q'
8✔
174
ldiv!(Q::AbstractQ, A::AbstractVecOrMat) = lmul!(Q', A)
4✔
175
ldiv!(C::AbstractVecOrMat, Q::AbstractQ, A::AbstractVecOrMat) = mul!(C, Q', A)
4✔
176
rdiv!(A::AbstractVecOrMat, Q::AbstractQ) = rmul!(A, Q')
4✔
177

178
logabsdet(Q::AbstractQ) = (d = det(Q); return log(abs(d)), sign(d))
24✔
179
function logdet(A::AbstractQ)
×
180
    d, s = logabsdet(A)
×
181
    return d + log(s)
×
182
end
183

184
###########################################################
185
################ Q from QR decompositions #################
186
###########################################################
187

188
"""
189
    QRPackedQ <: LinearAlgebra.AbstractQ
190

191
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or
192
[`QRPivoted`](@ref) format.
193
"""
194
struct QRPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T}
195
    factors::S
196
    τ::C
197

198
    function QRPackedQ{T,S,C}(factors, τ) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}}
3,020✔
199
        require_one_based_indexing(factors, τ)
3,020✔
200
        new{T,S,C}(factors, τ)
3,020✔
201
    end
202
end
203
QRPackedQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} =
3,020✔
204
    QRPackedQ{T,typeof(factors),typeof(τ)}(factors, τ)
205
QRPackedQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T} =
×
206
    QRPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ))
207
# backwards-compatible constructors (remove with Julia 2.0)
208
@deprecate(QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T,S},
209
           QRPackedQ{T,S,typeof(τ)}(factors, τ), false)
210

211
"""
212
    QRCompactWYQ <: LinearAlgebra.AbstractQ
213

214
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref)
215
format.
216
"""
217
struct QRCompactWYQ{S, M<:AbstractMatrix{S}, C<:AbstractMatrix{S}} <: AbstractQ{S}
218
    factors::M
219
    T::C
220

221
    function QRCompactWYQ{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}}
2,400✔
222
        require_one_based_indexing(factors, T)
2,400✔
223
        new{S,M,C}(factors, T)
2,400✔
224
    end
225
end
226
QRCompactWYQ(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} =
2,400✔
227
    QRCompactWYQ{S,typeof(factors),typeof(T)}(factors, T)
228
QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} =
×
229
    QRCompactWYQ(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T))
230
# backwards-compatible constructors (remove with Julia 2.0)
231
@deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M},
232
           QRCompactWYQ{S,M,typeof(T)}(factors, T), false)
233

234
QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ))
2,018✔
235
QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T))
1,677✔
236

237
# override generic square fallback
238
Matrix{T}(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {T,S} =
445✔
239
    convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...))))
240
Matrix(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {S} = Matrix{S}(Q)
291✔
241

242
convert(::Type{AbstractQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ{T}(Q)
2,018✔
243
convert(::Type{AbstractQ{T}}, Q::QRCompactWYQ) where {T} = QRCompactWYQ{T}(Q)
1,677✔
244

245
size(Q::Union{QRCompactWYQ,QRPackedQ}, dim::Integer) =
1,019✔
246
    size(Q.factors, dim == 2 ? 1 : dim)
247
size(Q::Union{QRCompactWYQ,QRPackedQ}) = (n = size(Q.factors, 1); (n, n))
4,244✔
248

249
## Multiplication
250
### QB
251
lmul!(A::QRCompactWYQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
1,781✔
252
    LAPACK.gemqrt!('L', 'N', A.factors, A.T, B)
253
lmul!(A::QRPackedQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
1,494✔
254
    LAPACK.ormqr!('L', 'N', A.factors, A.τ, B)
255
function lmul!(A::QRPackedQ, B::AbstractVecOrMat)
547✔
256
    require_one_based_indexing(B)
547✔
257
    mA, nA = size(A.factors)
547✔
258
    mB, nB = size(B,1), size(B,2)
547✔
259
    if mA != mB
547✔
260
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
20✔
261
    end
262
    Afactors = A.factors
527✔
263
    @inbounds begin
527✔
264
        for k = min(mA,nA):-1:1
1,054✔
265
            for j = 1:nB
6,260✔
266
                vBj = B[k,j]
23,955✔
267
                for i = k+1:mB
46,040✔
268
                    vBj += conj(Afactors[i,k])*B[i,j]
116,370✔
269
                end
210,655✔
270
                vBj = A.τ[k]*vBj
23,955✔
271
                B[k,j] -= vBj
23,955✔
272
                for i = k+1:mB
46,040✔
273
                    B[i,j] -= Afactors[i,k]*vBj
116,370✔
274
                end
210,655✔
275
            end
44,780✔
276
        end
5,733✔
277
    end
278
    B
527✔
279
end
280

281
### QcB
282
lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} =
323✔
283
    (Q = adjQ.Q; LAPACK.gemqrt!('L', 'T', Q.factors, Q.T, B))
323✔
284
lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
245✔
285
    (Q = adjQ.Q; LAPACK.gemqrt!('L', 'C', Q.factors, Q.T, B))
245✔
286
lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} =
268✔
287
    (Q = adjQ.Q; LAPACK.ormqr!('L', 'T', Q.factors, Q.τ, B))
268✔
288
lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
195✔
289
    (Q = adjQ.Q; LAPACK.ormqr!('L', 'C', Q.factors, Q.τ, B))
195✔
290
function lmul!(adjA::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractVecOrMat)
145✔
291
    require_one_based_indexing(B)
145✔
292
    A = adjA.Q
145✔
293
    mA, nA = size(A.factors)
145✔
294
    mB, nB = size(B,1), size(B,2)
145✔
295
    if mA != mB
145✔
296
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
5✔
297
    end
298
    Afactors = A.factors
140✔
299
    @inbounds begin
140✔
300
        for k = 1:min(mA,nA)
280✔
301
            for j = 1:nB
1,670✔
302
                vBj = B[k,j]
4,905✔
303
                for i = k+1:mB
9,545✔
304
                    vBj += conj(Afactors[i,k])*B[i,j]
26,895✔
305
                end
49,150✔
306
                vBj = conj(A.τ[k])*vBj
4,905✔
307
                B[k,j] -= vBj
4,905✔
308
                for i = k+1:mB
9,545✔
309
                    B[i,j] -= Afactors[i,k]*vBj
26,895✔
310
                end
49,150✔
311
            end
8,975✔
312
        end
1,530✔
313
    end
314
    B
140✔
315
end
316

317
### AQ
318
rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,<:StridedMatrix}) where {T<:BlasFloat} =
281✔
319
    LAPACK.gemqrt!('R', 'N', B.factors, B.T, A)
320
rmul!(A::StridedVecOrMat{T}, B::QRPackedQ{T,<:StridedMatrix}) where {T<:BlasFloat} =
168✔
321
    LAPACK.ormqr!('R', 'N', B.factors, B.τ, A)
322
function rmul!(A::AbstractMatrix, Q::QRPackedQ)
90✔
323
    require_one_based_indexing(A)
90✔
324
    mQ, nQ = size(Q.factors)
90✔
325
    mA, nA = size(A,1), size(A,2)
90✔
326
    if nA != mQ
90✔
327
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
10✔
328
    end
329
    Qfactors = Q.factors
80✔
330
    @inbounds begin
80✔
331
        for k = 1:min(mQ,nQ)
160✔
332
            for i = 1:mA
980✔
333
                vAi = A[i,k]
4,510✔
334
                for j = k+1:mQ
8,780✔
335
                    vAi += A[i,j]*Qfactors[j,k]
24,940✔
336
                end
45,610✔
337
                vAi = vAi*Q.τ[k]
4,510✔
338
                A[i,k] -= vAi
4,510✔
339
                for j = k+1:nA
8,780✔
340
                    A[i,j] -= vAi*conj(Qfactors[j,k])
24,940✔
341
                end
45,610✔
342
            end
8,530✔
343
        end
900✔
344
    end
345
    A
80✔
346
end
347

348
### AQc
349
rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} =
146✔
350
    (Q = adjQ.Q; LAPACK.gemqrt!('R', 'T', Q.factors, Q.T, A))
146✔
351
rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasComplex} =
147✔
352
    (Q = adjQ.Q; LAPACK.gemqrt!('R', 'C', Q.factors, Q.T, A))
147✔
353
rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasReal} =
96✔
354
    (Q = adjQ.Q; LAPACK.ormqr!('R', 'T', Q.factors, Q.τ, A))
96✔
355
rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasComplex} =
92✔
356
    (Q = adjQ.Q; LAPACK.ormqr!('R', 'C', Q.factors, Q.τ, A))
92✔
357
function rmul!(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:QRPackedQ})
90✔
358
    require_one_based_indexing(A)
90✔
359
    Q = adjQ.Q
90✔
360
    mQ, nQ = size(Q.factors)
90✔
361
    mA, nA = size(A,1), size(A,2)
90✔
362
    if nA != mQ
90✔
363
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
10✔
364
    end
365
    Qfactors = Q.factors
80✔
366
    @inbounds begin
80✔
367
        for k = min(mQ,nQ):-1:1
160✔
368
            for i = 1:mA
980✔
369
                vAi = A[i,k]
4,060✔
370
                for j = k+1:mQ
7,880✔
371
                    vAi += A[i,j]*Qfactors[j,k]
21,990✔
372
                end
40,160✔
373
                vAi = vAi*conj(Q.τ[k])
4,060✔
374
                A[i,k] -= vAi
4,060✔
375
                for j = k+1:nA
7,880✔
376
                    A[i,j] -= vAi*conj(Qfactors[j,k])
21,990✔
377
                end
40,160✔
378
            end
7,630✔
379
        end
900✔
380
    end
381
    A
80✔
382
end
383

384
det(Q::QRPackedQ) = _det_tau(Q.τ)
188✔
385
det(Q::QRCompactWYQ) =
192✔
386
    prod(i -> _det_tau(_diagview(Q.T[:, i:min(i + size(Q.T, 1), size(Q.T, 2))])),
216✔
387
         1:size(Q.T, 1):size(Q.T, 2))
388

389
_diagview(A) = @view A[diagind(A)]
216✔
390

391
# Compute `det` from the number of Householder reflections.  Handle
392
# the case `Q.τ` contains zeros.
393
_det_tau(τs::AbstractVector{<:Real}) =
298✔
394
    isodd(count(!iszero, τs)) ? -one(eltype(τs)) : one(eltype(τs))
395

396
# In complex case, we need to compute the non-unit eigenvalue `λ = 1 - c*τ`
397
# (where `c = v'v`) of each Householder reflector.  As we know that the
398
# reflector must have the determinant of 1, it must satisfy `abs2(λ) == 1`.
399
# Combining this with the constraint `c > 0`, it turns out that the eigenvalue
400
# (hence the determinant) can be computed as `λ = -sign(τ)^2`.
401
# See: https://github.com/JuliaLang/julia/pull/32887#issuecomment-521935716
402
_det_tau(τs) = prod(τ -> iszero(τ) ? one(τ) : -sign(τ)^2, τs)
2,830✔
403

404
###########################################################
405
######## Q from Hessenberg decomposition ##################
406
###########################################################
407

408
"""
409
    HessenbergQ <: AbstractQ
410

411
Given a [`Hessenberg`](@ref) factorization object `F`, `F.Q` returns
412
a `HessenbergQ` object, which is an implicit representation of the unitary
413
matrix `Q` in the Hessenberg factorization `QHQ'` represented by `F`.
414
This `F.Q` object can be efficiently multiplied by matrices or vectors,
415
and can be converted to an ordinary matrix type with `Matrix(F.Q)`.
416
"""
417
struct HessenbergQ{T,S<:AbstractMatrix,W<:AbstractVector,sym} <: AbstractQ{T}
418
    uplo::Char
419
    factors::S
420
    τ::W
421
    function HessenbergQ{T,S,W,sym}(uplo::AbstractChar, factors, τ) where {T,S<:AbstractMatrix,W<:AbstractVector,sym}
453✔
422
        new(uplo, factors, τ)
453✔
423
    end
424
end
425
HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,false}(F.uplo, F.factors, F.τ)
228✔
426
HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.τ)
225✔
427

428
size(Q::HessenbergQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim)
130✔
429
size(Q::HessenbergQ) = size(Q, 1), size(Q, 2)
50✔
430

431
# HessenbergQ from LAPACK/BLAS (as opposed to Julia libraries like GenericLinearAlgebra)
432
const BlasHessenbergQ{T,sym} = HessenbergQ{T,<:StridedMatrix{T},<:StridedVector{T},sym} where {T<:BlasFloat,sym}
433

434
## reconstruct the original matrix
435
Matrix{T}(Q::BlasHessenbergQ{<:Any,false}) where {T} = convert(Matrix{T}, LAPACK.orghr!(1, size(Q.factors, 1), copy(Q.factors), Q.τ))
25✔
436
Matrix{T}(Q::BlasHessenbergQ{<:Any,true}) where {T} = convert(Matrix{T}, LAPACK.orgtr!(Q.uplo, copy(Q.factors), Q.τ))
25✔
437

438
lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
116✔
439
    LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
440
rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} =
44✔
441
    LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
442
lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
50✔
443
    (Q = adjQ.Q; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X))
50✔
444
rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} =
105✔
445
    (Q = adjQ.Q; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X))
105✔
446

447
lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
113✔
448
    LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.τ, X)
449
rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} =
44✔
450
    LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.τ, X)
451
lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
48✔
452
    (Q = adjQ.Q; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X))
48✔
453
rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} =
104✔
454
    (Q = adjQ.Q; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.τ, X))
104✔
455

456
lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')'
×
457
rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')'
6✔
458
lmul!(adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T}  = rmul!(X', adjQ')'
×
459
rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')'
6✔
460

461
# flexible left-multiplication (and adjoint right-multiplication)
462
function (*)(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, b::AbstractVector)
301✔
463
    T = promote_type(eltype(Q), eltype(b))
301✔
464
    if size(Q.factors, 1) == length(b)
301✔
465
        bnew = copy_similar(b, T)
61✔
466
    elseif size(Q.factors, 2) == length(b)
240✔
467
        bnew = [b; zeros(T, size(Q.factors, 1) - length(b))]
60✔
468
    else
469
        throw(DimensionMismatch("vector must have length either $(size(Q.factors, 1)) or $(size(Q.factors, 2))"))
180✔
470
    end
471
    lmul!(convert(AbstractQ{T}, Q), bnew)
121✔
472
end
473
function (*)(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, B::AbstractMatrix)
1,562✔
474
    T = promote_type(eltype(Q), eltype(B))
1,562✔
475
    if size(Q.factors, 1) == size(B, 1)
1,562✔
476
        Bnew = copy_similar(B, T)
1,323✔
477
    elseif size(Q.factors, 2) == size(B, 1)
456✔
478
        Bnew = [B; zeros(T, size(Q.factors, 1) - size(B,1), size(B, 2))]
431✔
479
    else
480
        throw(DimensionMismatch("first dimension of matrix must have size either $(size(Q.factors, 1)) or $(size(Q.factors, 2))"))
25✔
481
    end
482
    lmul!(convert(AbstractQ{T}, Q), Bnew)
1,537✔
483
end
484
function (*)(A::AbstractMatrix, adjQ::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}})
577✔
485
    Q = adjQ.Q
577✔
486
    T = promote_type(eltype(A), eltype(adjQ))
577✔
487
    adjQQ = convert(AbstractQ{T}, adjQ)
577✔
488
    if size(A, 2) == size(Q.factors, 1)
577✔
489
        AA = copy_similar(A, T)
319✔
490
        return rmul!(AA, adjQQ)
277✔
491
    elseif size(A, 2) == size(Q.factors, 2)
300✔
492
        return rmul!([A zeros(T, size(A, 1), size(Q.factors, 1) - size(Q.factors, 2))], adjQQ)
120✔
493
    else
494
        throw(DimensionMismatch("matrix A has dimensions $(size(A)) but Q-matrix B has dimensions $(size(adjQ))"))
180✔
495
    end
496
end
497
(*)(u::AdjointAbsVec, Q::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) = (Q'u')'
2✔
498

499
det(Q::HessenbergQ) = _det_tau(Q.τ)
20✔
500

501
###########################################################
502
################ Q from LQ decomposition ##################
503
###########################################################
504

505
struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T}
506
    factors::S
1,854✔
507
    τ::C
508
end
509

510
LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ))
1,032✔
511
@deprecate(AbstractMatrix{T}(Q::LQPackedQ) where {T},
512
    convert(AbstractQ{T}, Q),
513
    false)
514
Matrix{T}(A::LQPackedQ) where {T} = convert(Matrix{T}, LAPACK.orglq!(copy(A.factors), A.τ))
360✔
515
convert(::Type{AbstractQ{T}}, Q::LQPackedQ) where {T} = LQPackedQ{T}(Q)
1,032✔
516

517
# size(Q::LQPackedQ) yields the shape of Q's square form
518
size(Q::LQPackedQ) = (n = size(Q.factors, 2); return n, n)
344✔
519

520
## Multiplication
521
### QB / QcB
522
lmul!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.τ,B)
1,416✔
523
lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasReal} =
124✔
524
    (A = adjA.Q; LAPACK.ormlq!('L', 'T', A.factors, A.τ, B))
124✔
525
lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
196✔
526
    (A = adjA.Q; LAPACK.ormlq!('L', 'C', A.factors, A.τ, B))
196✔
527

528
function (*)(adjA::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractVector)
40✔
529
    A = adjA.Q
40✔
530
    T = promote_type(eltype(A), eltype(B))
40✔
531
    if length(B) == size(A.factors, 2)
40✔
532
        C = copy_similar(B, T)
40✔
533
    elseif length(B) == size(A.factors, 1)
×
534
        C = [B; zeros(T, size(A.factors, 2) - size(A.factors, 1), size(B, 2))]
×
535
    else
536
        throw(DimensionMismatch("length of B, $(length(B)), must equal one of the dimensions of A, $(size(A))"))
×
537
    end
538
    lmul!(convert(AbstractQ{T}, adjA), C)
40✔
539
end
540
function (*)(adjA::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractMatrix)
240✔
541
    A = adjA.Q
240✔
542
    T = promote_type(eltype(A), eltype(B))
240✔
543
    if size(B,1) == size(A.factors,2)
240✔
544
        C = copy_similar(B, T)
160✔
545
    elseif size(B,1) == size(A.factors,1)
80✔
546
        C = [B; zeros(T, size(A.factors, 2) - size(A.factors, 1), size(B, 2))]
×
547
    else
548
        throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of A, $(size(A))"))
80✔
549
    end
550
    lmul!(convert(AbstractQ{T}, adjA), C)
160✔
551
end
552

553
# in-place right-application of LQPackedQs
554
# these methods require that the applied-to matrix's (A's) number of columns
555
# match the number of columns (nQ) of the LQPackedQ (Q) (necessary for in-place
556
# operation, and the underlying LAPACK routine (ormlq) treats the implicit Q
557
# as its (nQ-by-nQ) square form)
558
rmul!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasFloat} =
584✔
559
    LAPACK.ormlq!('R', 'N', B.factors, B.τ, A)
560
rmul!(A::StridedMatrix{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasReal} =
92✔
561
    (B = adjB.Q; LAPACK.ormlq!('R', 'T', B.factors, B.τ, A))
92✔
562
rmul!(A::StridedMatrix{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasComplex} =
116✔
563
    (B = adjB.Q; LAPACK.ormlq!('R', 'C', B.factors, B.τ, A))
116✔
564

565
# out-of-place right application of LQPackedQs
566
#
567
# these methods: (1) check whether the applied-to matrix's (A's) appropriate dimension
568
# (columns for A_*, rows for Ac_*) matches the number of columns (nQ) of the LQPackedQ (Q),
569
# and if so effectively apply Q's square form to A without additional shenanigans; and
570
# (2) if the preceding dimensions do not match, check whether the appropriate dimension of
571
# A instead matches the number of rows of the matrix of which Q is a factor (i.e.
572
# size(Q.factors, 1)), and if so implicitly apply Q's truncated form to A by zero extending
573
# A as necessary for check (1) to pass (if possible) and then applying Q's square form
574
#
575
function (*)(A::AbstractVector, Q::LQPackedQ)
×
576
    T = promote_type(eltype(A), eltype(Q))
×
577
    if 1 == size(Q.factors, 2)
×
578
        C = copy_similar(A, T)
×
579
    elseif 1 == size(Q.factors, 1)
×
580
        C = zeros(T, length(A), size(Q.factors, 2))
×
581
        copyto!(C, 1, A, 1, length(A))
×
582
    else
583
        _rightappdimmismatch("columns")
×
584
    end
585
    return rmul!(C, convert(AbstractQ{T}, Q))
×
586
end
587
function (*)(A::AbstractMatrix, Q::LQPackedQ)
620✔
588
    T = promote_type(eltype(A), eltype(Q))
620✔
589
    if size(A, 2) == size(Q.factors, 2)
620✔
590
        C = copy_similar(A, T)
583✔
591
    elseif size(A, 2) == size(Q.factors, 1)
201✔
592
        C = zeros(T, size(A, 1), size(Q.factors, 2))
14,292✔
593
        copyto!(C, 1, A, 1, length(A))
121✔
594
    else
595
        _rightappdimmismatch("columns")
80✔
596
    end
597
    return rmul!(C, convert(AbstractQ{T}, Q))
540✔
598
end
599
function (*)(adjA::AdjointAbsMat, Q::LQPackedQ)
4✔
600
    A = adjA.parent
4✔
601
    T = promote_type(eltype(A), eltype(Q))
4✔
602
    if size(A, 1) == size(Q.factors, 2)
4✔
603
        C = copy_similar(adjA, T)
3✔
604
    elseif size(A, 1) == size(Q.factors, 1)
1✔
605
        C = zeros(T, size(A, 2), size(Q.factors, 2))
12✔
606
        adjoint!(view(C, :, 1:size(A, 1)), A)
1✔
607
    else
608
        _rightappdimmismatch("rows")
×
609
    end
610
    return rmul!(C, convert(AbstractQ{T}, Q))
4✔
611
end
612
(*)(u::AdjointAbsVec, Q::LQPackedQ) = (Q'u')'
×
613

614
_rightappdimmismatch(rowsorcols) =
80✔
615
    throw(DimensionMismatch(string("the number of $(rowsorcols) of the matrix on the left ",
616
        "must match either (1) the number of columns of the (LQPackedQ) matrix on the right ",
617
        "or (2) the number of rows of that (LQPackedQ) matrix's internal representation ",
618
        "(the factorization's originating matrix's number of rows)")))
619

620
# In LQ factorization, `Q` is expressed as the product of the adjoint of the
621
# reflectors.  Thus, `det` has to be conjugated.
622
det(Q::LQPackedQ) = conj(_det_tau(Q.τ))
36✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc