@@ -69,31 +69,32 @@ is_right_isometric(A; kwargs...) = is_left_isometric(A'; kwargs...)
6969Test whether a linear map is Hermitian, i.e. ` A = A' `.
7070The `isapprox_kwargs` can be used to control the tolerances of the equality.
7171"""
72- function ishermitian(A; atol::Real = 0, rtol::Real = 0, norm = LinearAlgebra.norm, kwargs...)
72+ function ishermitian(A; atol::Real = 0, rtol::Real = 0, kwargs...)
7373 if iszero(atol) && iszero(rtol)
7474 return ishermitian_exact(A; kwargs...)
7575 else
76- return 2 * norm(project_antihermitian( A; kwargs...)) ≤ max( atol, rtol * norm(A) )
76+ return ishermitian_approx( A; atol, rtol, kwargs... )
7777 end
7878end
79- function ishermitian_exact(A)
80- return A == A'
81- end
82- function ishermitian_exact(A :: StridedMatrix ; kwargs... )
83- return strided_ishermitian_exact(A, Val( false ) ; kwargs... )
79+
80+ ishermitian_exact(A) = A == A'
81+ ishermitian_exact(A :: StridedMatrix ; kwargs ... ) = strided_ishermitian_exact(A, Val( false ); kwargs ... )
82+ function ishermitian_approx(A; atol, rtol, kwargs... )
83+ return 2 * norm(project_antihermitian(A ; kwargs... )) ≤ max(atol, rtol * norm(A) )
8484end
85+ ishermitian_approx(A:: StridedMatrix ; kwargs... ) = strided_ishermitian_approx(A, Val(false ); kwargs... )
8586
8687"""
8788 isantihermitian(A; isapprox_kwargs...)
8889
8990Test whether a linear map is anti-Hermitian, i.e. `A = -A'`.
9091The `isapprox_kwargs` can be used to control the tolerances of the equality.
9192"""
92- function isantihermitian(A; atol:: Real = 0 , rtol:: Real = 0 , norm = LinearAlgebra . norm, kwargs... )
93+ function isantihermitian(A; atol:: Real = 0 , rtol:: Real = 0 , kwargs... )
9394 if iszero(atol) && iszero(rtol)
9495 return isantihermitian_exact(A; kwargs... )
9596 else
96- return 2 * norm(project_hermitian( A; kwargs ... )) ≤ max( atol, rtol * norm(A) )
97+ return isantihermitian_approx( A; atol, rtol, kwargs ... )
9798 end
9899end
99100function isantihermitian_exact(A)
102103function isantihermitian_exact(A::StridedMatrix; kwargs...)
103104 return strided_ishermitian_exact(A, Val(true); kwargs...)
104105end
106+ function isantihermitian_approx(A; atol, rtol, kwargs...)
107+ return 2 * norm(project_hermitian(A; kwargs...)) ≤ max(atol, rtol * norm(A))
108+ end
109+ isantihermitian_approx(A::StridedMatrix; kwargs...) = strided_ishermitian_approx(A, Val(true); kwargs...)
105110
106111# blocked implementation of exact checks for strided matrices
107112# -----------------------------------------------------------
@@ -139,3 +144,51 @@ function _ishermitian_exact_offdiag(Al, Au, ::Val{anti}) where {anti}
139144 end
140145 return true
141146end
147+
148+
149+ function strided_ishermitian_approx(
150+ A::AbstractMatrix, anti::Val;
151+ blocksize = 32, atol::Real = default_hermitian_tol(A), rtol::Real = 0
152+ )
153+ n = size(A, 1)
154+ ϵ = abs2(zero(eltype(A)))
155+ ϵmax = oftype(ϵ, rtol > 0 ? max(atol, rtol * norm(A)) : atol)^2
156+ for j in 1:blocksize:n
157+ jb = min(blocksize, n - j + 1)
158+ ϵ += _ishermitian_approx_diag(view(A, j:(j + jb - 1), j:(j + jb - 1)), anti)
159+ ϵ < ϵmax || return false
160+ for i in 1:blocksize:(j - 1)
161+ ib = blocksize
162+ ϵ += _ishermitian_approx_offdiag(
163+ view(A, i:(i + ib - 1), j:(j + jb - 1)),
164+ view(A, j:(j + jb - 1), i:(i + ib - 1)),
165+ anti
166+ )
167+ ϵ < ϵmax || return false
168+ end
169+ end
170+ return true
171+ end
172+
173+ function _ishermitian_approx_diag(A, ::Val{anti}) where {anti}
174+ n = size(A, 1)
175+ ϵ = abs2(zero(eltype(A)))
176+ @inbounds for j in 1:n
177+ @simd for i in 1:j
178+ val = anti ? (A[i, j] + adjoint(A[j, i])) : (A[i, j] - adjoint(A[j, i]))
179+ ϵ += abs2(val)
180+ end
181+ end
182+ return ϵ
183+ end
184+ function _ishermitian_approx_offdiag(Al, Au, ::Val{anti}) where {anti}
185+ m, n = size(Al) # == reverse(size(Al))
186+ ϵ = abs2(zero(eltype(Al)))
187+ @inbounds for j in 1:n
188+ @simd for i in 1:m
189+ val = anti ? (Al[i, j] + adjoint(Au[j, i])) : (Al[i, j] - adjoint(Au[j, i]))
190+ ϵ += abs2(val)
191+ end
192+ end
193+ return ϵ
194+ end
0 commit comments