Skip to content

Commit da730f7

Browse files
committed
refactor: make domain-clamp helpers GPU compatible
Replace the scalar-indexing loops in `_clamp_domain_eigenvalues!` and `_check_nonzero_eigenvalues` with reductions and broadcasts, so the helpers work on GPU arrays. The `DomainError` now reports the worst offending eigenvalue instead of the first one encountered.
1 parent 1f05f59 commit da730f7

1 file changed

Lines changed: 27 additions & 35 deletions

File tree

src/implementations/matrixfunctions.jl

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,17 @@
55
# and throw a `DomainError` for eigenvalues that are genuinely negative, since then the
66
# result cannot be expressed with the same (real) scalar type.
77
function _clamp_domain_eigenvalues!::AbstractVector{<:Real}, atol::Real)
8-
for i in eachindex(λ)
9-
x = λ[i]
10-
if x < -atol
11-
throw(
12-
DomainError(
13-
x,
14-
"The matrix has a negative real eigenvalue beyond `domain_atol = $atol` and the result of this matrix function is complex. " *
15-
"Pass a complex matrix to obtain the principal value, or increase `domain_atol` if the eigenvalue is a rounding artifact."
16-
)
8+
λmin = minimum(λ; init = zero(eltype(λ)))
9+
if λmin < -atol
10+
throw(
11+
DomainError(
12+
λmin,
13+
"The matrix has a negative real eigenvalue beyond `domain_atol = $atol` and the result of this matrix function is complex. " *
14+
"Pass a complex matrix to obtain the principal value, or increase `domain_atol` if the eigenvalue is a rounding artifact."
1715
)
18-
elseif x < 0
19-
λ[i] = zero(x)
20-
end
16+
)
2117
end
18+
λ .= max.(λ, zero(eltype(λ)))
2219
return λ
2320
end
2421

@@ -33,37 +30,32 @@ end
3330
# Complex eigenvalues of a real matrix: only eigenvalues (numerically) on the negative
3431
# real axis obstruct a real result; complex-conjugate pairs do not.
3532
function _clamp_domain_eigenvalues!::AbstractVector{<:Complex}, atol::Real)
36-
for i in eachindex(λ)
37-
x = λ[i]
38-
if abs(imag(x)) <= atol && real(x) < 0
39-
if real(x) < -atol
40-
throw(
41-
DomainError(
42-
x,
43-
"The matrix has an eigenvalue on the negative real axis beyond `domain_atol = $atol` and the result of this matrix function is complex. " *
44-
"Pass a complex matrix to obtain the principal value, or increase `domain_atol` if the eigenvalue is a rounding artifact."
45-
)
46-
)
47-
else
48-
λ[i] = zero(x)
49-
end
50-
end
33+
onaxis = x -> abs(imag(x)) <= atol && real(x) < 0
34+
λmin = mapreduce(x -> onaxis(x) ? real(x) : zero(real(x)), min, λ; init = zero(real(eltype(λ))))
35+
if λmin < -atol
36+
throw(
37+
DomainError(
38+
λmin,
39+
"The matrix has an eigenvalue on the negative real axis beyond `domain_atol = $atol` and the result of this matrix function is complex. " *
40+
"Pass a complex matrix to obtain the principal value, or increase `domain_atol` if the eigenvalue is a rounding artifact."
41+
)
42+
)
5143
end
44+
λ .= ifelse.(onaxis.(λ), zero(eltype(λ)), λ)
5245
return λ
5346
end
5447

5548
# Reject (numerically) zero eigenvalues for functions that are undefined there,
5649
# e.g. `logarithm` and `power` with a negative fractional power.
5750
function _check_nonzero_eigenvalues(λ, atol::Real)
58-
for x in λ
59-
if abs(x) <= atol
60-
throw(
61-
DomainError(
62-
x,
63-
"The matrix has a (numerically) zero eigenvalue within `domain_atol = $atol`, for which this matrix function is not defined."
64-
)
51+
amin = minimum(abs, λ; init = typemax(real(eltype(λ))))
52+
if amin <= atol
53+
throw(
54+
DomainError(
55+
amin,
56+
"The matrix has a (numerically) zero eigenvalue within `domain_atol = $atol`, for which this matrix function is not defined."
6557
)
66-
end
58+
)
6759
end
6860
return λ
6961
end

0 commit comments

Comments
 (0)