Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LinearAlgebra: diagzero for non-OneTo axes #55252

Merged
merged 14 commits into from
Oct 9, 2024
1 change: 1 addition & 0 deletions stdlib/LinearAlgebra/src/LinearAlgebra.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ public AbstractTriangular,
peakflops,
symmetric,
symmetric_type,
zeroslike
matprod_dest

const BlasFloat = Union{Float64,Float32,ComplexF64,ComplexF32}
Expand Down
15 changes: 7 additions & 8 deletions stdlib/LinearAlgebra/src/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,14 @@ Bidiagonal(A::Bidiagonal) = A
Bidiagonal{T}(A::Bidiagonal{T}) where {T} = A
Bidiagonal{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A.dv, A.ev, A.uplo)

bidiagzero(::Bidiagonal{T}, i, j) where {T} = zero(T)
function bidiagzero(A::Bidiagonal{<:AbstractMatrix}, i, j)
Tel = eltype(eltype(A.dv))
function diagzero(A::Bidiagonal{<:AbstractMatrix}, i, j)
Tel = eltype(A)
if i < j && A.uplo == 'U' #= top right zeros =#
return zeros(Tel, size(A.ev[i], 1), size(A.ev[j-1], 2))
return zeroslike(Tel, axes(A.ev[i], 1), axes(A.ev[j-1], 2))
elseif j < i && A.uplo == 'L' #= bottom left zeros =#
return zeros(Tel, size(A.ev[i-1], 1), size(A.ev[j], 2))
return zeroslike(Tel, axes(A.ev[i-1], 1), axes(A.ev[j], 2))
else
return zeros(Tel, size(A.dv[i], 1), size(A.dv[j], 2))
return zeroslike(Tel, axes(A.dv[i], 1), axes(A.dv[j], 2))
end
end

Expand Down Expand Up @@ -161,7 +160,7 @@ end
elseif i == j - _offdiagind(A.uplo)
return @inbounds A.ev[A.uplo == 'U' ? i : j]
else
return bidiagzero(A, i, j)
return diagzero(A, i, j)
end
end

Expand All @@ -173,7 +172,7 @@ end
# we explicitly compare the possible bands as b.band may be constant-propagated
return @inbounds A.ev[b.index]
else
return bidiagzero(A, Tuple(_cartinds(b))...)
return diagzero(A, Tuple(_cartinds(b))...)
end
end

Expand Down
24 changes: 22 additions & 2 deletions stdlib/LinearAlgebra/src/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,28 @@ end
end
r
end
diagzero(::Diagonal{T}, i, j) where {T} = zero(T)
diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2))
"""
diagzero(A::AbstractMatrix, i, j)

Return the appropriate zero element `A[i, j]` corresponding to a banded matrix `A`.
"""
diagzero(A::AbstractMatrix, i, j) = zero(eltype(A))
diagzero(D::Diagonal{M}, i, j) where {T,M<:AbstractMatrix{T}} =
jishnub marked this conversation as resolved.
Show resolved Hide resolved
zeroslike(M, axes(D.diag[i], 1), axes(D.diag[j], 2))
# dispatching on the axes permits specializing on the axis types to return something other than an Array
zeroslike(M::Type, ax::Vararg{Union{AbstractUnitRange, Integer}}) = zeroslike(M, ax)
"""
zeroslike(::Type{M}, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) where {M<:AbstractMatrix}

Return an appropriate zero-ed array similar to `M`, with either
the axes `ax`, or the `size` `map(length, ax)`.
This will be used as a structural zero element of a banded matrix. By default, `zeroslike` falls back to
using the size along each axis to construct the array.
"""
zeroslike(M::Type, ax::Tuple{AbstractUnitRange, Vararg{AbstractUnitRange}}) = zeroslike(M, map(length, ax))
function zeroslike(::Type{M}, sz::Tuple{Integer, Vararg{Integer}}) where {M}
zeros(M <: AbstractMatrix ? eltype(M) : M, sz)
end
jishnub marked this conversation as resolved.
Show resolved Hide resolved

@inline function getindex(D::Diagonal, b::BandIndex)
@boundscheck checkbounds(D, b)
Expand Down
10 changes: 10 additions & 0 deletions stdlib/LinearAlgebra/test/bidiag.jl
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,16 @@ end
B = Bidiagonal(dv, ev, :U)
@test B == Matrix{eltype(B)}(B)
end

@testset "non-standard axes" begin
LinearAlgebra.diagzero(T::Type, ax::Tuple{SizedArrays.SOneTo, Vararg{SizedArrays.SOneTo}}) =
zeros(T, ax)

s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4])
B = Bidiagonal(fill(s,4), fill(s,3), :U)
@test @inferred(B[2,1]) isa typeof(s)
@test all(iszero, B[2,1])
end
end

@testset "copyto!" begin
Expand Down
7 changes: 7 additions & 0 deletions stdlib/LinearAlgebra/test/diagonal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,13 @@ end
D = Diagonal(fill(S,3))
@test D * fill(S,2,3)' == fill(S * S', 3, 2)
@test fill(S,3,2)' * D == fill(S' * S, 2, 3)

@testset "indexing with non-standard-axes" begin
s = SizedArrays.SizedArray{(2,2)}([1 2; 3 4])
D = Diagonal(fill(s,3))
@test @inferred(D[1,2]) isa typeof(s)
@test all(iszero, D[1,2])
end
end

@testset "Eigensystem for block diagonal (issue #30681)" begin
Expand Down
3 changes: 3 additions & 0 deletions test/testhelpers/SizedArrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,7 @@ mul!(dest::AbstractMatrix, S1::SizedMatrix, S2::SizedMatrix, α::Number, β::Num
mul!(dest::AbstractVector, M::AbstractMatrix, v::SizedVector, α::Number, β::Number) =
mul!(dest, M, _data(v), α, β)

LinearAlgebra.zeroslike(::Type{S}, ax::Tuple{SizedArrays.SOneTo, Vararg{SizedArrays.SOneTo}}) where {S<:SizedArray} =
zeros(eltype(S), ax)

end