Skip to content

Commit 4d3022e

Browse files
dkarraschKristofferC
authored andcommitted
Handle AbstractQ in concatenation (#51132)
Handling `AbstractQ`s in concatenation got lost in the overhaul. This brings it back (though it seemed to not be used anywhere), and even better: pre-1.9 `Q`s where handled via `AbstractMatrix` fallbacks, so elementwise. Now, it first materializes the matrix, and then copies to the right place in the destination array. (cherry picked from commit 50146b2)
1 parent f2c447d commit 4d3022e

File tree

5 files changed

+128
-115
lines changed

5 files changed

+128
-115
lines changed

base/abstractarray.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,17 +1811,16 @@ function __cat_offset1!(A, shape, catdims, offsets, x)
18111811
inds = ntuple(length(offsets)) do i
18121812
(i <= length(catdims) && catdims[i]) ? offsets[i] .+ cat_indices(x, i) : 1:shape[i]
18131813
end
1814-
if x isa AbstractArray
1815-
A[inds...] = x
1816-
else
1817-
fill!(view(A, inds...), x)
1818-
end
1814+
_copy_or_fill!(A, inds, x)
18191815
newoffsets = ntuple(length(offsets)) do i
18201816
(i <= length(catdims) && catdims[i]) ? offsets[i] + cat_size(x, i) : offsets[i]
18211817
end
18221818
return newoffsets
18231819
end
18241820

1821+
_copy_or_fill!(A, inds, x) = fill!(view(A, inds...), x)
1822+
_copy_or_fill!(A, inds, x::AbstractArray) = (A[inds...] = x)
1823+
18251824
"""
18261825
vcat(A...)
18271826

stdlib/LinearAlgebra/src/abstractq.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ end
88

99
parent(adjQ::AdjointQ) = adjQ.Q
1010
eltype(::Type{<:AbstractQ{T}}) where {T} = T
11+
Base.eltypeof(Q::AbstractQ) = eltype(Q)
1112
ndims(::AbstractQ) = 2
1213

1314
# inversion/adjoint/transpose
@@ -129,6 +130,16 @@ function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,per
129130
end
130131
return dest
131132
end
133+
# used in concatenations: Base.__cat_offset1!
134+
Base._copy_or_fill!(A, inds, Q::AbstractQ) = (A[inds...] = collect(Q))
135+
# overloads of helper functions
136+
Base.cat_size(A::AbstractQ) = size(A)
137+
Base.cat_size(A::AbstractQ, d) = size(A, d)
138+
Base.cat_length(a::AbstractQ) = prod(size(a))
139+
Base.cat_ndims(a::AbstractQ) = ndims(a)
140+
Base.cat_indices(A::AbstractQ, d) = axes(A, d)
141+
Base.cat_similar(A::AbstractQ, T::Type, shape::Tuple) = Array{T}(undef, shape)
142+
Base.cat_similar(A::AbstractQ, T::Type, shape::Vector) = Array{T}(undef, shape...)
132143

133144
function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ)
134145
print(io, Base.dims2string(size(Q)), ' ', summary(Q))

stdlib/LinearAlgebra/src/special.jl

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,116 @@ const _SpecialArrays = Union{}
336336

337337
promote_to_array_type(::Tuple) = Matrix
338338

339+
# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices
340+
# in A to matrices of type T and sizes given by n[k:end]. n is an array
341+
# so that the same promotion code can be used for hvcat. We pass the type T
342+
# so that we can re-use this code for sparse-matrix hcat etcetera.
343+
promote_to_arrays_(n::Int, ::Type, a::Number) = a
344+
promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n)
345+
promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A
346+
promote_to_arrays_(n::Int, ::Type, A::AbstractQ) = collect(A)
347+
promote_to_arrays(n,k, ::Type) = ()
348+
promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),)
349+
promote_to_arrays(n,k, ::Type{T}, A, B) where {T} =
350+
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B))
351+
promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} =
352+
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C))
353+
promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} =
354+
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...)
355+
356+
_us2number(A) = A
357+
_us2number(J::UniformScaling) = J.λ
358+
359+
for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols"))
360+
@eval begin
361+
@inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling}...) = $_f(A...)
362+
# if there's a Number present, J::UniformScaling must be 1x1-dimensional
363+
@inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = $f(map(_us2number, A)...)
364+
function $_f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A))
365+
n = -1
366+
for a in A
367+
if !isa(a, UniformScaling)
368+
require_one_based_indexing(a)
369+
na = size(a,$dim)
370+
n >= 0 && n != na &&
371+
throw(DimensionMismatch(string("number of ", $name,
372+
" of each array must match (got ", n, " and ", na, ")")))
373+
n = na
374+
end
375+
end
376+
n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size")))
377+
return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim))
378+
end
379+
end
380+
end
381+
382+
hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling}...) = _hvcat(rows, A...)
383+
hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = _hvcat(rows, A...)
384+
function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A))
385+
require_one_based_indexing(A...)
386+
nr = length(rows)
387+
sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments"))
388+
n = fill(-1, length(A))
389+
needcols = false # whether we also need to infer some sizes from the column count
390+
j = 0
391+
for i = 1:nr # infer UniformScaling sizes from row counts, if possible:
392+
ni = -1 # number of rows in this block-row, -1 indicates unknown
393+
for k = 1:rows[i]
394+
if !isa(A[j+k], UniformScaling)
395+
na = size(A[j+k], 1)
396+
ni >= 0 && ni != na &&
397+
throw(DimensionMismatch("mismatch in number of rows"))
398+
ni = na
399+
end
400+
end
401+
if ni >= 0
402+
for k = 1:rows[i]
403+
n[j+k] = ni
404+
end
405+
else # row consisted only of UniformScaling objects
406+
needcols = true
407+
end
408+
j += rows[i]
409+
end
410+
if needcols # some sizes still unknown, try to infer from column count
411+
nc = -1
412+
j = 0
413+
for i = 1:nr
414+
nci = 0
415+
rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue)
416+
for k = 1:rows[i]
417+
nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2)
418+
end
419+
nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns"))
420+
nc = nci
421+
j += rows[i]
422+
end
423+
nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred"))
424+
j = 0
425+
for i = 1:nr
426+
if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings
427+
nci, r = divrem(nc, rows[i])
428+
r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes"))
429+
for k = 1:rows[i]
430+
n[j+k] = nci
431+
end
432+
end
433+
j += rows[i]
434+
end
435+
end
436+
Amat = promote_to_arrays(n, 1, array_type, A...)
437+
# We have two methods for promote_to_array_type, one returning Matrix and
438+
# another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense
439+
# case, we cannot call hvcat for the promoted UniformScalings because this
440+
# causes a stack overflow. In the sparse case, however, we cannot call
441+
# typed_hvcat because we need a sparse output.
442+
if array_type == Matrix
443+
return typed_hvcat(promote_eltype(Amat...), rows, Amat...)
444+
else
445+
return hvcat(rows, Amat...)
446+
end
447+
end
448+
339449
# factorizations
340450
function cholesky(S::RealHermSymComplexHerm{<:Real,<:SymTridiagonal}, ::NoPivot = NoPivot(); check::Bool = true)
341451
T = choltype(eltype(S))

stdlib/LinearAlgebra/src/uniformscaling.jl

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -402,114 +402,6 @@ function cond(J::UniformScaling{T}) where T
402402
return J.λ zero(T) ? onereal : oftype(onereal, Inf)
403403
end
404404

405-
# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices
406-
# in A to matrices of type T and sizes given by n[k:end]. n is an array
407-
# so that the same promotion code can be used for hvcat. We pass the type T
408-
# so that we can re-use this code for sparse-matrix hcat etcetera.
409-
promote_to_arrays_(n::Int, ::Type, a::Number) = a
410-
promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n)
411-
promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A
412-
promote_to_arrays(n,k, ::Type) = ()
413-
promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),)
414-
promote_to_arrays(n,k, ::Type{T}, A, B) where {T} =
415-
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B))
416-
promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} =
417-
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C))
418-
promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} =
419-
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...)
420-
421-
_us2number(A) = A
422-
_us2number(J::UniformScaling) = J.λ
423-
424-
for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols"))
425-
@eval begin
426-
@inline $f(A::Union{AbstractArray,UniformScaling}...) = $_f(A...)
427-
# if there's a Number present, J::UniformScaling must be 1x1-dimensional
428-
@inline $f(A::Union{AbstractArray,UniformScaling,Number}...) = $f(map(_us2number, A)...)
429-
function $_f(A::Union{AbstractArray,UniformScaling,Number}...; array_type = promote_to_array_type(A))
430-
n = -1
431-
for a in A
432-
if !isa(a, UniformScaling)
433-
require_one_based_indexing(a)
434-
na = size(a,$dim)
435-
n >= 0 && n != na &&
436-
throw(DimensionMismatch(string("number of ", $name,
437-
" of each array must match (got ", n, " and ", na, ")")))
438-
n = na
439-
end
440-
end
441-
n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size")))
442-
return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim))
443-
end
444-
end
445-
end
446-
447-
hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,UniformScaling,Number}...) = _hvcat(rows, A...)
448-
function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,UniformScaling,Number}...; array_type = promote_to_array_type(A))
449-
require_one_based_indexing(A...)
450-
nr = length(rows)
451-
sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments"))
452-
n = fill(-1, length(A))
453-
needcols = false # whether we also need to infer some sizes from the column count
454-
j = 0
455-
for i = 1:nr # infer UniformScaling sizes from row counts, if possible:
456-
ni = -1 # number of rows in this block-row, -1 indicates unknown
457-
for k = 1:rows[i]
458-
if !isa(A[j+k], UniformScaling)
459-
na = size(A[j+k], 1)
460-
ni >= 0 && ni != na &&
461-
throw(DimensionMismatch("mismatch in number of rows"))
462-
ni = na
463-
end
464-
end
465-
if ni >= 0
466-
for k = 1:rows[i]
467-
n[j+k] = ni
468-
end
469-
else # row consisted only of UniformScaling objects
470-
needcols = true
471-
end
472-
j += rows[i]
473-
end
474-
if needcols # some sizes still unknown, try to infer from column count
475-
nc = -1
476-
j = 0
477-
for i = 1:nr
478-
nci = 0
479-
rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue)
480-
for k = 1:rows[i]
481-
nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2)
482-
end
483-
nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns"))
484-
nc = nci
485-
j += rows[i]
486-
end
487-
nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred"))
488-
j = 0
489-
for i = 1:nr
490-
if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings
491-
nci, r = divrem(nc, rows[i])
492-
r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes"))
493-
for k = 1:rows[i]
494-
n[j+k] = nci
495-
end
496-
end
497-
j += rows[i]
498-
end
499-
end
500-
Amat = promote_to_arrays(n, 1, array_type, A...)
501-
# We have two methods for promote_to_array_type, one returning Matrix and
502-
# another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense
503-
# case, we cannot call hvcat for the promoted UniformScalings because this
504-
# causes a stack overflow. In the sparse case, however, we cannot call
505-
# typed_hvcat because we need a sparse output.
506-
if array_type == Matrix
507-
return typed_hvcat(promote_eltype(Amat...), rows, Amat...)
508-
else
509-
return hvcat(rows, Amat...)
510-
end
511-
end
512-
513405
## Matrix construction from UniformScaling
514406
function Matrix{T}(s::UniformScaling, dims::Dims{2}) where {T}
515407
A = zeros(T, dims)

stdlib/LinearAlgebra/test/special.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,10 @@ end
259259
bidiagmat = Bidiagonal(1:N, 1:(N-1), :U)
260260
tridiagmat = Tridiagonal(1:(N-1), 1:N, 1:(N-1))
261261
symtridiagmat = SymTridiagonal(1:N, 1:(N-1))
262-
specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat)
262+
abstractq = qr(tridiagmat).Q
263+
specialmats = (diagmat, bidiagmat, tridiagmat, symtridiagmat, abstractq, zeros(Int,N,N))
263264
for specialmata in specialmats, specialmatb in specialmats
264-
MA = Matrix(specialmata); MB = Matrix(specialmatb)
265+
MA = collect(specialmata); MB = collect(specialmatb)
265266
@test hcat(specialmata, specialmatb) == hcat(MA, MB)
266267
@test vcat(specialmata, specialmatb) == vcat(MA, MB)
267268
@test hvcat((1,1), specialmata, specialmatb) == hvcat((1,1), MA, MB)

0 commit comments

Comments
 (0)