From 93732e94161272d332541c1a89681151a62b41e9 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 11 Sep 2025 20:33:47 +0530 Subject: [PATCH 1/7] Add `sym_uplo_unsafe` to skip validation --- src/LinearAlgebra.jl | 18 +++++++++++ src/bidiag.jl | 2 +- src/bunchkaufman.jl | 4 ++- src/special.jl | 12 ++++---- src/symmetric.jl | 72 +++++++++++++++++++++---------------------- src/symmetriceigen.jl | 12 +++++--- 6 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/LinearAlgebra.jl b/src/LinearAlgebra.jl index d97c308b..e9b90d23 100644 --- a/src/LinearAlgebra.jl +++ b/src/LinearAlgebra.jl @@ -363,6 +363,11 @@ function char_uplo(uplo::Symbol) end end +""" + sym_uplo(uplo::Char) + +Return the `Symbol` corresponding to the `uplo` `Char` by checking for validity. +""" function sym_uplo(uplo::Char) if uplo == 'U' return :U @@ -372,6 +377,19 @@ function sym_uplo(uplo::Char) throw_uplo() end end +""" + sym_uplo_unsafe(uplo::Char) + +Return the `Symbol` corresponding to the `uplo` `Char` without checking for validity. +See also `sym_uplo`, which checks for validity. +""" +function sym_uplo_unsafe(uplo::Char) + if uplo == 'U' + return :U + else + return :L + end +end @noinline throw_uplo() = throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)")) diff --git a/src/bidiag.jl b/src/bidiag.jl index cc5e6de7..c7de41b7 100644 --- a/src/bidiag.jl +++ b/src/bidiag.jl @@ -295,7 +295,7 @@ function show(io::IO, M::Bidiagonal) print(io, ", ") show(io, M.ev) print(io, ", ") - show(io, sym_uplo(M.uplo)) + show(io, sym_uplo_unsafe(M.uplo)) print(io, ")") end diff --git a/src/bunchkaufman.jl b/src/bunchkaufman.jl index 7efe57eb..440f723c 100644 --- a/src/bunchkaufman.jl +++ b/src/bunchkaufman.jl @@ -130,7 +130,9 @@ function bunchkaufman!(A::StridedMatrix{<:BlasFloat}, rook::Bool = false; check: end bkcopy_oftype(A, S) = eigencopy_oftype(A, S) -bkcopy_oftype(A::Symmetric{<:Complex}, S) = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) +function bkcopy_oftype(A::Symmetric{<:Complex}, S) + Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) +end """ bunchkaufman(A, rook::Bool=false; check = true) -> S::BunchKaufman diff --git a/src/special.jl b/src/special.jl index 83880ca5..4bbe885b 100644 --- a/src/special.jl +++ b/src/special.jl @@ -292,19 +292,19 @@ end for f in (:+, :-) @eval function $f(D::Diagonal{<:Number}, S::Symmetric) - uplo = sym_uplo(S.uplo) + uplo = sym_uplo_unsafe(S.uplo) return Symmetric(parentof_applytri($f, Symmetric(D, uplo), S), uplo) end @eval function $f(S::Symmetric, D::Diagonal{<:Number}) - uplo = sym_uplo(S.uplo) + uplo = sym_uplo_unsafe(S.uplo) return Symmetric(parentof_applytri($f, S, Symmetric(D, uplo)), uplo) end @eval function $f(D::Diagonal{<:Real}, H::Hermitian) - uplo = sym_uplo(H.uplo) + uplo = sym_uplo_unsafe(H.uplo) return Hermitian(parentof_applytri($f, Hermitian(D, uplo), H), uplo) end @eval function $f(H::Hermitian, D::Diagonal{<:Real}) - uplo = sym_uplo(H.uplo) + uplo = sym_uplo_unsafe(H.uplo) return Hermitian(parentof_applytri($f, H, Hermitian(D, uplo)), uplo) end end @@ -608,8 +608,8 @@ end # tridiagonal cholesky factorization function cholesky(S::RealSymHermitian{<:BiTriSym}, ::NoPivot = NoPivot(); check::Bool = true) T = choltype(S) - B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo(S.uplo)) - cholesky!(Hermitian(B, sym_uplo(S.uplo)), NoPivot(); check = check) + B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo_unsafe(S.uplo)) + cholesky!(Hermitian(B, sym_uplo_unsafe(S.uplo)), NoPivot(); check = check) end # istriu/istril for triangular wrappers of structured matrices diff --git a/src/symmetric.jl b/src/symmetric.jl index 2cc74763..b448636c 100644 --- a/src/symmetric.jl +++ b/src/symmetric.jl @@ -198,7 +198,7 @@ for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end end - $S(A::$H) = $S(A, sym_uplo(A.uplo)) + $S(A::$H) = $S(A, sym_uplo_unsafe(A.uplo)) function $S(A::$H, uplo::Symbol) if A.uplo == char_uplo(uplo) if $H === Hermitian && !(eltype(A) <: Real) && @@ -206,7 +206,7 @@ for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) throw(ArgumentError("Cannot construct $($S)($($H))); diagonal contains complex values")) end - return $S(A.data, sym_uplo(A.uplo)) + return $S(A.data, sym_uplo_unsafe(A.uplo)) else throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end @@ -251,7 +251,7 @@ end @inline function getindex(A::Symmetric, i::Int, j::Int) @boundscheck checkbounds(A, i, j) @inbounds if i == j - return symmetric(A.data[i, j], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) + return symmetric(A.data[i, j], sym_uplo_unsafe(A.uplo))::symmetric_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else @@ -261,7 +261,7 @@ end @inline function getindex(A::Hermitian, i::Int, j::Int) @boundscheck checkbounds(A, i, j) @inbounds if i == j - return hermitian(A.data[i, j], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) + return hermitian(A.data[i, j], sym_uplo_unsafe(A.uplo))::hermitian_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else @@ -294,14 +294,14 @@ Base._reverse(A::Hermitian, ::Colon) = Hermitian(reverse(A.data), A.uplo == 'U' end Base.dataids(A::HermOrSym) = Base.dataids(parent(A)) -Base.unaliascopy(A::Hermitian) = Hermitian(Base.unaliascopy(parent(A)), sym_uplo(A.uplo)) -Base.unaliascopy(A::Symmetric) = Symmetric(Base.unaliascopy(parent(A)), sym_uplo(A.uplo)) +Base.unaliascopy(A::Hermitian) = Hermitian(Base.unaliascopy(parent(A)), sym_uplo_unsafe(A.uplo)) +Base.unaliascopy(A::Symmetric) = Symmetric(Base.unaliascopy(parent(A)), sym_uplo_unsafe(A.uplo)) _conjugation(::Union{Symmetric, Hermitian{<:Real}}) = transpose _conjugation(::Hermitian) = adjoint -diag(A::Symmetric) = symmetric.(diag(parent(A)), sym_uplo(A.uplo)) -diag(A::Hermitian) = hermitian.(diag(parent(A)), sym_uplo(A.uplo)) +diag(A::Symmetric) = symmetric.(diag(parent(A)), sym_uplo_unsafe(A.uplo)) +diag(A::Hermitian) = hermitian.(diag(parent(A)), sym_uplo_unsafe(A.uplo)) function applytri(f, A::HermOrSym) if A.uplo == 'U' @@ -345,15 +345,15 @@ similar(A::Union{Symmetric,Hermitian}, ::Type{T}, dims::Dims{N}) where {T,N} = s parent(A::HermOrSym) = A.data Symmetric{T,S}(A::Symmetric{T,S}) where {T,S<:AbstractMatrix{T}} = A Symmetric{T,S}(A::Symmetric) where {T,S<:AbstractMatrix{T}} = Symmetric{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) +AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), sym_uplo_unsafe(A.uplo)) AbstractMatrix{T}(A::Symmetric{T}) where {T} = copy(A) Hermitian{T,S}(A::Hermitian{T,S}) where {T,S<:AbstractMatrix{T}} = A Hermitian{T,S}(A::Hermitian) where {T,S<:AbstractMatrix{T}} = Hermitian{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) +AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), sym_uplo_unsafe(A.uplo)) AbstractMatrix{T}(A::Hermitian{T}) where {T} = copy(A) -copy(A::Symmetric) = (Symmetric(parentof_applytri(copy, A), sym_uplo(A.uplo))) -copy(A::Hermitian) = (Hermitian(parentof_applytri(copy, A), sym_uplo(A.uplo))) +copy(A::Symmetric) = (Symmetric(parentof_applytri(copy, A), sym_uplo_unsafe(A.uplo))) +copy(A::Hermitian) = (Hermitian(parentof_applytri(copy, A), sym_uplo_unsafe(A.uplo))) function copyto!(dest::Symmetric, src::Symmetric) if axes(dest) != axes(src) @@ -394,13 +394,13 @@ end end @inline function _symmetrize_diagonal!(B, A::Symmetric) for i = 1:size(A, 1) - B[i,i] = symmetric(A[i,i], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) + B[i,i] = symmetric(A[i,i], sym_uplo_unsafe(A.uplo))::symmetric_type(eltype(A.data)) end return B end @inline function _symmetrize_diagonal!(B, A::Hermitian) for i = 1:size(A, 1) - B[i,i] = hermitian(A[i,i], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) + B[i,i] = hermitian(A[i,i], sym_uplo_unsafe(A.uplo))::hermitian_type(eltype(A.data)) end return B end @@ -472,9 +472,9 @@ transpose(A::Hermitian{<:Real}) = A real(A::Symmetric{<:Real}) = A real(A::Hermitian{<:Real}) = A -real(A::Symmetric) = Symmetric(parentof_applytri(real, A), sym_uplo(A.uplo)) -real(A::Hermitian) = Hermitian(parentof_applytri(real, A), sym_uplo(A.uplo)) -imag(A::Symmetric) = Symmetric(parentof_applytri(imag, A), sym_uplo(A.uplo)) +real(A::Symmetric) = Symmetric(parentof_applytri(real, A), sym_uplo_unsafe(A.uplo)) +real(A::Hermitian) = Hermitian(parentof_applytri(real, A), sym_uplo_unsafe(A.uplo)) +imag(A::Symmetric) = Symmetric(parentof_applytri(imag, A), sym_uplo_unsafe(A.uplo)) Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Symmetric(copy(adjoint(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) @@ -484,8 +484,8 @@ Base.copy(A::Transpose{<:Any,<:Hermitian}) = tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) tr(A::Hermitian{<:Number}) = real(tr(A.data)) -Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo(A.uplo)) -Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo(A.uplo)) +Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo_unsafe(A.uplo)) +Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo_unsafe(A.uplo)) Base.conj!(A::HermOrSym) = typeof(A)(parentof_applytri(conj!, A), A.uplo) # tril/triu @@ -702,25 +702,25 @@ function _hermkron!(C, A, B, conj, real, Auplo, Buplo) end end -(-)(A::Symmetric) = Symmetric(parentof_applytri(-, A), sym_uplo(A.uplo)) -(-)(A::Hermitian) = Hermitian(parentof_applytri(-, A), sym_uplo(A.uplo)) +(-)(A::Symmetric) = Symmetric(parentof_applytri(-, A), sym_uplo_unsafe(A.uplo)) +(-)(A::Hermitian) = Hermitian(parentof_applytri(-, A), sym_uplo_unsafe(A.uplo)) ## Addition/subtraction for f ∈ (:+, :-), Wrapper ∈ (:Hermitian, :Symmetric) @eval function $f(A::$Wrapper, B::$Wrapper) - uplo = A.uplo == B.uplo ? sym_uplo(A.uplo) : (:U) + uplo = A.uplo == B.uplo ? sym_uplo_unsafe(A.uplo) : (:U) $Wrapper(parentof_applytri($f, A, B), uplo) end end for f in (:+, :-) @eval begin - $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo(B.uplo))) - $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo(A.uplo)), B) - $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, sym_uplo(B.uplo)), B) - $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, sym_uplo(A.uplo))) - $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, sym_uplo(B.uplo)), B) - $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, sym_uplo(A.uplo))) + $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo_unsafe(B.uplo))) + $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo_unsafe(A.uplo)), B) + $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, sym_uplo_unsafe(B.uplo)), B) + $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, sym_uplo_unsafe(A.uplo))) + $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, sym_uplo_unsafe(B.uplo)), B) + $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, sym_uplo_unsafe(A.uplo))) end end @@ -770,12 +770,12 @@ function dot(x::AbstractVector, A::HermOrSym, y::AbstractVector) end # Scaling with Number -*(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y * x, A), sym_uplo(A.uplo)) -*(x::Number, A::Symmetric) = Symmetric(parentof_applytri(y -> x * y, A), sym_uplo(A.uplo)) -*(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y * x, A), sym_uplo(A.uplo)) -*(x::Real, A::Hermitian) = Hermitian(parentof_applytri(y -> x * y, A), sym_uplo(A.uplo)) -/(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y/x, A), sym_uplo(A.uplo)) -/(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y/x, A), sym_uplo(A.uplo)) +*(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y * x, A), sym_uplo_unsafe(A.uplo)) +*(x::Number, A::Symmetric) = Symmetric(parentof_applytri(y -> x * y, A), sym_uplo_unsafe(A.uplo)) +*(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y * x, A), sym_uplo_unsafe(A.uplo)) +*(x::Real, A::Hermitian) = Hermitian(parentof_applytri(y -> x * y, A), sym_uplo_unsafe(A.uplo)) +/(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y/x, A), sym_uplo_unsafe(A.uplo)) +/(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y/x, A), sym_uplo_unsafe(A.uplo)) factorize(A::HermOrSym) = _factorize(A) function _factorize(A::HermOrSym{T}; check::Bool=true) where T @@ -821,8 +821,8 @@ function _inv(A::HermOrSym) B end # StridedMatrix restriction seems necessary due to inv! call in _inv above -inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), sym_uplo(A.uplo)) -inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), sym_uplo(A.uplo)) +inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), sym_uplo_unsafe(A.uplo)) +inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), sym_uplo_unsafe(A.uplo)) function svd(A::RealHermSymComplexHerm; full::Bool=false) vals, vecs = eigen(A) diff --git a/src/symmetriceigen.jl b/src/symmetriceigen.jl index f6640bc1..2edb6a51 100644 --- a/src/symmetriceigen.jl +++ b/src/symmetriceigen.jl @@ -2,8 +2,12 @@ # preserve HermOrSym wrapper # Call `copytrito!` instead of `copy_similar` to only copy the matching triangular half -eigencopy_oftype(A::Hermitian, ::Type{S}) where S = Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) -eigencopy_oftype(A::Symmetric, ::Type{S}) where S = Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo(A.uplo)) +function eigencopy_oftype(A::Hermitian, ::Type{S}) where S + Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) +end +function eigencopy_oftype(A::Symmetric, ::Type{S}) where S + Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) +end eigencopy_oftype(A::Symmetric{<:Complex}, ::Type{S}) where S = copyto!(similar(parent(A), S), A) """ @@ -314,8 +318,8 @@ end # Perform U' \ A / U in-place, where U::Union{UpperTriangular,Diagonal} UtiAUi!(A, U) = _UtiAUi!(A, U) -UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), sym_uplo(A.uplo)) -UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), sym_uplo(A.uplo)) +UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), sym_uplo_unsafe(A.uplo)) +UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), sym_uplo_unsafe(A.uplo)) _UtiAUi!(A, U) = rdiv!(ldiv!(U', A), U) function eigvals(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} From 91f5fd74a2eac19bcadc689c45844b3d693ac3d4 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 11 Sep 2025 20:40:10 +0530 Subject: [PATCH 2/7] Update docstrings --- src/LinearAlgebra.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LinearAlgebra.jl b/src/LinearAlgebra.jl index e9b90d23..a0d163a4 100644 --- a/src/LinearAlgebra.jl +++ b/src/LinearAlgebra.jl @@ -366,7 +366,7 @@ end """ sym_uplo(uplo::Char) -Return the `Symbol` corresponding to the `uplo` `Char` by checking for validity. +Return the `Symbol` corresponding the `uplo` by checking for validity. """ function sym_uplo(uplo::Char) if uplo == 'U' @@ -380,7 +380,7 @@ end """ sym_uplo_unsafe(uplo::Char) -Return the `Symbol` corresponding to the `uplo` `Char` without checking for validity. +Return the `Symbol` corresponding to `uplo` without checking for validity. See also `sym_uplo`, which checks for validity. """ function sym_uplo_unsafe(uplo::Char) From 7b44cdd5ca3e79f0b3af439ff73c769f3407379a Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 15 Sep 2025 16:40:26 +0530 Subject: [PATCH 3/7] Make the method a one-liner --- src/LinearAlgebra.jl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/LinearAlgebra.jl b/src/LinearAlgebra.jl index a0d163a4..a3b06624 100644 --- a/src/LinearAlgebra.jl +++ b/src/LinearAlgebra.jl @@ -383,13 +383,7 @@ end Return the `Symbol` corresponding to `uplo` without checking for validity. See also `sym_uplo`, which checks for validity. """ -function sym_uplo_unsafe(uplo::Char) - if uplo == 'U' - return :U - else - return :L - end -end +sym_uplo_unsafe(uplo::Char) = uplo == 'U' ? (:U) : (:L) @noinline throw_uplo() = throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)")) From 34c26b78b99a0dc49ea31ae70ec6c35b3c5c8cf7 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 15 Sep 2025 19:04:49 +0530 Subject: [PATCH 4/7] Tests for `sym_uplo` --- test/symmetric.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/symmetric.jl b/test/symmetric.jl index 707b392d..c6ab1144 100644 --- a/test/symmetric.jl +++ b/test/symmetric.jl @@ -1343,4 +1343,10 @@ end @test_throws msg LinearAlgebra.fillband!(Symmetric(A), 2, 0, 1) end +@testset "sym_uplo" begin + @test LinearAlgebra.sym_uplo('U') == :U + @test LinearAlgebra.sym_uplo('L') == :L + @test_throws ArgumentError LinearAlgebra.sym_uplo('N') +end + end # module TestSymmetric From 14ce3f18f0430eeaa0904ead602ad33b76ee9c3d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 15 Sep 2025 19:06:14 +0530 Subject: [PATCH 5/7] Add a comment --- src/LinearAlgebra.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LinearAlgebra.jl b/src/LinearAlgebra.jl index a3b06624..096926de 100644 --- a/src/LinearAlgebra.jl +++ b/src/LinearAlgebra.jl @@ -369,6 +369,8 @@ end Return the `Symbol` corresponding the `uplo` by checking for validity. """ function sym_uplo(uplo::Char) + # This method is called by other packages, and isn't used within LinearAlgebra + # It's retained here for backward compatibility. if uplo == 'U' return :U elseif uplo == 'L' From ef5ab42b3519db4b4466c5fb2ad20ead6ddd9b39 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 17 Sep 2025 19:40:59 +0530 Subject: [PATCH 6/7] Rename to `_sym_uplo` --- src/LinearAlgebra.jl | 4 ++-- src/bidiag.jl | 2 +- src/bunchkaufman.jl | 2 +- src/special.jl | 12 ++++++------ src/symmetriceigen.jl | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/LinearAlgebra.jl b/src/LinearAlgebra.jl index 096926de..4d4eb029 100644 --- a/src/LinearAlgebra.jl +++ b/src/LinearAlgebra.jl @@ -380,12 +380,12 @@ function sym_uplo(uplo::Char) end end """ - sym_uplo_unsafe(uplo::Char) + _sym_uplo(uplo::Char) Return the `Symbol` corresponding to `uplo` without checking for validity. See also `sym_uplo`, which checks for validity. """ -sym_uplo_unsafe(uplo::Char) = uplo == 'U' ? (:U) : (:L) +_sym_uplo(uplo::Char) = uplo == 'U' ? (:U) : (:L) @noinline throw_uplo() = throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)")) diff --git a/src/bidiag.jl b/src/bidiag.jl index c7de41b7..f74f759a 100644 --- a/src/bidiag.jl +++ b/src/bidiag.jl @@ -295,7 +295,7 @@ function show(io::IO, M::Bidiagonal) print(io, ", ") show(io, M.ev) print(io, ", ") - show(io, sym_uplo_unsafe(M.uplo)) + show(io, _sym_uplo(M.uplo)) print(io, ")") end diff --git a/src/bunchkaufman.jl b/src/bunchkaufman.jl index 440f723c..69811fde 100644 --- a/src/bunchkaufman.jl +++ b/src/bunchkaufman.jl @@ -131,7 +131,7 @@ end bkcopy_oftype(A, S) = eigencopy_oftype(A, S) function bkcopy_oftype(A::Symmetric{<:Complex}, S) - Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) + Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), _sym_uplo(A.uplo)) end """ diff --git a/src/special.jl b/src/special.jl index 4bbe885b..9dff10a4 100644 --- a/src/special.jl +++ b/src/special.jl @@ -292,19 +292,19 @@ end for f in (:+, :-) @eval function $f(D::Diagonal{<:Number}, S::Symmetric) - uplo = sym_uplo_unsafe(S.uplo) + uplo = _sym_uplo(S.uplo) return Symmetric(parentof_applytri($f, Symmetric(D, uplo), S), uplo) end @eval function $f(S::Symmetric, D::Diagonal{<:Number}) - uplo = sym_uplo_unsafe(S.uplo) + uplo = _sym_uplo(S.uplo) return Symmetric(parentof_applytri($f, S, Symmetric(D, uplo)), uplo) end @eval function $f(D::Diagonal{<:Real}, H::Hermitian) - uplo = sym_uplo_unsafe(H.uplo) + uplo = _sym_uplo(H.uplo) return Hermitian(parentof_applytri($f, Hermitian(D, uplo), H), uplo) end @eval function $f(H::Hermitian, D::Diagonal{<:Real}) - uplo = sym_uplo_unsafe(H.uplo) + uplo = _sym_uplo(H.uplo) return Hermitian(parentof_applytri($f, H, Hermitian(D, uplo)), uplo) end end @@ -608,8 +608,8 @@ end # tridiagonal cholesky factorization function cholesky(S::RealSymHermitian{<:BiTriSym}, ::NoPivot = NoPivot(); check::Bool = true) T = choltype(S) - B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo_unsafe(S.uplo)) - cholesky!(Hermitian(B, sym_uplo_unsafe(S.uplo)), NoPivot(); check = check) + B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), _sym_uplo(S.uplo)) + cholesky!(Hermitian(B, _sym_uplo(S.uplo)), NoPivot(); check = check) end # istriu/istril for triangular wrappers of structured matrices diff --git a/src/symmetriceigen.jl b/src/symmetriceigen.jl index 2edb6a51..a8a54037 100644 --- a/src/symmetriceigen.jl +++ b/src/symmetriceigen.jl @@ -3,10 +3,10 @@ # preserve HermOrSym wrapper # Call `copytrito!` instead of `copy_similar` to only copy the matching triangular half function eigencopy_oftype(A::Hermitian, ::Type{S}) where S - Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) + Hermitian(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), _sym_uplo(A.uplo)) end function eigencopy_oftype(A::Symmetric, ::Type{S}) where S - Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), sym_uplo_unsafe(A.uplo)) + Symmetric(copytrito!(similar(parent(A), S, size(A)), A.data, A.uplo), _sym_uplo(A.uplo)) end eigencopy_oftype(A::Symmetric{<:Complex}, ::Type{S}) where S = copyto!(similar(parent(A), S), A) @@ -318,8 +318,8 @@ end # Perform U' \ A / U in-place, where U::Union{UpperTriangular,Diagonal} UtiAUi!(A, U) = _UtiAUi!(A, U) -UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), sym_uplo_unsafe(A.uplo)) -UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), sym_uplo_unsafe(A.uplo)) +UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), _sym_uplo(A.uplo)) +UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), _sym_uplo(A.uplo)) _UtiAUi!(A, U) = rdiv!(ldiv!(U', A), U) function eigvals(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} From eb5a514836466b8371dea2bba3215ce094f7a8df Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 17 Sep 2025 19:42:38 +0530 Subject: [PATCH 7/7] Replace in symmetric.jl --- src/symmetric.jl | 72 ++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/symmetric.jl b/src/symmetric.jl index b448636c..f7885a6b 100644 --- a/src/symmetric.jl +++ b/src/symmetric.jl @@ -198,7 +198,7 @@ for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end end - $S(A::$H) = $S(A, sym_uplo_unsafe(A.uplo)) + $S(A::$H) = $S(A, _sym_uplo(A.uplo)) function $S(A::$H, uplo::Symbol) if A.uplo == char_uplo(uplo) if $H === Hermitian && !(eltype(A) <: Real) && @@ -206,7 +206,7 @@ for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) throw(ArgumentError("Cannot construct $($S)($($H))); diagonal contains complex values")) end - return $S(A.data, sym_uplo_unsafe(A.uplo)) + return $S(A.data, _sym_uplo(A.uplo)) else throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end @@ -251,7 +251,7 @@ end @inline function getindex(A::Symmetric, i::Int, j::Int) @boundscheck checkbounds(A, i, j) @inbounds if i == j - return symmetric(A.data[i, j], sym_uplo_unsafe(A.uplo))::symmetric_type(eltype(A.data)) + return symmetric(A.data[i, j], _sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else @@ -261,7 +261,7 @@ end @inline function getindex(A::Hermitian, i::Int, j::Int) @boundscheck checkbounds(A, i, j) @inbounds if i == j - return hermitian(A.data[i, j], sym_uplo_unsafe(A.uplo))::hermitian_type(eltype(A.data)) + return hermitian(A.data[i, j], _sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else @@ -294,14 +294,14 @@ Base._reverse(A::Hermitian, ::Colon) = Hermitian(reverse(A.data), A.uplo == 'U' end Base.dataids(A::HermOrSym) = Base.dataids(parent(A)) -Base.unaliascopy(A::Hermitian) = Hermitian(Base.unaliascopy(parent(A)), sym_uplo_unsafe(A.uplo)) -Base.unaliascopy(A::Symmetric) = Symmetric(Base.unaliascopy(parent(A)), sym_uplo_unsafe(A.uplo)) +Base.unaliascopy(A::Hermitian) = Hermitian(Base.unaliascopy(parent(A)), _sym_uplo(A.uplo)) +Base.unaliascopy(A::Symmetric) = Symmetric(Base.unaliascopy(parent(A)), _sym_uplo(A.uplo)) _conjugation(::Union{Symmetric, Hermitian{<:Real}}) = transpose _conjugation(::Hermitian) = adjoint -diag(A::Symmetric) = symmetric.(diag(parent(A)), sym_uplo_unsafe(A.uplo)) -diag(A::Hermitian) = hermitian.(diag(parent(A)), sym_uplo_unsafe(A.uplo)) +diag(A::Symmetric) = symmetric.(diag(parent(A)), _sym_uplo(A.uplo)) +diag(A::Hermitian) = hermitian.(diag(parent(A)), _sym_uplo(A.uplo)) function applytri(f, A::HermOrSym) if A.uplo == 'U' @@ -345,15 +345,15 @@ similar(A::Union{Symmetric,Hermitian}, ::Type{T}, dims::Dims{N}) where {T,N} = s parent(A::HermOrSym) = A.data Symmetric{T,S}(A::Symmetric{T,S}) where {T,S<:AbstractMatrix{T}} = A Symmetric{T,S}(A::Symmetric) where {T,S<:AbstractMatrix{T}} = Symmetric{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), sym_uplo_unsafe(A.uplo)) +AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), _sym_uplo(A.uplo)) AbstractMatrix{T}(A::Symmetric{T}) where {T} = copy(A) Hermitian{T,S}(A::Hermitian{T,S}) where {T,S<:AbstractMatrix{T}} = A Hermitian{T,S}(A::Hermitian) where {T,S<:AbstractMatrix{T}} = Hermitian{T,S}(convert(S,A.data),A.uplo) -AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), sym_uplo_unsafe(A.uplo)) +AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), _sym_uplo(A.uplo)) AbstractMatrix{T}(A::Hermitian{T}) where {T} = copy(A) -copy(A::Symmetric) = (Symmetric(parentof_applytri(copy, A), sym_uplo_unsafe(A.uplo))) -copy(A::Hermitian) = (Hermitian(parentof_applytri(copy, A), sym_uplo_unsafe(A.uplo))) +copy(A::Symmetric) = (Symmetric(parentof_applytri(copy, A), _sym_uplo(A.uplo))) +copy(A::Hermitian) = (Hermitian(parentof_applytri(copy, A), _sym_uplo(A.uplo))) function copyto!(dest::Symmetric, src::Symmetric) if axes(dest) != axes(src) @@ -394,13 +394,13 @@ end end @inline function _symmetrize_diagonal!(B, A::Symmetric) for i = 1:size(A, 1) - B[i,i] = symmetric(A[i,i], sym_uplo_unsafe(A.uplo))::symmetric_type(eltype(A.data)) + B[i,i] = symmetric(A[i,i], _sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) end return B end @inline function _symmetrize_diagonal!(B, A::Hermitian) for i = 1:size(A, 1) - B[i,i] = hermitian(A[i,i], sym_uplo_unsafe(A.uplo))::hermitian_type(eltype(A.data)) + B[i,i] = hermitian(A[i,i], _sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) end return B end @@ -472,9 +472,9 @@ transpose(A::Hermitian{<:Real}) = A real(A::Symmetric{<:Real}) = A real(A::Hermitian{<:Real}) = A -real(A::Symmetric) = Symmetric(parentof_applytri(real, A), sym_uplo_unsafe(A.uplo)) -real(A::Hermitian) = Hermitian(parentof_applytri(real, A), sym_uplo_unsafe(A.uplo)) -imag(A::Symmetric) = Symmetric(parentof_applytri(imag, A), sym_uplo_unsafe(A.uplo)) +real(A::Symmetric) = Symmetric(parentof_applytri(real, A), _sym_uplo(A.uplo)) +real(A::Hermitian) = Hermitian(parentof_applytri(real, A), _sym_uplo(A.uplo)) +imag(A::Symmetric) = Symmetric(parentof_applytri(imag, A), _sym_uplo(A.uplo)) Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Symmetric(copy(adjoint(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) @@ -484,8 +484,8 @@ Base.copy(A::Transpose{<:Any,<:Hermitian}) = tr(A::Symmetric{<:Number}) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) tr(A::Hermitian{<:Number}) = real(tr(A.data)) -Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), sym_uplo_unsafe(A.uplo)) -Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), sym_uplo_unsafe(A.uplo)) +Base.conj(A::Symmetric) = Symmetric(parentof_applytri(conj, A), _sym_uplo(A.uplo)) +Base.conj(A::Hermitian) = Hermitian(parentof_applytri(conj, A), _sym_uplo(A.uplo)) Base.conj!(A::HermOrSym) = typeof(A)(parentof_applytri(conj!, A), A.uplo) # tril/triu @@ -702,25 +702,25 @@ function _hermkron!(C, A, B, conj, real, Auplo, Buplo) end end -(-)(A::Symmetric) = Symmetric(parentof_applytri(-, A), sym_uplo_unsafe(A.uplo)) -(-)(A::Hermitian) = Hermitian(parentof_applytri(-, A), sym_uplo_unsafe(A.uplo)) +(-)(A::Symmetric) = Symmetric(parentof_applytri(-, A), _sym_uplo(A.uplo)) +(-)(A::Hermitian) = Hermitian(parentof_applytri(-, A), _sym_uplo(A.uplo)) ## Addition/subtraction for f ∈ (:+, :-), Wrapper ∈ (:Hermitian, :Symmetric) @eval function $f(A::$Wrapper, B::$Wrapper) - uplo = A.uplo == B.uplo ? sym_uplo_unsafe(A.uplo) : (:U) + uplo = A.uplo == B.uplo ? _sym_uplo(A.uplo) : (:U) $Wrapper(parentof_applytri($f, A, B), uplo) end end for f in (:+, :-) @eval begin - $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo_unsafe(B.uplo))) - $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo_unsafe(A.uplo)), B) - $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, sym_uplo_unsafe(B.uplo)), B) - $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, sym_uplo_unsafe(A.uplo))) - $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, sym_uplo_unsafe(B.uplo)), B) - $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, sym_uplo_unsafe(A.uplo))) + $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), _sym_uplo(B.uplo))) + $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), _sym_uplo(A.uplo)), B) + $f(A::SymTridiagonal, B::Symmetric) = $f(Symmetric(A, _sym_uplo(B.uplo)), B) + $f(A::Symmetric, B::SymTridiagonal) = $f(A, Symmetric(B, _sym_uplo(A.uplo))) + $f(A::SymTridiagonal{<:Real}, B::Hermitian) = $f(Hermitian(A, _sym_uplo(B.uplo)), B) + $f(A::Hermitian, B::SymTridiagonal{<:Real}) = $f(A, Hermitian(B, _sym_uplo(A.uplo))) end end @@ -770,12 +770,12 @@ function dot(x::AbstractVector, A::HermOrSym, y::AbstractVector) end # Scaling with Number -*(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y * x, A), sym_uplo_unsafe(A.uplo)) -*(x::Number, A::Symmetric) = Symmetric(parentof_applytri(y -> x * y, A), sym_uplo_unsafe(A.uplo)) -*(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y * x, A), sym_uplo_unsafe(A.uplo)) -*(x::Real, A::Hermitian) = Hermitian(parentof_applytri(y -> x * y, A), sym_uplo_unsafe(A.uplo)) -/(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y/x, A), sym_uplo_unsafe(A.uplo)) -/(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y/x, A), sym_uplo_unsafe(A.uplo)) +*(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y * x, A), _sym_uplo(A.uplo)) +*(x::Number, A::Symmetric) = Symmetric(parentof_applytri(y -> x * y, A), _sym_uplo(A.uplo)) +*(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y * x, A), _sym_uplo(A.uplo)) +*(x::Real, A::Hermitian) = Hermitian(parentof_applytri(y -> x * y, A), _sym_uplo(A.uplo)) +/(A::Symmetric, x::Number) = Symmetric(parentof_applytri(y -> y/x, A), _sym_uplo(A.uplo)) +/(A::Hermitian, x::Real) = Hermitian(parentof_applytri(y -> y/x, A), _sym_uplo(A.uplo)) factorize(A::HermOrSym) = _factorize(A) function _factorize(A::HermOrSym{T}; check::Bool=true) where T @@ -821,8 +821,8 @@ function _inv(A::HermOrSym) B end # StridedMatrix restriction seems necessary due to inv! call in _inv above -inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), sym_uplo_unsafe(A.uplo)) -inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), sym_uplo_unsafe(A.uplo)) +inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), _sym_uplo(A.uplo)) +inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), _sym_uplo(A.uplo)) function svd(A::RealHermSymComplexHerm; full::Bool=false) vals, vecs = eigen(A)