@@ -31,6 +31,10 @@ function isunitary(A; isapprox_kwargs...)
3131 return is_left_isometry (A; isapprox_kwargs... ) &&
3232 is_right_isometry (A; isapprox_kwargs... )
3333end
34+ function isunitary (A:: AbstractMatrix ; isapprox_kwargs... )
35+ size (A, 1 ) == size (A, 2 ) || return false
36+ return is_left_isometry (A; isapprox_kwargs... )
37+ end
3438
3539@doc """
3640 is_left_isometry(A; isapprox_kwargs...) -> Bool
@@ -41,8 +45,11 @@ The `isapprox_kwargs` can be used to control the tolerances of the equality.
4145See also [`isisometry`](@ref) and [`is_right_isometry`](@ref).
4246""" is_left_isometry
4347
44- function is_left_isometry (A:: AbstractMatrix ; isapprox_kwargs... )
45- return isapprox (A' * A, LinearAlgebra. I; isapprox_kwargs... )
48+ function is_left_isometry (A:: AbstractMatrix ; atol:: Real = 0 , rtol:: Real = defaulttol (A), norm = LinearAlgebra. norm)
49+ P = A' * A
50+ nP = norm (P) # isapprox would use `rtol * max(norm(P), norm(I))`
51+ diagview (P) .- = 1
52+ return norm (P) <= max (atol, rtol * nP) # assume that the norm of I is `sqrt(n)`
4653end
4754
4855@doc """
@@ -54,6 +61,86 @@ The `isapprox_kwargs` can be used to control the tolerances of the equality.
5461See also [`isisometry`](@ref) and [`is_left_isometry`](@ref).
5562""" is_right_isometry
5663
57- function is_right_isometry (A:: AbstractMatrix ; isapprox_kwargs... )
58- return isapprox (A * A' , LinearAlgebra. I; isapprox_kwargs... )
64+ function is_right_isometry (A:: AbstractMatrix ; atol:: Real = 0 , rtol:: Real = defaulttol (A), norm = LinearAlgebra. norm)
65+ P = A * A'
66+ nP = norm (P) # isapprox would use `rtol * max(norm(P), norm(I))`
67+ diagview (P) .- = 1
68+ return norm (P) <= max (atol, rtol * nP) # assume that the norm of I is `sqrt(n)`
69+ end
70+
71+ """
72+ ishermitian(A; isapprox_kwargs...)
73+
74+ Test whether a linear map is Hermitian, i.e. `A = A'`.
75+ The `isapprox_kwargs` can be used to control the tolerances of the equality.
76+ """
77+ function ishermitian (A; atol:: Real = 0 , rtol:: Real = 0 , norm = LinearAlgebra. norm, kwargs... )
78+ if iszero (atol) && iszero (rtol)
79+ return ishermitian_exact (A; kwargs... )
80+ else
81+ return 2 * norm (project_antihermitian (A; kwargs... )) ≤ max (atol, rtol * norm (A))
82+ end
83+ end
84+ function ishermitian_exact (A)
85+ return A == A'
86+ end
87+ function ishermitian_exact (A:: StridedMatrix ; kwargs... )
88+ return strided_ishermitian_exact (A, Val (false ); kwargs... )
89+ end
90+
91+ """
92+ isantihermitian(A; isapprox_kwargs...)
93+
94+ Test whether a linear map is anti-Hermitian, i.e. `A = -A'`.
95+ The `isapprox_kwargs` can be used to control the tolerances of the equality.
96+ """
97+ function isantihermitian (A; atol:: Real = 0 , rtol:: Real = 0 , norm = LinearAlgebra. norm, kwargs... )
98+ if iszero (atol) && iszero (rtol)
99+ return isantihermitian_exact (A; kwargs... )
100+ else
101+ return 2 * norm (project_hermitian (A; kwargs... )) ≤ max (atol, rtol * norm (A))
102+ end
103+ end
104+ function isantihermitian_exact (A)
105+ return A == - A'
106+ end
107+ function isantihermitian_exact (A:: StridedMatrix ; kwargs... )
108+ return strided_ishermitian_exact (A, Val (true ); kwargs... )
109+ end
110+
111+ # blocked implementation of exact checks for strided matrices
112+ # -----------------------------------------------------------
113+ function strided_ishermitian_exact (A:: AbstractMatrix , anti:: Val ; blocksize = 32 )
114+ n = size (A, 1 )
115+ for j in 1 : blocksize: n
116+ jb = min (blocksize, n - j + 1 )
117+ _ishermitian_exact_diag (view (A, j: (j + jb - 1 ), j: (j + jb - 1 )), anti) || return false
118+ for i in 1 : blocksize: (j - 1 )
119+ ib = blocksize
120+ _ishermitian_exact_offdiag (
121+ view (A, i: (i + ib - 1 ), j: (j + jb - 1 )),
122+ view (A, j: (j + jb - 1 ), i: (i + ib - 1 )),
123+ anti
124+ ) || return false
125+ end
126+ end
127+ return true
128+ end
129+ function _ishermitian_exact_diag (A, :: Val{anti} ) where {anti}
130+ n = size (A, 1 )
131+ @inbounds for j in 1 : n
132+ @simd for i in 1 : j
133+ A[i, j] == (anti ? - adjoint (A[j, i]) : adjoint (A[j, i])) || return false
134+ end
135+ end
136+ return true
137+ end
138+ function _ishermitian_exact_offdiag (Al, Au, :: Val{anti} ) where {anti}
139+ m, n = size (Al) # == reverse(size(Al))
140+ @inbounds for j in 1 : n
141+ @simd for i in 1 : m
142+ Al[i, j] == (anti ? - adjoint (Au[j, i]) : adjoint (Au[j, i])) || return false
143+ end
144+ end
145+ return true
59146end
0 commit comments