Skip to content

Commit 712f3c9

Browse files
committed
also implement LQ
1 parent d8a48f9 commit 712f3c9

2 files changed

Lines changed: 166 additions & 160 deletions

File tree

src/implementations/lq.jl

Lines changed: 160 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -86,65 +86,48 @@ for f! in (:lq_full!, :lq_compact!)
8686
end
8787
end
8888

89-
# Implementation
90-
# --------------
91-
function lq_full!(A::AbstractMatrix, LQ, alg::LAPACK_HouseholderLQ)
92-
check_input(lq_full!, A, LQ, alg)
93-
L, Q = LQ
94-
_lapack_lq!(A, L, Q; alg.kwargs...)
95-
return L, Q
96-
end
97-
function lq_compact!(A::AbstractMatrix, LQ, alg::LAPACK_HouseholderLQ)
98-
check_input(lq_compact!, A, LQ, alg)
99-
L, Q = LQ
100-
_lapack_lq!(A, L, Q; alg.kwargs...)
101-
return L, Q
102-
end
103-
function lq_null!(A::AbstractMatrix, Nᴴ, alg::LAPACK_HouseholderLQ)
104-
check_input(lq_null!, A, Nᴴ, alg)
105-
_lapack_lq_null!(A, Nᴴ; alg.kwargs...)
106-
return Nᴴ
107-
end
89+
# ==========================
90+
# IMPLEMENTATIONS
91+
# ==========================
10892

109-
function lq_full!(A::AbstractMatrix, LQ, alg::LQViaTransposedQR)
93+
# Householder
94+
# -----------
95+
function lq_full!(A, LQ, alg::Householder)
11096
check_input(lq_full!, A, LQ, alg)
111-
L, Q = LQ
112-
lq_via_qr!(A, L, Q, alg.qr_alg)
113-
return L, Q
97+
return householder_lq!(alg.driver, A, LQ...; alg.kwargs...)
11498
end
115-
function lq_compact!(A::AbstractMatrix, LQ, alg::LQViaTransposedQR)
99+
function lq_compact!(A, LQ, alg::Householder)
116100
check_input(lq_compact!, A, LQ, alg)
117-
L, Q = LQ
118-
lq_via_qr!(A, L, Q, alg.qr_alg)
119-
return L, Q
101+
return householder_lq!(alg.driver, A, LQ...; alg.kwargs...)
120102
end
121-
function lq_null!(A::AbstractMatrix, Nᴴ, alg::LQViaTransposedQR)
103+
function lq_null!(A, Nᴴ, alg::Householder)
122104
check_input(lq_null!, A, Nᴴ, alg)
123-
lq_null_via_qr!(A, Nᴴ, alg.qr_alg)
124-
return Nᴴ
105+
return householder_lq_null!(alg.driver, A, Nᴴ; alg.kwargs...)
125106
end
126107

127-
function lq_full!(A::AbstractMatrix, LQ, alg::DiagonalAlgorithm)
128-
check_input(lq_full!, A, LQ, alg)
129-
L, Q = LQ
130-
_diagonal_lq!(A, L, Q; alg.kwargs...)
131-
return L, Q
108+
householder_lq!(::DefaultDriver, A, L, Q; kwargs...) =
109+
householder_lq!(default_householder_driver(A), A, L, Q; kwargs...)
110+
householder_lq_null!(::DefaultDriver, A, Nᴴ; kwargs...) =
111+
householder_lq_null!(default_householder_driver(A), A, Nᴴ; kwargs...)
112+
113+
# dispatch helpers
114+
for f in (:gelqt!, :gemlqt!, :gelqf!, :unglq!, :unmlq!)
115+
@eval begin
116+
$f(::LAPACK, args...) = YALAPACK.$f(args...)
117+
end
132118
end
133-
function lq_compact!(A::AbstractMatrix, LQ, alg::DiagonalAlgorithm)
134-
check_input(lq_compact!, A, LQ, alg)
135-
L, Q = LQ
136-
_diagonal_lq!(A, L, Q; alg.kwargs...)
137-
return L, Q
119+
120+
function householder_lq!(driver::Union{CUSOLVER, ROCSOLVER, GLA}, A, L, Q; kwargs...)
121+
qr_alg = driver === GLA() ? GLA_HouseholderQR(; kwargs...) : Householder(driver; kwargs...)
122+
return lq_via_qr!(A, L, Q, qr_alg)
138123
end
139-
function lq_null!(A::AbstractMatrix, N, alg::DiagonalAlgorithm)
140-
check_input(lq_null!, A, N, alg)
141-
return _diagonal_lq_null!(A, N; alg.kwargs...)
124+
function householder_lq_null!(driver::Union{CUSOLVER, ROCSOLVER, GLA}, A, Nᴴ; kwargs...)
125+
qr_alg = driver === GLA() ? GLA_HouseholderQR(; kwargs...) : Householder(driver; kwargs...)
126+
return lq_null_via_qr!(A, Nᴴ, qr_alg)
142127
end
143128

144-
# LAPACK logic
145-
# ------------
146-
function _lapack_lq!(
147-
A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix;
129+
function householder_lq!(
130+
driver::LAPACK, A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix;
148131
positive = true, pivoted = false,
149132
blocksize = ((pivoted || A === Q) ? 1 : YALAPACK.default_qr_blocksize(A))
150133
)
@@ -153,31 +136,29 @@ function _lapack_lq!(
153136
computeL = length(L) > 0
154137
inplaceQ = Q === A
155138

156-
if pivoted && (blocksize > 1)
157-
throw(ArgumentError("LAPACK does not provide a blocked implementation for a pivoted LQ decomposition"))
158-
end
159-
if inplaceQ && (computeL || positive || blocksize > 1 || n < m)
160-
throw(ArgumentError("inplace Q only supported if matrix is wide (`m <= n`), L is not required, and using the unblocked algorithm (`blocksize=1`) with `positive=false`"))
161-
end
139+
pivoted && (blocksize > 1) &&
140+
throw(ArgumentError(lazy"$driver does not provide a blocked pivoted LQ decomposition"))
141+
(inplaceQ && (computeL || positive || blocksize > 1 || n < m)) &&
142+
throw(ArgumentError("inplace Q only supported if matrix is wide (`m <= n`), L is not required, and using the unblocked algorithm (`blocksize = 1`) with `positive = false`"))
162143

163144
if blocksize > 1
164145
mb = min(minmn, blocksize)
165146
if computeL # first use L as space for T
166-
A, T = YALAPACK.gelqt!(A, view(L, 1:mb, 1:minmn))
147+
A, T = gelqt!(driver, A, view(L, 1:mb, 1:minmn))
167148
else
168-
A, T = YALAPACK.gelqt!(A, similar(A, mb, minmn))
149+
A, T = gelqt!(driver, A, similar(A, mb, minmn))
169150
end
170-
Q = YALAPACK.gemlqt!('R', 'N', A, T, one!(Q))
151+
Q = gemlqt!(driver, 'R', 'N', A, T, one!(Q))
171152
else
172-
A, τ = YALAPACK.gelqf!(A)
153+
A, τ = gelqf!(driver, A)
173154
if inplaceQ
174-
Q = YALAPACK.unglq!(A, τ)
155+
Q = unglq!(driver, A, τ)
175156
else
176-
Q = YALAPACK.unmlq!('R', 'N', A, τ, one!(Q))
157+
Q = unmlq!(driver, 'R', 'N', A, τ, one!(Q))
177158
end
178159
end
179160

180-
if positive # already fix Q even if we do not need R
161+
if positive # already fix Q even if we do not need L
181162
@inbounds for j in 1:n
182163
@simd for i in 1:minmn
183164
s = sign_safe(A[i, i])
@@ -200,28 +181,102 @@ function _lapack_lq!(
200181
end
201182
return L, Q
202183
end
203-
204-
function _lapack_lq_null!(
205-
A::AbstractMatrix, Nᴴ::AbstractMatrix;
184+
function householder_lq_null!(
185+
driver::LAPACK, A::AbstractMatrix, Nᴴ::AbstractMatrix;
206186
positive = true, pivoted = false, blocksize = YALAPACK.default_qr_blocksize(A)
207187
)
208188
m, n = size(A)
209189
minmn = min(m, n)
210-
fill!(Nᴴ, zero(eltype(Nᴴ)))
190+
zero!(Nᴴ)
211191
one!(view(Nᴴ, 1:(n - minmn), (minmn + 1):n))
212192
if blocksize > 1
213193
mb = min(minmn, blocksize)
214-
A, T = YALAPACK.gelqt!(A, similar(A, mb, minmn))
215-
Nᴴ = YALAPACK.gemlqt!('R', 'N', A, T, Nᴴ)
194+
A, T = gelqt!(driver, A, similar(A, mb, minmn))
195+
Nᴴ = gemlqt!(driver, 'R', 'N', A, T, Nᴴ)
216196
else
217-
A, τ = YALAPACK.gelqf!(A)
218-
Nᴴ = YALAPACK.unmlq!('R', 'N', A, τ, Nᴴ)
197+
A, τ = gelqf!(driver, A)
198+
Nᴴ = unmlq!(driver, 'R', 'N', A, τ, Nᴴ)
199+
end
200+
return Nᴴ
201+
end
202+
function householder_lq!(
203+
::Native, A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix;
204+
positive::Bool = true # always true regardless of setting
205+
)
206+
m, n = size(A)
207+
minmn = min(m, n)
208+
@inbounds for i in 1:minmn
209+
for j in 1:(i - 1)
210+
L[i, j] = A[i, j]
211+
end
212+
β, v, L[i, i] = _householder!(conj!(view(A, i, i:n)), 1)
213+
for j in (i + 1):size(L, 2)
214+
L[i, j] = 0
215+
end
216+
H = HouseholderReflection(conj(β), v, i:n)
217+
rmul!(A, H; rows = (i + 1):m)
218+
# A[i, i] == 1; store β instead
219+
A[i, i] = β
220+
end
221+
# copy remaining rows for m > n
222+
@inbounds for j in 1:size(L, 2)
223+
for i in (minmn + 1):m
224+
L[i, j] = A[i, j]
225+
end
226+
end
227+
# build Q
228+
one!(Q)
229+
@inbounds for i in minmn:-1:1
230+
β = A[i, i]
231+
A[i, i] = 1
232+
Hᴴ = HouseholderReflection(β, view(A, i, i:n), i:n)
233+
rmul!(Q, Hᴴ)
234+
end
235+
return L, Q
236+
end
237+
function householder_lq_null!(::Native, A::AbstractMatrix, Nᴴ::AbstractMatrix; positive::Bool = true)
238+
m, n = size(A)
239+
minmn = min(m, n)
240+
@inbounds for i in 1:minmn
241+
β, v, ν = _householder!(conj!(view(A, i, i:n)), 1)
242+
H = HouseholderReflection(conj(β), v, i:n)
243+
rmul!(A, H; rows = (i + 1):m)
244+
# A[i, i] == 1; store β instead
245+
A[i, i] = β
246+
end
247+
# build Nᴴ
248+
zero!(Nᴴ)
249+
one!(view(Nᴴ, 1:(n - minmn), (minmn + 1):n))
250+
@inbounds for i in minmn:-1:1
251+
β = A[i, i]
252+
A[i, i] = 1
253+
Hᴴ = HouseholderReflection(β, view(A, i, i:n), i:n)
254+
rmul!(Nᴴ, Hᴴ)
219255
end
220256
return Nᴴ
221257
end
222258

259+
223260
# LQ via transposition and QR
224261
# ---------------------------
262+
function lq_full!(A::AbstractMatrix, LQ, alg::LQViaTransposedQR)
263+
check_input(lq_full!, A, LQ, alg)
264+
L, Q = LQ
265+
lq_via_qr!(A, L, Q, alg.qr_alg)
266+
return L, Q
267+
end
268+
function lq_compact!(A::AbstractMatrix, LQ, alg::LQViaTransposedQR)
269+
check_input(lq_compact!, A, LQ, alg)
270+
L, Q = LQ
271+
lq_via_qr!(A, L, Q, alg.qr_alg)
272+
return L, Q
273+
end
274+
function lq_null!(A::AbstractMatrix, Nᴴ, alg::LQViaTransposedQR)
275+
check_input(lq_null!, A, Nᴴ, alg)
276+
lq_null_via_qr!(A, Nᴴ, alg.qr_alg)
277+
return Nᴴ
278+
end
279+
225280
function lq_via_qr!(
226281
A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix, qr_alg::AbstractAlgorithm
227282
)
@@ -250,8 +305,26 @@ function lq_null_via_qr!(A::AbstractMatrix, N::AbstractMatrix, qr_alg::AbstractA
250305
return N
251306
end
252307

253-
# Diagonal logic
254-
# --------------
308+
309+
# Diagonal
310+
# --------
311+
function lq_full!(A::AbstractMatrix, LQ, alg::DiagonalAlgorithm)
312+
check_input(lq_full!, A, LQ, alg)
313+
L, Q = LQ
314+
_diagonal_lq!(A, L, Q; alg.kwargs...)
315+
return L, Q
316+
end
317+
function lq_compact!(A::AbstractMatrix, LQ, alg::DiagonalAlgorithm)
318+
check_input(lq_compact!, A, LQ, alg)
319+
L, Q = LQ
320+
_diagonal_lq!(A, L, Q; alg.kwargs...)
321+
return L, Q
322+
end
323+
function lq_null!(A::AbstractMatrix, N, alg::DiagonalAlgorithm)
324+
check_input(lq_null!, A, N, alg)
325+
return _diagonal_lq_null!(A, N; alg.kwargs...)
326+
end
327+
255328
function _diagonal_lq!(
256329
A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix; positive::Bool = true
257330
)
@@ -271,84 +344,22 @@ end
271344

272345
_diagonal_lq_null!(A::AbstractMatrix, N; positive::Bool = true) = N
273346

274-
# Native logic
275-
# -------------
276-
function lq_full!(A::AbstractMatrix, LQ, alg::Native_HouseholderLQ)
277-
check_input(lq_full!, A, LQ, alg)
278-
L, Q = LQ
279-
A === Q &&
280-
throw(ArgumentError("inplace Q not supported with native LQ implementation"))
281-
_native_lq!(A, L, Q; alg.kwargs...)
282-
return L, Q
283-
end
284-
function lq_compact!(A::AbstractMatrix, LQ, alg::Native_HouseholderLQ)
285-
check_input(lq_compact!, A, LQ, alg)
286-
L, Q = LQ
287-
A === Q &&
288-
throw(ArgumentError("inplace Q not supported with native LQ implementation"))
289-
_native_lq!(A, L, Q; alg.kwargs...)
290-
return L, Q
291-
end
292-
function lq_null!(A::AbstractMatrix, N, alg::Native_HouseholderLQ)
293-
check_input(lq_null!, A, N, alg)
294-
_native_lq_null!(A, N; alg.kwargs...)
295-
return N
296-
end
297-
298-
function _native_lq!(
299-
A::AbstractMatrix, L::AbstractMatrix, Q::AbstractMatrix;
300-
positive::Bool = true # always true regardless of setting
301-
)
302-
m, n = size(A)
303-
minmn = min(m, n)
304-
@inbounds for i in 1:minmn
305-
for j in 1:(i - 1)
306-
L[i, j] = A[i, j]
307-
end
308-
β, v, L[i, i] = _householder!(conj!(view(A, i, i:n)), 1)
309-
for j in (i + 1):size(L, 2)
310-
L[i, j] = 0
311-
end
312-
H = Householder(conj(β), v, i:n)
313-
rmul!(A, H; rows = (i + 1):m)
314-
# A[i, i] == 1; store β instead
315-
A[i, i] = β
316-
end
317-
# copy remaining rows for m > n
318-
@inbounds for j in 1:size(L, 2)
319-
for i in (minmn + 1):m
320-
L[i, j] = A[i, j]
321-
end
322-
end
323-
# build Q
324-
one!(Q)
325-
@inbounds for i in minmn:-1:1
326-
β = A[i, i]
327-
A[i, i] = 1
328-
Hᴴ = Householder(β, view(A, i, i:n), i:n)
329-
rmul!(Q, Hᴴ)
330-
end
331-
return L, Q
332-
end
333-
334-
function _native_lq_null!(A::AbstractMatrix, Nᴴ::AbstractMatrix; positive::Bool = true)
335-
m, n = size(A)
336-
minmn = min(m, n)
337-
@inbounds for i in 1:minmn
338-
β, v, ν = _householder!(conj!(view(A, i, i:n)), 1)
339-
H = Householder(conj(β), v, i:n)
340-
rmul!(A, H; rows = (i + 1):m)
341-
# A[i, i] == 1; store β instead
342-
A[i, i] = β
343-
end
344-
# build Nᴴ
345-
fill!(Nᴴ, zero(eltype(Nᴴ)))
346-
one!(view(Nᴴ, 1:(n - minmn), (minmn + 1):n))
347-
@inbounds for i in minmn:-1:1
348-
β = A[i, i]
349-
A[i, i] = 1
350-
Hᴴ = Householder(β, view(A, i, i:n), i:n)
351-
rmul!(Nᴴ, Hᴴ)
347+
# Deprecations
348+
# ------------
349+
for drivertype in (:LAPACK, :Native)
350+
algtype = Symbol(drivertype, :_HouseholderLQ)
351+
@eval begin
352+
Base.@deprecate(
353+
lq_full!(A, LQ, alg::$algtype),
354+
lq_full!(A, LQ, Householder($drivertype(), alg.kwargs))
355+
)
356+
Base.@deprecate(
357+
lq_compact!(A, LQ, alg::$algtype),
358+
lq_compact!(A, LQ, Householder($drivertype(), alg.kwargs))
359+
)
360+
Base.@deprecate(
361+
lq_null!(A, Nᴴ, alg::$algtype),
362+
lq_null!(A, Nᴴ, Householder($drivertype(), alg.kwargs))
363+
)
352364
end
353-
return Nᴴ
354365
end

0 commit comments

Comments
 (0)