mollusk 0e4acfb8f2 fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
2018-06-11 03:28:36 -07:00

848 lines
30 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# This file is a part of Julia. License is MIT: https://julialang.org/license
# QR and Hessenberg Factorizations
"""
QR <: Factorization
A QR matrix factorization stored in a packed format, typically obtained from
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
```math
A = Q R
```
where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular.
The matrix ``Q`` is stored as a sequence of Householder reflectors ``v_i``
and coefficients ``\\tau_i`` where:
```math
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T).
```
The object has two fields:
* `factors` is an `m`×`n` matrix.
- The upper triangular part contains the elements of ``R``, that is `R =
triu(F.factors)` for a `QR` object `F`.
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where
``v_i`` is the ``i``th column of the matrix `V = eye(m,n) + tril(F.factors,-1)`.
* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``.
"""
struct QR{T,S<:AbstractMatrix} <: Factorization{T}
factors::S
τ::Vector{T}
QR{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
end
QR(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = QR{T,typeof(factors)}(factors, τ)
# Note. For QRCompactWY factorization without pivoting, the WY representation based method introduced in LAPACK 3.4
"""
QRCompactWY <: Factorization
A QR matrix factorization stored in a compact blocked format, typically obtained from
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
```math
A = Q R
```
where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. It is similar
to the [`QR`](@ref) format except that the orthogonal/unitary matrix ``Q`` is stored in
*Compact WY* format [^Schreiber1989], as a lower trapezoidal matrix ``V`` and an upper
triangular matrix ``T`` where
```math
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T) = I - V T V^T
```
such that ``v_i`` is the ``i``th column of ``V``, and ``\tau_i`` is the ``i``th diagonal
element of ``T``.
The object has two fields:
* `factors`, as in the [`QR`](@ref) type, is an `m`×`n` matrix.
- The upper triangular part contains the elements of ``R``, that is `R =
triu(F.factors)` for a `QR` object `F`.
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format such
that `V = eye(m,n) + tril(F.factors,-1)`.
* `T` is a square matrix with `min(m,n)` columns, whose upper triangular part gives the
matrix ``T`` above (the subdiagonal elements are ignored).
!!! note
This format should not to be confused with the older *WY* representation
[^Bischof1987].
[^Bischof1987]: C Bischof and C Van Loan, "The WY representation for products of Householder matrices", SIAM J Sci Stat Comput 8 (1987), s2-s13. [doi:10.1137/0908009](http://dx.doi.org/10.1137/0908009)
[^Schreiber1989]: R Schreiber and C Van Loan, "A storage-efficient WY representation for products of Householder transformations", SIAM J Sci Stat Comput 10 (1989), 53-57. [doi:10.1137/0910005](http://dx.doi.org/10.1137/0910005)
"""
struct QRCompactWY{S,M<:AbstractMatrix} <: Factorization{S}
factors::M
T::Matrix{S}
QRCompactWY{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M<:AbstractMatrix} = new(factors, T)
end
QRCompactWY(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = QRCompactWY{S,typeof(factors)}(factors, T)
"""
QRPivoted <: Factorization
A QR matrix factorization with column pivoting in a packed format, typically obtained from
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
```math
A P = Q R
```
where ``P`` is a permutation matrix, ``Q`` is an orthogonal/unitary matrix and ``R`` is
upper triangular. The matrix ``Q`` is stored as a sequence of Householder reflectors:
```math
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T).
```
The object has three fields:
* `factors` is an `m`×`n` matrix.
- The upper triangular part contains the elements of ``R``, that is `R =
triu(F.factors)` for a `QR` object `F`.
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where
``v_i`` is the ``i``th column of the matrix `V = eye(m,n) + tril(F.factors,-1)`.
* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``.
* `jpvt` is an integer vector of length `n` corresponding to the permutation ``P``.
"""
struct QRPivoted{T,S<:AbstractMatrix} <: Factorization{T}
factors::S
τ::Vector{T}
jpvt::Vector{BlasInt}
QRPivoted{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}, jpvt::Vector{BlasInt}) where {T,S<:AbstractMatrix} =
new(factors, τ, jpvt)
end
QRPivoted(factors::AbstractMatrix{T}, τ::Vector{T}, jpvt::Vector{BlasInt}) where {T} =
QRPivoted{T,typeof(factors)}(factors, τ, jpvt)
function qrfactUnblocked!(A::AbstractMatrix{T}) where {T}
m, n = size(A)
τ = zeros(T, min(m,n))
for k = 1:min(m - 1 + !(T<:Real), n)
x = view(A, k:m, k)
τk = reflector!(x)
τ[k] = τk
reflectorApply!(x, τk, view(A, k:m, k + 1:n))
end
QR(A, τ)
end
# Find index for columns with largest two norm
function indmaxcolumn(A::StridedMatrix)
mm = norm(view(A, :, 1))
ii = 1
for i = 2:size(A, 2)
mi = norm(view(A, :, i))
if abs(mi) > mm
mm = mi
ii = i
end
end
return ii
end
function qrfactPivotedUnblocked!(A::StridedMatrix)
m, n = size(A)
piv = collect(UnitRange{BlasInt}(1,n))
τ = Vector{eltype(A)}(min(m,n))
for j = 1:min(m,n)
# Find column with maximum norm in trailing submatrix
jm = indmaxcolumn(view(A, j:m, j:n)) + j - 1
if jm != j
# Flip elements in pivoting vector
tmpp = piv[jm]
piv[jm] = piv[j]
piv[j] = tmpp
# Update matrix with
for i = 1:m
tmp = A[i,jm]
A[i,jm] = A[i,j]
A[i,j] = tmp
end
end
# Compute reflector of columns j
x = view(A, j:m, j)
τj = LinAlg.reflector!(x)
τ[j] = τj
# Update trailing submatrix with reflector
LinAlg.reflectorApply!(x, τj, view(A, j:m, j+1:n))
end
return LinAlg.QRPivoted{eltype(A), typeof(A)}(A, τ, piv)
end
# LAPACK version
qrfact!(A::StridedMatrix{<:BlasFloat}, ::Type{Val{false}}) = QRCompactWY(LAPACK.geqrt!(A, min(minimum(size(A)), 36))...)
qrfact!(A::StridedMatrix{<:BlasFloat}, ::Type{Val{true}}) = QRPivoted(LAPACK.geqp3!(A)...)
qrfact!(A::StridedMatrix{<:BlasFloat}) = qrfact!(A, Val{false})
# Generic fallbacks
"""
qrfact!(A, pivot=Val{false})
`qrfact!` is the same as [`qrfact`](@ref) when `A` is a subtype of
`StridedMatrix`, but saves space by overwriting the input `A`, instead of creating a copy.
An [`InexactError`](@ref) exception is thrown if the factorization produces a number not
representable by the element type of `A`, e.g. for integer types.
"""
qrfact!(A::StridedMatrix, ::Type{Val{false}}) = qrfactUnblocked!(A)
qrfact!(A::StridedMatrix, ::Type{Val{true}}) = qrfactPivotedUnblocked!(A)
qrfact!(A::StridedMatrix) = qrfact!(A, Val{false})
"""
qrfact(A, pivot=Val{false}) -> F
Compute the QR factorization of the matrix `A`: an orthogonal (or unitary if `A` is
complex-valued) matrix `Q`, and an upper triangular matrix `R` such that
```math
A = Q R
```
The returned object `F` stores the factorization in a packed format:
- if `pivot == Val{true}` then `F` is a [`QRPivoted`](@ref) object,
- otherwise if the element type of `A` is a BLAS type ([`Float32`](@ref), [`Float64`](@ref),
`Complex64` or `Complex128`), then `F` is a [`QRCompactWY`](@ref) object,
- otherwise `F` is a [`QR`](@ref) object.
The individual components of the factorization `F` can be accessed by indexing with a symbol:
- `F[:Q]`: the orthogonal/unitary matrix `Q`
- `F[:R]`: the upper triangular matrix `R`
- `F[:p]`: the permutation vector of the pivot ([`QRPivoted`](@ref) only)
- `F[:P]`: the permutation matrix of the pivot ([`QRPivoted`](@ref) only)
The following functions are available for the `QR` objects: [`inv`](@ref), [`size`](@ref),
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]`
and `F[:Q]*A` are supported. A `Q` matrix can be converted into a regular matrix with
[`full`](@ref) which has a named argument `thin`.
# Example
```jldoctest
julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0]
3×2 Array{Float64,2}:
3.0 -6.0
4.0 -8.0
0.0 1.0
julia> F = qrfact(A)
Base.LinAlg.QRCompactWY{Float64,Array{Float64,2}} with factors Q and R:
[-0.6 0.0 0.8; -0.8 0.0 -0.6; 0.0 -1.0 0.0]
[-5.0 10.0; 0.0 -1.0]
julia> F[:Q] * F[:R] == A
true
```
!!! note
`qrfact` returns multiple types because LAPACK uses several representations
that minimize the memory storage requirements of products of Householder
elementary reflectors, so that the `Q` and `R` matrices can be stored
compactly rather as two separate dense matrices.
"""
function qrfact(A::AbstractMatrix{T}, arg) where T
AA = similar(A, typeof(zero(T)/norm(one(T))), size(A))
copy!(AA, A)
return qrfact!(AA, arg)
end
function qrfact(A::AbstractMatrix{T}) where T
AA = similar(A, typeof(zero(T)/norm(one(T))), size(A))
copy!(AA, A)
return qrfact!(AA)
end
qrfact(x::Number) = qrfact(fill(x,1,1))
"""
qr(A, pivot=Val{false}; thin::Bool=true) -> 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.
"""
qr(A::Union{Number, AbstractMatrix}, pivot::Union{Type{Val{false}}, Type{Val{true}}}=Val{false}; thin::Bool=true) =
_qr(A, pivot, thin=thin)
function _qr(A::Union{Number, AbstractMatrix}, ::Type{Val{false}}; thin::Bool=true)
F = qrfact(A, Val{false})
full(getq(F), thin=thin), F[:R]::Matrix{eltype(F)}
end
function _qr(A::Union{Number, AbstractMatrix}, ::Type{Val{true}}; thin::Bool=true)
F = qrfact(A, Val{true})
full(getq(F), thin=thin), F[:R]::Matrix{eltype(F)}, F[:p]::Vector{BlasInt}
end
"""
qr(v::AbstractVector) -> w, r
Computes the polar decomposition of a vector.
Returns `w`, a unit vector in the direction of `v`, and
`r`, the norm of `v`.
See also [`normalize`](@ref), [`normalize!`](@ref),
and [`LinAlg.qr!`](@ref).
# Example
```jldoctest
julia> v = [1; 2]
2-element Array{Int64,1}:
1
2
julia> w, r = qr(v)
([0.447214, 0.894427], 2.23606797749979)
julia> w*r == v
true
```
"""
function qr(v::AbstractVector)
nrm = norm(v)
if !isempty(v)
vv = copy_oftype(v, typeof(v[1]/nrm))
return __normalize!(vv, nrm), nrm
else
T = typeof(zero(eltype(v))/nrm)
return T[], oneunit(T)
end
end
"""
LinAlg.qr!(v::AbstractVector) -> w, r
Computes the polar decomposition of a vector. Instead of returning a new vector
as `qr(v::AbstractVector)`, this function mutates the input vector `v` in place.
Returns `w`, a unit vector in the direction of `v` (this is a mutation of `v`),
and `r`, the norm of `v`.
See also [`normalize`](@ref), [`normalize!`](@ref),
and [`qr`](@ref).
"""
function qr!(v::AbstractVector)
nrm = norm(v)
__normalize!(v, nrm), nrm
end
# Conversions
convert(::Type{QR{T}}, A::QR) where {T} = QR(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ))
convert(::Type{Factorization{T}}, A::QR{T}) where {T} = A
convert(::Type{Factorization{T}}, A::QR) where {T} = convert(QR{T}, A)
convert(::Type{QRCompactWY{T}}, A::QRCompactWY) where {T} = QRCompactWY(convert(AbstractMatrix{T}, A.factors), convert(AbstractMatrix{T}, A.T))
convert(::Type{Factorization{T}}, A::QRCompactWY{T}) where {T} = A
convert(::Type{Factorization{T}}, A::QRCompactWY) where {T} = convert(QRCompactWY{T}, A)
convert(::Type{AbstractMatrix}, F::Union{QR,QRCompactWY}) = F[:Q] * F[:R]
convert(::Type{AbstractArray}, F::Union{QR,QRCompactWY}) = convert(AbstractMatrix, F)
convert(::Type{Matrix}, F::Union{QR,QRCompactWY}) = convert(Array, convert(AbstractArray, F))
convert(::Type{Array}, F::Union{QR,QRCompactWY}) = convert(Matrix, F)
full(F::Union{QR,QRCompactWY}) = convert(AbstractArray, F)
convert(::Type{QRPivoted{T}}, A::QRPivoted) where {T} = QRPivoted(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ), A.jpvt)
convert(::Type{Factorization{T}}, A::QRPivoted{T}) where {T} = A
convert(::Type{Factorization{T}}, A::QRPivoted) where {T} = convert(QRPivoted{T}, A)
convert(::Type{AbstractMatrix}, F::QRPivoted) = (F[:Q] * F[:R])[:,invperm(F[:p])]
convert(::Type{AbstractArray}, F::QRPivoted) = convert(AbstractMatrix, F)
convert(::Type{Matrix}, F::QRPivoted) = convert(Array, convert(AbstractArray, F))
convert(::Type{Array}, F::QRPivoted) = convert(Matrix, F)
full(F::QRPivoted) = convert(AbstractArray, F)
function show(io::IO, F::Union{QR, QRCompactWY, QRPivoted})
println(io, "$(typeof(F)) with factors Q and R:")
show(io, F[:Q])
println(io)
show(io, F[:R])
end
function getindex(A::QR, d::Symbol)
m, n = size(A)
if d == :R
return triu!(A.factors[1:min(m,n), 1:n])
elseif d == :Q
return getq(A)
else
throw(KeyError(d))
end
end
function getindex(A::QRCompactWY, d::Symbol)
m, n = size(A)
if d == :R
return triu!(A.factors[1:min(m,n), 1:n])
elseif d == :Q
return getq(A)
else
throw(KeyError(d))
end
end
function getindex(A::QRPivoted{T}, d::Symbol) where T
m, n = size(A)
if d == :R
return triu!(A.factors[1:min(m,n), 1:n])
elseif d == :Q
return getq(A)
elseif d == :p
return A.jpvt
elseif d == :P
p = A[:p]
n = length(p)
P = zeros(T, n, n)
for i in 1:n
P[p[i],i] = one(T)
end
return P
else
throw(KeyError(d))
end
end
# Type-stable interface to get Q
getq(A::QRCompactWY) = QRCompactWYQ(A.factors,A.T)
getq(A::Union{QR, QRPivoted}) = QRPackedQ(A.factors,A.τ)
"""
QRPackedQ <: AbstractMatrix
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or
[`QRPivoted`](@ref) format.
"""
struct QRPackedQ{T,S<:AbstractMatrix} <: AbstractMatrix{T}
factors::S
τ::Vector{T}
QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
end
QRPackedQ(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = QRPackedQ{T,typeof(factors)}(factors, τ)
"""
QRCompactWYQ <: AbstractMatrix
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref)
format.
"""
struct QRCompactWYQ{S, M<:AbstractMatrix} <: AbstractMatrix{S}
factors::M
T::Matrix{S}
QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::Matrix{S}) where {S,M<:AbstractMatrix} = new(factors, T)
end
QRCompactWYQ(factors::AbstractMatrix{S}, T::Matrix{S}) where {S} = QRCompactWYQ{S,typeof(factors)}(factors, T)
convert(::Type{QRPackedQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ))
convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ{T}) where {T} = Q
convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ) where {T} = convert(QRPackedQ{T}, Q)
convert(::Type{QRCompactWYQ{S}}, Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T))
convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ{S}) where {S} = Q
convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ) where {S} = convert(QRCompactWYQ{S}, Q)
convert(::Type{Matrix}, A::Union{QRPackedQ{T},QRCompactWYQ{T}}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), minimum(size(A.factors))))
convert(::Type{Array}, A::Union{QRPackedQ,QRCompactWYQ}) = convert(Matrix, A)
"""
full(A::Union{QRPackedQ,QRCompactWYQ}; thin::Bool=true) -> Matrix
Converts an orthogonal or unitary matrix stored as a `QRCompactWYQ` object, i.e. in the
compact WY format [^Bischof1987], or in the `QRPackedQ` format, to a dense matrix.
Optionally takes a `thin` Boolean argument, which if `true` omits the columns that span the
rows of `R` in the QR factorization that are zero. The resulting matrix is the `Q` in a thin
QR factorization (sometimes called the reduced QR factorization). If `false`, returns a `Q`
that spans all rows of `R` in its corresponding QR factorization.
"""
function full{T}(A::Union{QRPackedQ{T},QRCompactWYQ{T}}; thin::Bool = true)
if thin
convert(Array, A)
else
A_mul_B!(A, eye(T, size(A.factors, 1)))
end
end
size(A::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(A.factors, dim)
size(A::Union{QR,QRCompactWY,QRPivoted}) = size(A.factors)
size(A::Union{QRPackedQ,QRCompactWYQ}, dim::Integer) = 0 < dim ? (dim <= 2 ? size(A.factors, 1) : 1) : throw(BoundsError())
size(A::Union{QRPackedQ,QRCompactWYQ}) = size(A, 1), size(A, 2)
function getindex(A::Union{QRPackedQ,QRCompactWYQ}, i::Integer, j::Integer)
x = zeros(eltype(A), size(A, 1))
x[i] = 1
y = zeros(eltype(A), size(A, 2))
y[j] = 1
return dot(x, A_mul_B!(A, y))
end
## Multiplication by Q
### QB
A_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.gemqrt!('L','N',A.factors,A.T,B)
A_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormqr!('L','N',A.factors,A.τ,B)
function A_mul_B!(A::QRPackedQ, B::AbstractVecOrMat)
mA, nA = size(A.factors)
mB, nB = size(B,1), size(B,2)
if mA != mB
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
end
Afactors = A.factors
@inbounds begin
for k = min(mA,nA):-1:1
for j = 1:nB
vBj = B[k,j]
for i = k+1:mB
vBj += conj(Afactors[i,k])*B[i,j]
end
vBj = A.τ[k]*vBj
B[k,j] -= vBj
for i = k+1:mB
B[i,j] -= Afactors[i,k]*vBj
end
end
end
end
B
end
function (*)(A::Union{QRPackedQ,QRCompactWYQ}, b::StridedVector)
TAb = promote_type(eltype(A), eltype(b))
Anew = convert(AbstractMatrix{TAb}, A)
if size(A.factors, 1) == length(b)
bnew = copy_oftype(b, TAb)
elseif size(A.factors, 2) == length(b)
bnew = [b; zeros(TAb, size(A.factors, 1) - length(b))]
else
throw(DimensionMismatch("vector must have length either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
end
A_mul_B!(Anew, bnew)
end
function (*)(A::Union{QRPackedQ,QRCompactWYQ}, B::StridedMatrix)
TAB = promote_type(eltype(A), eltype(B))
Anew = convert(AbstractMatrix{TAB}, A)
if size(A.factors, 1) == size(B, 1)
Bnew = copy_oftype(B, TAB)
elseif size(A.factors, 2) == size(B, 1)
Bnew = [B; zeros(TAB, size(A.factors, 1) - size(B,1), size(B, 2))]
else
throw(DimensionMismatch("first dimension of matrix must have size either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
end
A_mul_B!(Anew, Bnew)
end
### QcB
Ac_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = LAPACK.gemqrt!('L','T',A.factors,A.T,B)
Ac_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = LAPACK.gemqrt!('L','C',A.factors,A.T,B)
Ac_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = LAPACK.ormqr!('L','T',A.factors,A.τ,B)
Ac_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = LAPACK.ormqr!('L','C',A.factors,A.τ,B)
function Ac_mul_B!(A::QRPackedQ, B::AbstractVecOrMat)
mA, nA = size(A.factors)
mB, nB = size(B,1), size(B,2)
if mA != mB
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
end
Afactors = A.factors
@inbounds begin
for k = 1:min(mA,nA)
for j = 1:nB
vBj = B[k,j]
for i = k+1:mB
vBj += conj(Afactors[i,k])*B[i,j]
end
vBj = conj(A.τ[k])*vBj
B[k,j] -= vBj
for i = k+1:mB
B[i,j] -= Afactors[i,k]*vBj
end
end
end
end
B
end
function Ac_mul_B(Q::Union{QRPackedQ,QRCompactWYQ}, B::StridedVecOrMat)
TQB = promote_type(eltype(Q), eltype(B))
return Ac_mul_B!(convert(AbstractMatrix{TQB}, Q), copy_oftype(B, TQB))
end
### QBc/QcBc
for (f1, f2) in ((:A_mul_Bc, :A_mul_B!),
(:Ac_mul_Bc, :Ac_mul_B!))
@eval begin
function ($f1)(Q::Union{QRPackedQ,QRCompactWYQ}, B::StridedVecOrMat)
TQB = promote_type(eltype(Q), eltype(B))
Bc = similar(B, TQB, (size(B, 2), size(B, 1)))
ctranspose!(Bc, B)
return ($f2)(convert(AbstractMatrix{TQB}, Q), Bc)
end
end
end
### AQ
A_mul_B!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasFloat} = LAPACK.gemqrt!('R','N', B.factors, B.T, A)
A_mul_B!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasFloat} = LAPACK.ormqr!('R', 'N', B.factors, B.τ, A)
function A_mul_B!(A::StridedMatrix,Q::QRPackedQ)
mQ, nQ = size(Q.factors)
mA, nA = size(A,1), size(A,2)
if nA != mQ
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
end
Qfactors = Q.factors
@inbounds begin
for k = 1:min(mQ,nQ)
for i = 1:mA
vAi = A[i,k]
for j = k+1:mQ
vAi += A[i,j]*Qfactors[j,k]
end
vAi = vAi*Q.τ[k]
A[i,k] -= vAi
for j = k+1:nA
A[i,j] -= vAi*conj(Qfactors[j,k])
end
end
end
end
A
end
function (*)(A::StridedMatrix, Q::Union{QRPackedQ,QRCompactWYQ})
TAQ = promote_type(eltype(A), eltype(Q))
return A_mul_B!(copy_oftype(A, TAQ), convert(AbstractMatrix{TAQ}, Q))
end
### AQc
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasReal} = LAPACK.gemqrt!('R','T',B.factors,B.T,A)
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasComplex} = LAPACK.gemqrt!('R','C',B.factors,B.T,A)
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasReal} = LAPACK.ormqr!('R','T',B.factors,B.τ,A)
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasComplex} = LAPACK.ormqr!('R','C',B.factors,B.τ,A)
function A_mul_Bc!(A::AbstractMatrix,Q::QRPackedQ)
mQ, nQ = size(Q.factors)
mA, nA = size(A,1), size(A,2)
if nA != mQ
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
end
Qfactors = Q.factors
@inbounds begin
for k = min(mQ,nQ):-1:1
for i = 1:mA
vAi = A[i,k]
for j = k+1:mQ
vAi += A[i,j]*Qfactors[j,k]
end
vAi = vAi*conj(Q.τ[k])
A[i,k] -= vAi
for j = k+1:nA
A[i,j] -= vAi*conj(Qfactors[j,k])
end
end
end
end
A
end
function A_mul_Bc(A::AbstractMatrix, B::Union{QRCompactWYQ,QRPackedQ})
TAB = promote_type(eltype(A),eltype(B))
BB = convert(AbstractMatrix{TAB}, B)
if size(A,2) == size(B.factors, 1)
AA = similar(A, TAB, size(A))
copy!(AA, A)
return A_mul_Bc!(AA, BB)
elseif size(A,2) == size(B.factors,2)
return A_mul_Bc!([A zeros(TAB, size(A, 1), size(B.factors, 1) - size(B.factors, 2))], BB)
else
throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(B))"))
end
end
@inline A_mul_Bc(rowvec::RowVector, B::Union{LinAlg.QRCompactWYQ,LinAlg.QRPackedQ}) = ctranspose(B*ctranspose(rowvec))
### AcQ/AcQc
for (f1, f2) in ((:Ac_mul_B, :A_mul_B!),
(:Ac_mul_Bc, :A_mul_Bc!))
@eval begin
function ($f1)(A::StridedVecOrMat, Q::Union{QRPackedQ,QRCompactWYQ})
TAQ = promote_type(eltype(A), eltype(Q))
Ac = similar(A, TAQ, (size(A, 2), size(A, 1)))
ctranspose!(Ac, A)
return ($f2)(Ac, convert(AbstractMatrix{TAQ}, Q))
end
end
end
A_ldiv_B!(A::QRCompactWY{T}, b::StridedVector{T}) where {T<:BlasFloat} = (A_ldiv_B!(UpperTriangular(A[:R]), view(Ac_mul_B!(A[:Q], b), 1:size(A, 2))); b)
A_ldiv_B!(A::QRCompactWY{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = (A_ldiv_B!(UpperTriangular(A[:R]), view(Ac_mul_B!(A[:Q], B), 1:size(A, 2), 1:size(B, 2))); B)
# Julia implementation similarly to xgelsy
function A_ldiv_B!(A::QRPivoted{T}, B::StridedMatrix{T}, rcond::Real) where T<:BlasFloat
mA, nA = size(A.factors)
nr = min(mA,nA)
nrhs = size(B, 2)
if nr == 0
return B, 0
end
ar = abs(A.factors[1])
if ar == 0
B[1:nA, :] = 0
return B, 0
end
rnk = 1
xmin = ones(T, 1)
xmax = ones(T, 1)
tmin = tmax = ar
while rnk < nr
tmin, smin, cmin = LAPACK.laic1!(2, xmin, tmin, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1])
tmax, smax, cmax = LAPACK.laic1!(1, xmax, tmax, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1])
tmax*rcond > tmin && break
push!(xmin, cmin)
push!(xmax, cmax)
for i = 1:rnk
xmin[i] *= smin
xmax[i] *= smax
end
rnk += 1
end
C, τ = LAPACK.tzrzf!(A.factors[1:rnk,:])
A_ldiv_B!(UpperTriangular(C[1:rnk,1:rnk]),view(Ac_mul_B!(getq(A),view(B, 1:mA, 1:nrhs)),1:rnk,1:nrhs))
B[rnk+1:end,:] = zero(T)
LAPACK.ormrz!('L', eltype(B)<:Complex ? 'C' : 'T', C, τ, view(B,1:nA,1:nrhs))
B[1:nA,:] = view(B, 1:nA, :)[invperm(A[:p]::Vector{BlasInt}),:]
return B, rnk
end
A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = vec(A_ldiv_B!(A,reshape(B,length(B),1)))
A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A_ldiv_B!(A, B, maximum(size(A))*eps(real(float(one(eltype(B))))))[1]
function A_ldiv_B!(A::QR{T}, B::StridedMatrix{T}) where T
m, n = size(A)
minmn = min(m,n)
mB, nB = size(B)
Ac_mul_B!(A[:Q], view(B, 1:m, :))
R = A[:R]
@inbounds begin
if n > m # minimum norm solution
τ = zeros(T,m)
for k = m:-1:1 # Trapezoid to triangular by elementary operation
x = view(R, k, [k; m + 1:n])
τk = reflector!(x)
τ[k] = τk'
for i = 1:k - 1
vRi = R[i,k]
for j = m + 1:n
vRi += R[i,j]*x[j - m + 1]'
end
vRi *= τk
R[i,k] -= vRi
for j = m + 1:n
R[i,j] -= vRi*x[j - m + 1]
end
end
end
end
Base.A_ldiv_B!(UpperTriangular(view(R, :, 1:minmn)), view(B, 1:minmn, :))
if n > m # Apply elementary transformation to solution
B[m + 1:mB,1:nB] = zero(T)
for j = 1:nB
for k = 1:m
vBj = B[k,j]
for i = m + 1:n
vBj += B[i,j]*R[k,i]'
end
vBj *= τ[k]
B[k,j] -= vBj
for i = m + 1:n
B[i,j] -= R[k,i]*vBj
end
end
end
end
end
return B
end
A_ldiv_B!(A::QR, B::StridedVector) = A_ldiv_B!(A, reshape(B, length(B), 1))[:]
function A_ldiv_B!(A::QRPivoted, b::StridedVector)
A_ldiv_B!(QR(A.factors,A.τ), b)
b[1:size(A.factors, 2)] = view(b, 1:size(A.factors, 2))[invperm(A.jpvt)]
b
end
function A_ldiv_B!(A::QRPivoted, B::StridedMatrix)
A_ldiv_B!(QR(A.factors, A.τ), B)
B[1:size(A.factors, 2),:] = view(B, 1:size(A.factors, 2), :)[invperm(A.jpvt),:]
B
end
# convenience methods
## return only the solution of a least squares problem while avoiding promoting
## vectors to matrices.
_cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x
_cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X
## append right hand side with zeros if necessary
_zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n))
_zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2))
function (\)(A::Union{QR{TA},QRCompactWY{TA},QRPivoted{TA}}, B::AbstractVecOrMat{TB}) where {TA,TB}
S = promote_type(TA,TB)
m, n = size(A)
m == size(B,1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(B,1)) rows"))
AA = convert(Factorization{S}, A)
X = _zeros(S, B, n)
X[1:size(B, 1), :] = B
A_ldiv_B!(AA, X)
return _cut_B(X, 1:n)
end
# With a real lhs and complex rhs with the same precision, we can reinterpret the complex
# rhs as a real rhs with twice the number of columns.
# convenience methods to compute the return size correctly for vectors and matrices
_ret_size(A::Factorization, b::AbstractVector) = (max(size(A, 2), length(b)),)
_ret_size(A::Factorization, B::AbstractMatrix) = (max(size(A, 2), size(B, 1)), size(B, 2))
function (\)(A::Union{QR{T},QRCompactWY{T},QRPivoted{T}}, BIn::VecOrMat{Complex{T}}) where T<:BlasReal
m, n = size(A)
m == size(BIn, 1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(BIn,1)) rows"))
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
# |z2|z4| -> |y1|y2|y3|y4| -> |x2|y2| -> |x2|y2|x4|y4|
# |x3|y3|
# |x4|y4|
B = reshape(transpose(reinterpret(T, BIn, (2, length(BIn)))), size(BIn, 1), 2*size(BIn, 2))
X = _zeros(T, B, n)
X[1:size(B, 1), :] = B
A_ldiv_B!(A, X)
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
# |z2|z4| <- |y1|y2|y3|y4| <- |x2|y2| <- |x2|y2|x4|y4|
# |x3|y3|
# |x4|y4|
XX = reinterpret(Complex{T}, transpose(reshape(X, div(length(X), 2), 2)), _ret_size(A, BIn))
return _cut_B(XX, 1:n)
end
##TODO: Add methods for rank(A::QRP{T}) and adjust the (\) method accordingly
## Add rcond methods for Cholesky, LU, QR and QRP types
## Lower priority: Add LQ, QL and RQ factorizations
# FIXME! Should add balancing option through xgebal