From e7361e7dffd15bf36fa50bba012e9a340d17f441 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Sat, 9 Dec 2023 10:05:16 +0530 Subject: [PATCH 1/6] Specialize matmul dest for structured matrices --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 8 ++++++++ stdlib/LinearAlgebra/src/diagonal.jl | 9 --------- stdlib/LinearAlgebra/src/matmul.jl | 5 ++++- stdlib/LinearAlgebra/src/triangular.jl | 13 ------------- stdlib/LinearAlgebra/test/diagonal.jl | 8 ++++++++ stdlib/LinearAlgebra/test/triangular.jl | 12 ++++++++++++ test/testhelpers/SizedArrays.jl | 5 +++++ 7 files changed, 37 insertions(+), 23 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 10cc9a2f3459a..cdfe19d29aea6 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -542,6 +542,14 @@ _init_eltype(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = _initarray(op, ::Type{TA}, ::Type{TB}, C) where {TA,TB} = similar(C, _init_eltype(op, TA, TB), size(C)) +# destination type for matmul +matprod_dest(A::StructuredMatrix, B::StructuredMatrix, TS) = similar(B, TS, size(B)) +matprod_dest(A, B::StructuredMatrix, TS) = similar(A, TS, size(A)) +matprod_dest(A::StructuredMatrix, B, TS) = similar(B, TS, size(B)) +matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = similar(A, TS) +matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = similar(B, TS) +matprod_dest(A::Diagonal, B::Diagonal, TS) = similar(B, TS) + # General fallback definition for handling under- and overdetermined system as well as square problems # While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs # which is required by LAPACK but not SuiteSparse which allows real-complex solves in some cases. Hence, diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index e47b0248fbe53..b5dd1e761b890 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -308,15 +308,6 @@ function (*)(D::Diagonal, V::AbstractVector) return D.diag .* V end -(*)(A::AbstractMatrix, D::Diagonal) = - mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag))), A, D) -(*)(A::HermOrSym, D::Diagonal) = - mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), A, D) -(*)(D::Diagonal, A::AbstractMatrix) = - mul!(similar(A, promote_op(*, eltype(D.diag), eltype(A))), D, A) -(*)(D::Diagonal, A::HermOrSym) = - mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), D, A) - rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D) lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index adc2d866ad70e..b4ba94a29773c 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -106,8 +106,11 @@ julia> [1 1; 0 1] * [1 0; 1 1] """ function (*)(A::AbstractMatrix, B::AbstractMatrix) TS = promote_op(matprod, eltype(A), eltype(B)) - mul!(similar(B, TS, (size(A, 1), size(B, 2))), A, B) + mul!(matprod_dest(A, B, TS), A, B) end + +matprod_dest(A, B, TS) = similar(B, TS, (size(A, 1), size(B, 2))) + # optimization for dispatching to BLAS, e.g. *(::Matrix{Float32}, ::Matrix{Float64}) # but avoiding the case *(::Matrix{<:BlasComplex}, ::Matrix{<:BlasReal}) # which is better handled by reinterpreting rather than promotion diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 6d703b2436b23..bd55df1c3d2b6 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -1471,12 +1471,6 @@ function *(A::AbstractTriangular, B::AbstractTriangular) end for mat in (:AbstractVector, :AbstractMatrix) - ### Multiplication with triangle to the left and hence rhs cannot be transposed. - @eval function *(A::AbstractTriangular, B::$mat) - require_one_based_indexing(B) - TAB = _init_eltype(*, eltype(A), eltype(B)) - mul!(similar(B, TAB, size(B)), A, B) - end ### Left division with triangle to the left hence rhs cannot be transposed. No quotients. @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat) require_one_based_indexing(B) @@ -1502,13 +1496,6 @@ for mat in (:AbstractVector, :AbstractMatrix) _rdiv!(similar(A, TAB, size(A)), A, B) end end -### Multiplication with triangle to the right and hence lhs cannot be transposed. -# Only for AbstractMatrix, hence outside the above loop. -function *(A::AbstractMatrix, B::AbstractTriangular) - require_one_based_indexing(A) - TAB = _init_eltype(*, eltype(A), eltype(B)) - mul!(similar(A, TAB, size(A)), A, B) -end # ambiguity resolution with definitions in matmul.jl *(v::AdjointAbsVec, A::AbstractTriangular) = adjoint(adjoint(A) * v.parent) *(v::TransposeAbsVec, A::AbstractTriangular) = transpose(transpose(A) * v.parent) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 83a5ea3b3e03d..4f5384e206599 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -1235,4 +1235,12 @@ end end end +@testset "avoid matmul ambiguities with ::MyMatrix * ::AbstractMatrix" begin + A = [i+j for i in 1:2, j in 1:2] + S = SizedArrays.SizedArray{(2,2)}(A) + D = Diagonal([1:2;]) + @test S * D == A * D + @test D * S == D * A +end + end # module TestDiagonal diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index e0ba443ece7df..1b74a5ca3158b 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -8,6 +8,10 @@ using LinearAlgebra: BlasFloat, errorbounds, full!, transpose!, UnitUpperTriangular, UnitLowerTriangular, mul!, rdiv!, rmul!, lmul! +const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") +isdefined(Main, :SizedArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "SizedArrays.jl")) +using .Main.SizedArrays + debug && println("Triangular matrices") n = 9 @@ -866,4 +870,12 @@ end end end +@testset "avoid matmul ambiguities with ::MyMatrix * ::AbstractMatrix" begin + A = [i+j for i in 1:2, j in 1:2] + S = SizedArrays.SizedArray{(2,2)}(A) + U = UpperTriangular(ones(2,2)) + @test S * U == A * U + @test U * S == U * A +end + end # module TestTriangular diff --git a/test/testhelpers/SizedArrays.jl b/test/testhelpers/SizedArrays.jl index fc2862d844b3f..e104aa5095a3a 100644 --- a/test/testhelpers/SizedArrays.jl +++ b/test/testhelpers/SizedArrays.jl @@ -46,4 +46,9 @@ function *(S1::SizedArrayLike, S2::SizedArrayLike) SZ = ndims(data) == 1 ? (size(S1, 1), ) : (size(S1, 1), size(S2, 2)) SizedArray{SZ}(data) end + +# deliberately wide method definition to ensure that this doesn't lead to ambiguities with +# structured matrices +*(S1::SizedArrayLike, M::AbstractMatrix) = _data(S1) * M + end From e92453a0cbe538a60a4e5ae872121ae11316b15c Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 27 Dec 2023 18:29:48 +0100 Subject: [PATCH 2/6] remove `_initarray` and `_init_eltype` --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 19 ----------------- stdlib/LinearAlgebra/src/bidiag.jl | 26 ++++++++++++----------- stdlib/LinearAlgebra/src/diagonal.jl | 8 +++---- stdlib/LinearAlgebra/src/hessenberg.jl | 12 +++++------ stdlib/LinearAlgebra/src/triangular.jl | 13 ++++-------- 5 files changed, 28 insertions(+), 50 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index cdfe19d29aea6..51fd6746247d4 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -523,25 +523,6 @@ _makevector(x::AbstractVector) = Vector(x) _pushzero(A) = (B = similar(A, length(A)+1); @inbounds B[begin:end-1] .= A; @inbounds B[end] = zero(eltype(B)); B) _droplast!(A) = deleteat!(A, lastindex(A)) -# some trait like this would be cool -# onedefined(::Type{T}) where {T} = hasmethod(one, (T,)) -# but we are actually asking for oneunit(T), that is, however, defined for generic T as -# `T(one(T))`, so the question is equivalent for whether one(T) is defined -onedefined(::Type) = false -onedefined(::Type{<:Number}) = true - -# initialize return array for op(A, B) -_init_eltype(::typeof(*), ::Type{TA}, ::Type{TB}) where {TA,TB} = - (onedefined(TA) && onedefined(TB)) ? - typeof(matprod(oneunit(TA), oneunit(TB))) : - promote_op(matprod, TA, TB) -_init_eltype(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = - (onedefined(TA) && onedefined(TB)) ? - typeof(op(oneunit(TA), oneunit(TB))) : - promote_op(op, TA, TB) -_initarray(op, ::Type{TA}, ::Type{TB}, C) where {TA,TB} = - similar(C, _init_eltype(op, TA, TB), size(C)) - # destination type for matmul matprod_dest(A::StructuredMatrix, B::StructuredMatrix, TS) = similar(B, TS, size(B)) matprod_dest(A, B::StructuredMatrix, TS) = similar(A, TS, size(A)) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 78c79b6fcefac..66d81a3d63805 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -801,34 +801,35 @@ ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMa (t = wrapperop(A); _rdiv!(t(c), t(b), t(A)); return c) ### Generic promotion methods and fallbacks -\(A::Bidiagonal, B::AbstractVecOrMat) = ldiv!(_initarray(\, eltype(A), eltype(B), B), A, B) +\(A::Bidiagonal, B::AbstractVecOrMat) = + ldiv!(similar(B, promote_op(\, eltype(A), eltype(B)), size(B)), A, B) \(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function \(B::Bidiagonal, U::$tri) - A = ldiv!(_initarray(\, eltype(B), eltype(U), U), B, U) + A = ldiv!(similar(U, promote_op(\, eltype(B), eltype(U)), size(U)), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function \(U::$tri, B::Bidiagonal) - A = ldiv!(_initarray(\, eltype(U), eltype(B), U), U, B) + A = ldiv!(similar(U, promote_op(\, eltype(U), eltype(B)), size(U)), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function \(B::Bidiagonal, L::$tri) - A = ldiv!(_initarray(\, eltype(B), eltype(L), L), B, L) + A = ldiv!(similar(L, promote_op(\, eltype(B), eltype(L)), size(L)), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function \(L::$tri, B::Bidiagonal) - A = ldiv!(_initarray(\, eltype(L), eltype(B), L), L, B) + A = ldiv!(similar(L, promote_op(\, eltype(L), eltype(B)), size(L)), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end end ### Diagonal specialization function \(B::Bidiagonal, D::Diagonal) - A = ldiv!(_initarray(\, eltype(B), eltype(D), D), B, D) + A = ldiv!(similar(D, promote_op(\, eltype(B), eltype(D)), size(D)), B, D) return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) end @@ -878,33 +879,34 @@ rdiv!(A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = (t = wrapperop(B); ldiv!(t(C), t(B), t(A)); return C) -/(A::AbstractMatrix, B::Bidiagonal) = _rdiv!(_initarray(/, eltype(A), eltype(B), A), A, B) +/(A::AbstractMatrix, B::Bidiagonal) = + _rdiv!(similar(A, promote_op(/, eltype(A), eltype(B)), size(A)), A, B) ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function /(U::$tri, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(U), eltype(B), U), U, B) + A = _rdiv!(similar(U, promote_op(/, eltype(U), eltype(B)), size(U)), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function /(B::Bidiagonal, U::$tri) - A = _rdiv!(_initarray(/, eltype(B), eltype(U), U), B, U) + A = _rdiv!(similar(U, promote_op(/, eltype(B), eltype(U)), size(U)), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function /(L::$tri, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(L), eltype(B), L), L, B) + A = _rdiv!(similar(L, promote_op(/, eltype(L), eltype(B)), size(L)), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function /(B::Bidiagonal, L::$tri) - A = _rdiv!(_initarray(/, eltype(B), eltype(L), L), B, L) + A = _rdiv!(similar(L, promote_op(/, eltype(B), eltype(L)), size(L)), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end end ### Diagonal specialization function /(D::Diagonal, B::Bidiagonal) - A = _rdiv!(_initarray(/, eltype(D), eltype(B), D), D, B) + A = _rdiv!(similar(D, promote_op(/, eltype(D), eltype(B)), size(D)), D, B) return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) end diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 5c2fa92548292..d8729cefc3f07 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -422,8 +422,8 @@ function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) return Diagonal(Da.diag .* Db.diag .* Dc.diag) end -/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D))), A, D) -/(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D)), size(A)), A, D) +/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D))), A, D) +/(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D)), size(A)), A, D) rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) # avoid copy when possible via internal 3-arg backend @@ -449,8 +449,8 @@ function \(D::Diagonal, B::AbstractVector) isnothing(j) || throw(SingularException(j)) return D.diag .\ B end -\(D::Diagonal, B::AbstractMatrix) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B))), D, B) -\(D::Diagonal, B::HermOrSym) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B)), size(B)), D, B) +\(D::Diagonal, B::AbstractMatrix) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B))), D, B) +\(D::Diagonal, B::HermOrSym) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B)), size(B)), D, B) ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B) function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 87f7305f0146c..3a68c3a9aecaa 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -132,29 +132,29 @@ for T = (:Number, :UniformScaling, :Diagonal) end function *(H::UpperHessenberg, U::UpperOrUnitUpperTriangular) - HH = mul!(_initarray(*, eltype(H), eltype(U), H), H, U) + HH = mul!(similar(H, promote_op(matprod, eltype(H), eltype(U)), size(H)), H, U) UpperHessenberg(HH) end function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) - HH = mul!(_initarray(*, eltype(U), eltype(H), H), U, H) + HH = mul!(similar(H, promote_op(matprod, eltype(U), eltype(H)), size(H)), U, H) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UpperTriangular) - HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) + HH = _rdiv!(similar(H, promote_op(/, eltype(H), eltype(U)), size(H)), H, U) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UnitUpperTriangular) - HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) + HH = _rdiv!(similar(H, promote_op(/, eltype(H), eltype(U)), size(H)), H, U) UpperHessenberg(HH) end function \(U::UpperTriangular, H::UpperHessenberg) - HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) + HH = ldiv!(similar(H, promote_op(\, eltype(U), eltype(H)), size(H)), U, H) UpperHessenberg(HH) end function \(U::UnitUpperTriangular, H::UpperHessenberg) - HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) + HH = ldiv!(similar(H, promote_op(\, eltype(U), eltype(H)), size(H)), U, H) UpperHessenberg(HH) end diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 8838b847817ff..7917ebc02149c 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -1505,15 +1505,10 @@ rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!( ## necessary in the general triangular solve problem. _inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} = - _init_eltype(*, TA, TB) + promote_op(matprod, TA, TB) _inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = - _init_eltype(op, TA, TB) + promote_op(op, TA, TB) ## The general promotion methods -function *(A::AbstractTriangular, B::AbstractTriangular) - TAB = _init_eltype(*, eltype(A), eltype(B)) - mul!(similar(B, TAB, size(B)), A, B) -end - for mat in (:AbstractVector, :AbstractMatrix) ### Left division with triangle to the left hence rhs cannot be transposed. No quotients. @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat) @@ -1524,7 +1519,7 @@ for mat in (:AbstractVector, :AbstractMatrix) ### Left division with triangle to the left hence rhs cannot be transposed. Quotients. @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat) require_one_based_indexing(B) - TAB = _init_eltype(\, eltype(A), eltype(B)) + TAB = promote_op(\, eltype(A), eltype(B)) ldiv!(similar(B, TAB, size(B)), A, B) end ### Right division with triangle to the right hence lhs cannot be transposed. No quotients. @@ -1536,7 +1531,7 @@ for mat in (:AbstractVector, :AbstractMatrix) ### Right division with triangle to the right hence lhs cannot be transposed. Quotients. @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular}) require_one_based_indexing(A) - TAB = _init_eltype(/, eltype(A), eltype(B)) + TAB = promote_op(/, eltype(A), eltype(B)) _rdiv!(similar(A, TAB, size(A)), A, B) end end From 237de1bff484081be43e4922c11661096ff09d07 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 27 Dec 2023 18:44:40 +0100 Subject: [PATCH 3/6] use matprod_dest in hessenberg mul --- stdlib/LinearAlgebra/src/hessenberg.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 0b4c0bead03f8..4db1891dc43ce 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -133,11 +133,11 @@ for T = (:Number, :UniformScaling, :Diagonal) end function *(H::UpperHessenberg, U::UpperOrUnitUpperTriangular) - HH = mul!(similar(H, promote_op(matprod, eltype(H), eltype(U)), size(H)), H, U) + HH = mul!(matprod_dest(H, U, promote_op(matprod, eltype(H), eltype(U))), H, U) UpperHessenberg(HH) end function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) - HH = mul!(similar(H, promote_op(matprod, eltype(U), eltype(H)), size(H)), U, H) + HH = mul!(matprod_dest(U, H, promote_op(matprod, eltype(U), eltype(H))), U, H) UpperHessenberg(HH) end From 7d003099a2d683adfada02e94e8cd703a035e93a Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 30 Dec 2023 11:54:45 +0100 Subject: [PATCH 4/6] spread usage of `matprod_dest` --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 2 ++ stdlib/LinearAlgebra/src/bidiag.jl | 18 +++++++++--------- stdlib/LinearAlgebra/src/diagonal.jl | 14 +++++++------- stdlib/LinearAlgebra/src/hessenberg.jl | 8 ++++---- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index 51fd6746247d4..a435b909aba91 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -530,6 +530,8 @@ matprod_dest(A::StructuredMatrix, B, TS) = similar(B, TS, size(B)) matprod_dest(A::StructuredMatrix, B::Diagonal, TS) = similar(A, TS) matprod_dest(A::Diagonal, B::StructuredMatrix, TS) = similar(B, TS) matprod_dest(A::Diagonal, B::Diagonal, TS) = similar(B, TS) +matprod_dest(A::HermOrSym, B::Diagonal, TS) = similar(A, TS, size(A)) +matprod_dest(A::Diagonal, B::HermOrSym, TS) = similar(B, TS, size(B)) # General fallback definition for handling under- and overdetermined system as well as square problems # While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 13699ffcc4b82..2b4b2ac98317c 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -803,27 +803,27 @@ ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMa ### Generic promotion methods and fallbacks \(A::Bidiagonal, B::AbstractVecOrMat) = - ldiv!(similar(B, promote_op(\, eltype(A), eltype(B)), size(B)), A, B) + ldiv!(matprod_dest(A, B, promote_op(\, eltype(A), eltype(B))), A, B) \(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function \(B::Bidiagonal, U::$tri) - A = ldiv!(similar(U, promote_op(\, eltype(B), eltype(U)), size(U)), B, U) + A = ldiv!(matprod_dest(B, U, promote_op(\, eltype(B), eltype(U))), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function \(U::$tri, B::Bidiagonal) - A = ldiv!(similar(U, promote_op(\, eltype(U), eltype(B)), size(U)), U, B) + A = ldiv!(matprod_dest(U, B, promote_op(\, eltype(U), eltype(B))), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function \(B::Bidiagonal, L::$tri) - A = ldiv!(similar(L, promote_op(\, eltype(B), eltype(L)), size(L)), B, L) + A = ldiv!(matprod_dest(B, L, promote_op(\, eltype(B), eltype(L))), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function \(L::$tri, B::Bidiagonal) - A = ldiv!(similar(L, promote_op(\, eltype(L), eltype(B)), size(L)), L, B) + A = ldiv!(matprod_dest(L, B, promote_op(\, eltype(L), eltype(B))), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end end @@ -886,21 +886,21 @@ _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function /(U::$tri, B::Bidiagonal) - A = _rdiv!(similar(U, promote_op(/, eltype(U), eltype(B)), size(U)), U, B) + A = _rdiv!(matprod_dest(U, B, promote_op(/, eltype(U), eltype(B))), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function /(B::Bidiagonal, U::$tri) - A = _rdiv!(similar(U, promote_op(/, eltype(B), eltype(U)), size(U)), B, U) + A = _rdiv!(matprod_dest(B, U, promote_op(/, eltype(B), eltype(U))), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function /(L::$tri, B::Bidiagonal) - A = _rdiv!(similar(L, promote_op(/, eltype(L), eltype(B)), size(L)), L, B) + A = _rdiv!(matprod_dest(L, B, promote_op(/, eltype(L), eltype(B))), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function /(B::Bidiagonal, L::$tri) - A = _rdiv!(similar(L, promote_op(/, eltype(B), eltype(L)), size(L)), B, L) + A = _rdiv!(matprod_dest(B, L, promote_op(/, eltype(B), eltype(L))), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end end diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 9b6ee3245b666..e434df7c12cc1 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -422,8 +422,8 @@ function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) return Diagonal(Da.diag .* Db.diag .* Dc.diag) end -/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D))), A, D) -/(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D)), size(A)), A, D) +/(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(matprod_dest(A, D, promote_op(/, eltype(A), eltype(D))), A, D) +/(A::HermOrSym, D::Diagonal) = _rdiv!(matprod_dest(A, D, promote_op(/, eltype(A), eltype(D))), A, D) rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) # avoid copy when possible via internal 3-arg backend @@ -449,8 +449,8 @@ function \(D::Diagonal, B::AbstractVector) isnothing(j) || throw(SingularException(j)) return D.diag .\ B end -\(D::Diagonal, B::AbstractMatrix) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B))), D, B) -\(D::Diagonal, B::HermOrSym) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B)), size(B)), D, B) +\(D::Diagonal, B::AbstractMatrix) = ldiv!(matprod_dest(D, B, promote_op(\, eltype(D), eltype(B))), D, B) +\(D::Diagonal, B::HermOrSym) = ldiv!(matprod_dest(D, B, promote_op(\, eltype(D), eltype(B))), D, B) ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B) function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) @@ -470,8 +470,8 @@ function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) end # Optimizations for \, / between Diagonals -\(D::Diagonal, B::Diagonal) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B))), D, B) -/(A::Diagonal, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D))), A, D) +\(D::Diagonal, B::Diagonal) = ldiv!(matprod_dest(D, B, promote_op(\, eltype(D), eltype(B))), D, B) +/(A::Diagonal, D::Diagonal) = _rdiv!(matprod_dest(A, D, promote_op(/, eltype(A), eltype(D))), A, D) function _rdiv!(Dc::Diagonal, Db::Diagonal, Da::Diagonal) n, k = length(Db.diag), length(Da.diag) n == k || throw(DimensionMismatch("left hand side has $n columns but D is $k by $k")) @@ -534,7 +534,7 @@ function (/)(S::SymTridiagonal, D::Diagonal) dl = similar(S.ev, T, max(length(S.dv)-1, 0)) _rdiv!(Tridiagonal(dl, d, du), S, D) end -(/)(T::Tridiagonal, D::Diagonal) = _rdiv!(similar(T, promote_op(/, eltype(T), eltype(D))), T, D) +(/)(T::Tridiagonal, D::Diagonal) = _rdiv!(matprod_dest(T, D, promote_op(/, eltype(T), eltype(D))), T, D) function _rdiv!(T::Tridiagonal, S::Union{SymTridiagonal,Tridiagonal}, D::Diagonal) n = size(S, 2) dd = D.diag diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 4db1891dc43ce..3be41baf24b24 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -142,20 +142,20 @@ function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) end function /(H::UpperHessenberg, U::UpperTriangular) - HH = _rdiv!(similar(H, promote_op(/, eltype(H), eltype(U)), size(H)), H, U) + HH = _rdiv!(matprod_dest(H, U, promote_op(/, eltype(H), eltype(U))), H, U) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UnitUpperTriangular) - HH = _rdiv!(similar(H, promote_op(/, eltype(H), eltype(U)), size(H)), H, U) + HH = _rdiv!(matprod_dest(H, U, promote_op(/, eltype(H), eltype(U))), H, U) UpperHessenberg(HH) end function \(U::UpperTriangular, H::UpperHessenberg) - HH = ldiv!(similar(H, promote_op(\, eltype(U), eltype(H)), size(H)), U, H) + HH = ldiv!(matprod_dest(U, H, promote_op(\, eltype(U), eltype(H))), U, H) UpperHessenberg(HH) end function \(U::UnitUpperTriangular, H::UpperHessenberg) - HH = ldiv!(similar(H, promote_op(\, eltype(U), eltype(H)), size(H)), U, H) + HH = ldiv!(matprod_dest(U, H, promote_op(\, eltype(U), eltype(H))), U, H) UpperHessenberg(HH) end From 023b435fee5144ec2be333a33de431eb48f01a7e Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 30 Dec 2023 18:07:11 +0100 Subject: [PATCH 5/6] delete unnecessary methods --- stdlib/LinearAlgebra/src/diagonal.jl | 3 --- stdlib/LinearAlgebra/src/triangular.jl | 3 --- 2 files changed, 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index e434df7c12cc1..063dde619bd1a 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -867,9 +867,6 @@ function svd(D::Diagonal{T}) where {T<:Number} return SVD(U, S, Vt) end -# disambiguation methods: * and / of Diagonal and Adj/Trans AbsVec -*(u::AdjointAbsVec, D::Diagonal) = (D'u')' -*(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) * transpose(u)) *(x::AdjointAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) *(x::TransposeAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) /(u::AdjointAbsVec, D::Diagonal) = (D' \ u')' diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 1790e7e701fab..e2e911d66b7d2 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -1536,9 +1536,6 @@ for mat in (:AbstractVector, :AbstractMatrix) _rdiv!(similar(A, TAB, size(A)), A, B) end end -# ambiguity resolution with definitions in matmul.jl -*(v::AdjointAbsVec, A::AbstractTriangular) = adjoint(adjoint(A) * v.parent) -*(v::TransposeAbsVec, A::AbstractTriangular) = transpose(transpose(A) * v.parent) ## Some Triangular-Triangular cases. We might want to write tailored methods ## for these cases, but I'm not sure it is worth it. From 69aee20edc67434c664f6be2529d86ff87b2f6fb Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 30 Dec 2023 22:04:50 +0100 Subject: [PATCH 6/6] return temporarily `_init*`-stuff --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index a435b909aba91..a80c8d0600a78 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -533,6 +533,26 @@ matprod_dest(A::Diagonal, B::Diagonal, TS) = similar(B, TS) matprod_dest(A::HermOrSym, B::Diagonal, TS) = similar(A, TS, size(A)) matprod_dest(A::Diagonal, B::HermOrSym, TS) = similar(B, TS, size(B)) +# TODO: remove once not used anymore in SparseArrays.jl +# some trait like this would be cool +# onedefined(::Type{T}) where {T} = hasmethod(one, (T,)) +# but we are actually asking for oneunit(T), that is, however, defined for generic T as +# `T(one(T))`, so the question is equivalent for whether one(T) is defined +onedefined(::Type) = false +onedefined(::Type{<:Number}) = true + +# initialize return array for op(A, B) +_init_eltype(::typeof(*), ::Type{TA}, ::Type{TB}) where {TA,TB} = + (onedefined(TA) && onedefined(TB)) ? + typeof(matprod(oneunit(TA), oneunit(TB))) : + promote_op(matprod, TA, TB) +_init_eltype(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = + (onedefined(TA) && onedefined(TB)) ? + typeof(op(oneunit(TA), oneunit(TB))) : + promote_op(op, TA, TB) +_initarray(op, ::Type{TA}, ::Type{TB}, C) where {TA,TB} = + similar(C, _init_eltype(op, TA, TB), size(C)) + # General fallback definition for handling under- and overdetermined system as well as square problems # While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs # which is required by LAPACK but not SuiteSparse which allows real-complex solves in some cases. Hence,