diff --git a/src/bidiag.jl b/src/bidiag.jl index ca95e386..a28bf4c8 100644 --- a/src/bidiag.jl +++ b/src/bidiag.jl @@ -1209,7 +1209,7 @@ function dot(x::AbstractVector, B::Bidiagonal, y::AbstractVector) nx, ny = length(x), length(y) (nx == size(B, 1) == ny) || throw(DimensionMismatch()) if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(B)), zero(eltype(y))) + nx == 0 && return zero(dot(zero(eltype(x)), zero(eltype(B)), zero(eltype(y)))) return dot(x[1], B.dv[1], y[1]) end ev, dv = B.ev, B.dv diff --git a/src/blas.jl b/src/blas.jl index 04e590c1..b5fd5287 100644 --- a/src/blas.jl +++ b/src/blas.jl @@ -84,8 +84,7 @@ export trsm!, trsm -using ..LinearAlgebra: libblastrampoline, BlasReal, BlasComplex, BlasFloat, BlasInt, - DimensionMismatch, checksquare, chkstride1, SingularException +using ..LinearAlgebra: libblastrampoline, BlasReal, BlasComplex, BlasFloat, BlasInt, DimensionMismatch, checksquare, chkstride1 include("lbt.jl") @@ -1370,11 +1369,6 @@ for (fname, elty) in ((:dtrsv_,:Float64), throw(DimensionMismatch(lazy"size of A is $n != length(x) = $(length(x))")) end chkstride1(A) - if diag == 'N' - for i in 1:n - iszero(A[i,i]) && throw(SingularException(i)) - end - end px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, @@ -2098,7 +2092,7 @@ end her2k!(uplo, trans, alpha, A, B, beta, C) Rank-2k update of the Hermitian matrix `C` as -`alpha*A*B' + alpha*B*A' + beta*C` or `alpha*A'*B + alpha*B'*A + beta*C` +`alpha*A*B' + alpha'*B*A' + beta*C` or `alpha*A'*B + alpha'*B'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). The scalar `beta` has to be real. Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Return `C`. """ @@ -2107,8 +2101,8 @@ function her2k! end """ her2k(uplo, trans, alpha, A, B) -Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*B' + alpha*B*A'` -or `alpha*A'*B + alpha*B'*A`, according to [`trans`](@ref stdlib-blas-trans). +Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*B' + alpha'*B*A'` +or `alpha*A'*B + alpha'*B'*A`, according to [`trans`](@ref stdlib-blas-trans). """ her2k(uplo, trans, alpha, A, B) @@ -2223,11 +2217,6 @@ for (mmname, smname, elty) in end chkstride1(A) chkstride1(B) - if diag == 'N' - for i in 1:k - iszero(A[i,i]) && throw(SingularException(i)) - end - end ccall((@blasfunc($smname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, diff --git a/src/generic.jl b/src/generic.jl index 3b9a4232..f98276fd 100644 --- a/src/generic.jl +++ b/src/generic.jl @@ -986,7 +986,8 @@ function dot(x::AbstractArray, y::AbstractArray) throw(DimensionMismatch(lazy"first array has length $(lx) which does not match the length of the second, $(length(y)).")) end if lx == 0 - return dot(zero(eltype(x)), zero(eltype(y))) + # make sure the returned result equals exactly the zero element + return zero(dot(zero(eltype(x)), zero(eltype(y)))) end s = zero(dot(first(x), first(y))) for (Ix, Iy) in zip(eachindex(x), eachindex(y)) @@ -1027,6 +1028,8 @@ dot(x, A, y) = dot(x, A*y) # generic fallback for cases that are not covered by function dot(x::AbstractVector, A::AbstractMatrix, y::AbstractVector) (axes(x)..., axes(y)...) == axes(A) || throw(DimensionMismatch()) + # outermost zero call to avoid spurious sign ambiguity (like 0.0 - 0.0im) + any(isempty, (x, y)) && return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) T = typeof(dot(first(x), first(A), first(y))) s = zero(T) i₁ = first(eachindex(x)) diff --git a/src/hessenberg.jl b/src/hessenberg.jl index 158e81c8..cd0df936 100644 --- a/src/hessenberg.jl +++ b/src/hessenberg.jl @@ -360,7 +360,7 @@ function dot(x::AbstractVector, H::UpperHessenberg, y::AbstractVector) m = size(H, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) - return dot(zero(eltype(x)), zero(eltype(H)), zero(eltype(y))) + return zero(dot(zero(eltype(x)), zero(eltype(H)), zero(eltype(y)))) end x₁ = x[1] r = dot(x₁, H[1,1], y[1]) diff --git a/src/matmul.jl b/src/matmul.jl index 2548a239..e45c9efb 100644 --- a/src/matmul.jl +++ b/src/matmul.jl @@ -460,7 +460,7 @@ end throw(DimensionMismatch( LazyString( "incompatible destination size: ", - lazy"the destination $strC of $size_or_len_str_C $C_size_len is incomatible with the product of a $strA of size $sizeA and a $strB of $size_or_len_str_B $B_size_len. ", + lazy"the destination $strC of $size_or_len_str_C $C_size_len is incompatible with the product of a $strA of size $sizeA and a $strB of $size_or_len_str_B $B_size_len. ", lazy"The destination must be of $size_or_len_str_dest $destsize." ) ) diff --git a/src/symmetric.jl b/src/symmetric.jl index 1edb0570..e39f62d9 100644 --- a/src/symmetric.jl +++ b/src/symmetric.jl @@ -520,7 +520,7 @@ for (T, trans, real) in [(:Symmetric, :transpose, :identity), (:(Hermitian{<:Uni if n != size(B, 2) throw(DimensionMismatch(lazy"A has dimensions $(size(A)) but B has dimensions $(size(B))")) end - + iszero(n) && return $real(zero(dot(zero(eltype(A)), zero(eltype(B))))) dotprod = $real(zero(dot(first(A), first(B)))) @inbounds if A.uplo == 'U' && B.uplo == 'U' for j in 1:n @@ -719,6 +719,7 @@ function dot(x::AbstractVector, A::RealHermSymComplexHerm, y::AbstractVector) require_one_based_indexing(x, y) n = length(x) (n == length(y) == size(A, 1)) || throw(DimensionMismatch()) + iszero(n) && return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) data = A.data r = dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) iszero(n) && return r diff --git a/src/triangular.jl b/src/triangular.jl index 35c887f2..8a11f14a 100644 --- a/src/triangular.jl +++ b/src/triangular.jl @@ -849,7 +849,7 @@ function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) + return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) end x₁ = x[1] r = dot(x₁, A[1,1], y[1]) @@ -870,7 +870,7 @@ function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) + return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) end x₁ = first(x) r = dot(x₁, y[1]) @@ -892,7 +892,7 @@ function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) + return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) end r = zero(typeof(dot(first(x), first(A), first(y)))) @inbounds for j in axes(A, 2) @@ -912,7 +912,7 @@ function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) - return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) + return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) end r = zero(typeof(dot(first(x), first(y)))) @inbounds for j in axes(A, 2) @@ -1268,13 +1268,11 @@ function generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, end end # division -generic_trimatdiv!(C::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVector{T}) where {T<:BlasFloat} = - BLAS.trsv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B)) -function generic_trimatdiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} +function generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} if stride(C,1) == stride(A,1) == 1 - BLAS.trsm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B)) + LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B)) else # incompatible with LAPACK - @invoke generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) + @invoke generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) end end function generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} diff --git a/src/tridiag.jl b/src/tridiag.jl index 14f2a6be..40345a45 100644 --- a/src/tridiag.jl +++ b/src/tridiag.jl @@ -246,7 +246,7 @@ function dot(x::AbstractVector, S::SymTridiagonal, y::AbstractVector) nx, ny = length(x), length(y) (nx == size(S, 1) == ny) || throw(DimensionMismatch("dot")) if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(S)), zero(eltype(y))) + nx == 0 && return zero(dot(zero(eltype(x)), zero(eltype(S)), zero(eltype(y)))) return dot(x[1], S.dv[1], y[1]) end dv, ev = S.dv, S.ev @@ -965,7 +965,7 @@ function dot(x::AbstractVector, A::Tridiagonal, y::AbstractVector) nx, ny = length(x), length(y) (nx == size(A, 1) == ny) || throw(DimensionMismatch()) if nx ≤ 1 - nx == 0 && return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) + nx == 0 && return zero(dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y)))) return dot(x[1], A.d[1], y[1]) end @inbounds begin diff --git a/test/generic.jl b/test/generic.jl index 10bdc19c..cd017c5c 100644 --- a/test/generic.jl +++ b/test/generic.jl @@ -733,6 +733,10 @@ end @test dot(x, B', y) ≈ dot(B*x, y) elty <: Real && @test dot(x, transpose(B), y) ≈ dot(x, transpose(B)*y) end + for (m, n) in ((0, 0), (1, 0), (0, 1)) + v = zeros(ComplexF64, m); a = zeros(ComplexF64, m, n); w = zeros(Float64, n) + @test dot(v, a, w) === zero(ComplexF64) + end end @testset "condskeel #34512" begin diff --git a/test/matmul.jl b/test/matmul.jl index 3d5a9c96..5f1f8a05 100644 --- a/test/matmul.jl +++ b/test/matmul.jl @@ -676,23 +676,24 @@ end @test dot(Z, Z) == convert(elty, 34.0) end -dot1(x, y) = invoke(dot, Tuple{Any,Any}, x, y) -dot2(x, y) = invoke(dot, Tuple{AbstractArray,AbstractArray}, x, y) @testset "generic dot" begin + dot1(x, y) = invoke(dot, Tuple{Any,Any}, x, y) + dot2(x, y) = invoke(dot, Tuple{AbstractArray,AbstractArray}, x, y) AA = [1+2im 3+4im; 5+6im 7+8im] BB = [2+7im 4+1im; 3+8im 6+5im] for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) @test dot(A, B) == dot(vec(A), vec(B)) == dot1(A, B) == dot2(A, B) == dot(float.(A), float.(B)) - @test dot(Int[], Int[]) == 0 == dot1(Int[], Int[]) == dot2(Int[], Int[]) - @test_throws MethodError dot(Any[], Any[]) - @test_throws MethodError dot1(Any[], Any[]) - @test_throws MethodError dot2(Any[], Any[]) - for n1 = 0:2, n2 = 0:2, d in (dot, dot1, dot2) - if n1 != n2 - @test_throws DimensionMismatch d(1:n1, 1:n2) - else - @test d(1:n1, 1:n2) ≈ norm(1:n1)^2 - end + end + @test dot(Int[], Int[]) == 0 == dot1(Int[], Int[]) == dot2(Int[], Int[]) + @test dot(ComplexF64[], Float64[]) === dot(ComplexF64[;;], Float64[;;]) === zero(ComplexF64) + @test_throws MethodError dot(Any[], Any[]) + @test_throws MethodError dot1(Any[], Any[]) + @test_throws MethodError dot2(Any[], Any[]) + for n1 = 0:2, n2 = 0:2, d in (dot, dot1, dot2) + if n1 != n2 + @test_throws DimensionMismatch d(1:n1, 1:n2) + else + @test d(1:n1, 1:n2) ≈ norm(1:n1)^2 end end end diff --git a/test/symmetric.jl b/test/symmetric.jl index 17500537..ca83dafc 100644 --- a/test/symmetric.jl +++ b/test/symmetric.jl @@ -470,6 +470,10 @@ end @test dot(symblockmu, symblockml) ≈ dot(msymblockmu, msymblockml) @test dot(symblockml, symblockmu) ≈ dot(msymblockml, msymblockmu) @test dot(symblockml, symblockml) ≈ dot(msymblockml, msymblockml) + + # empty matrices + @test dot(mtype(ComplexF64[;;], :U), mtype(Float64[;;], :U)) === zero(mtype == Hermitian ? Float64 : ComplexF64) + @test dot(mtype(ComplexF64[;;], :L), mtype(Float64[;;], :L)) === zero(mtype == Hermitian ? Float64 : ComplexF64) end end diff --git a/test/testtriag.jl b/test/testtriag.jl index 6ed7a0d6..7542d843 100644 --- a/test/testtriag.jl +++ b/test/testtriag.jl @@ -493,8 +493,6 @@ function test_triangular(elty1_types) @test_throws DimensionMismatch transpose(Ann) \ bm if t1 == UpperTriangular || t1 == LowerTriangular @test_throws SingularException ldiv!(t1(zeros(elty1, n, n)), fill(eltyB(1), n)) - @test_throws SingularException ldiv!(t1(zeros(elty1, n, n)), fill(eltyB(1), n, 2)) - @test_throws SingularException rdiv!(fill(eltyB(1), n, n), t1(zeros(elty1, n, n))) end @test B / A1 ≈ B / M1 @test B / transpose(A1) ≈ B / transpose(M1) diff --git a/test/triangular.jl b/test/triangular.jl index 76e7dcc1..215f577a 100644 --- a/test/triangular.jl +++ b/test/triangular.jl @@ -904,13 +904,8 @@ end end end -@testset "(l/r)mul! and (l/r)div! for non-contiguous arrays" begin +@testset "(l/r)mul! and (l/r)div! for non-contiguous matrices" begin U = UpperTriangular(reshape(collect(3:27.0),5,5)) - b = float.(1:10) - b2 = copy(b); b2v = view(b2, 1:2:9); b2vc = copy(b2v) - @test lmul!(U, b2v) == lmul!(U, b2vc) - b2 = copy(b); b2v = view(b2, 1:2:9); b2vc = copy(b2v) - @test ldiv!(U, b2v) ≈ ldiv!(U, b2vc) B = float.(collect(reshape(1:100, 10,10))) B2 = copy(B); B2v = view(B2, 1:2:9, 1:5); B2vc = copy(B2v) @test lmul!(U, B2v) == lmul!(U, B2vc)