Skip to content

Commit 3de1bfa

Browse files
committed
non-allocating ishermitian_approx
1 parent 66eba4d commit 3de1bfa

File tree

1 file changed

+62
-9
lines changed

1 file changed

+62
-9
lines changed

src/common/matrixproperties.jl

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,32 @@ is_right_isometric(A; kwargs...) = is_left_isometric(A'; kwargs...)
6969
Test whether a linear map is Hermitian, i.e. `A = A'`.
7070
The `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
7878
end
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))
8484
end
85+
ishermitian_approx(A::StridedMatrix; kwargs...) = strided_ishermitian_approx(A, Val(false); kwargs...)
8586

8687
"""
8788
isantihermitian(A; isapprox_kwargs...)
8889
8990
Test whether a linear map is anti-Hermitian, i.e. `A = -A'`.
9091
The `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
9899
end
99100
function isantihermitian_exact(A)
@@ -102,6 +103,10 @@ end
102103
function isantihermitian_exact(A::StridedMatrix; kwargs...)
103104
return strided_ishermitian_exact(A, Val(true); kwargs...)
104105
end
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
141146
end
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

Comments
 (0)