|
| 1 | +# Inputs |
| 2 | +# ------ |
| 3 | +function copy_input(::typeof(power), A::AbstractMatrix, p::Real) |
| 4 | + return copy!(similar(A, float(eltype(A))), A), p |
| 5 | +end |
| 6 | +copy_input(::typeof(power), A::Diagonal, p::Real) = Diagonal(float.(diagview(A))), p |
| 7 | + |
| 8 | +function check_input(::typeof(power!), A::AbstractMatrix, p::Real, powA, alg::AbstractAlgorithm) |
| 9 | + m = LinearAlgebra.checksquare(A) |
| 10 | + @check_size(powA, (m, m)) |
| 11 | + @check_scalar(powA, A) |
| 12 | + return nothing |
| 13 | +end |
| 14 | + |
| 15 | +function check_input(::typeof(power!), A::AbstractMatrix, p::Real, powA, ::DiagonalAlgorithm) |
| 16 | + m = LinearAlgebra.checksquare(A) |
| 17 | + @assert isdiag(A) |
| 18 | + @assert powA isa Diagonal |
| 19 | + @check_size(powA, (m, m)) |
| 20 | + @check_scalar(powA, A) |
| 21 | + return nothing |
| 22 | +end |
| 23 | + |
| 24 | +# Algorithm selection |
| 25 | +# ------------------- |
| 26 | +power!(A::AbstractMatrix, p::Real, alg::DefaultAlgorithm) = power!(A, p, select_algorithm(power!, (A, p), nothing; alg.kwargs...)) |
| 27 | +power!(A::AbstractMatrix, p::Real, out, alg::DefaultAlgorithm) = power!(A, p, out, select_algorithm(power!, (A, p), nothing; alg.kwargs...)) |
| 28 | + |
| 29 | +# Outputs |
| 30 | +# ------- |
| 31 | +initialize_output(::typeof(power!), A::AbstractMatrix, p::Real, ::AbstractAlgorithm) = A |
| 32 | + |
| 33 | +# Implementation |
| 34 | +# -------------- |
| 35 | +function power!(A::AbstractMatrix, p::Real, powA, alg::MatrixFunctionViaLA) |
| 36 | + check_input(power!, A, p, powA, alg) |
| 37 | + isempty(alg.kwargs) || throw(ArgumentError("`MatrixFunctionViaLA` does not accept keyword arguments for `power`")) |
| 38 | + result = A^p |
| 39 | + _copy_result!(power!, powA, result) |
| 40 | + return powA |
| 41 | +end |
| 42 | + |
| 43 | +function power!(A::AbstractMatrix, p::Real, powA, alg::MatrixFunctionViaEigh) |
| 44 | + check_input(power!, A, p, powA, alg) |
| 45 | + D, V = eigh_full!(A, alg.eigh_alg) |
| 46 | + λ = diagview(D) |
| 47 | + if isinteger(p) |
| 48 | + p < 0 && any(iszero, λ) && throw(LinearAlgebra.SingularException(0)) |
| 49 | + λ .= λ .^ p |
| 50 | + VD = V * D |
| 51 | + mul!(powA, VD, V') |
| 52 | + else |
| 53 | + atol = something(alg.domain_atol, default_domain_atol(λ)) |
| 54 | + p < 0 && _check_nonzero_eigenvalues(λ, atol) |
| 55 | + _clamp_domain_eigenvalues!(λ, atol) |
| 56 | + # `A^p = (V * D^(p/2)) * (V * D^(p/2))'` is hermitian by construction |
| 57 | + λ .= λ .^ (p / 2) |
| 58 | + Vs = rmul!(V, D) |
| 59 | + mul!(powA, Vs, Vs') |
| 60 | + end |
| 61 | + return project_hermitian!(powA) |
| 62 | +end |
| 63 | + |
| 64 | +function power!(A::AbstractMatrix, p::Real, powA, alg::MatrixFunctionViaEig) |
| 65 | + check_input(power!, A, p, powA, alg) |
| 66 | + D, V = eig_full!(A, alg.eig_alg) |
| 67 | + λ = diagview(D) |
| 68 | + if isinteger(p) |
| 69 | + p < 0 && any(iszero, λ) && throw(LinearAlgebra.SingularException(0)) |
| 70 | + else |
| 71 | + atol = something(alg.domain_atol, default_domain_atol(λ)) |
| 72 | + p < 0 && _check_nonzero_eigenvalues(λ, atol) |
| 73 | + eltype(A) <: Real && _clamp_domain_eigenvalues!(λ, atol) |
| 74 | + end |
| 75 | + if eltype(A) <: Real |
| 76 | + λ .= λ .^ p |
| 77 | + VD = V * D |
| 78 | + powAc = rdiv!(VD, LinearAlgebra.lu!(V)) |
| 79 | + return powA .= real.(powAc) |
| 80 | + else |
| 81 | + λ .= λ .^ p |
| 82 | + powA .= V .* transpose(λ) |
| 83 | + return rdiv!(powA, LinearAlgebra.lu!(V)) |
| 84 | + end |
| 85 | +end |
| 86 | + |
| 87 | +# Diagonal logic |
| 88 | +# -------------- |
| 89 | +function power!(A::AbstractMatrix, p::Real, powA, alg::DiagonalAlgorithm) |
| 90 | + check_input(power!, A, p, powA, alg) |
| 91 | + λ = diagview(powA) |
| 92 | + copyto!(λ, diagview(A)) |
| 93 | + if isinteger(p) |
| 94 | + p < 0 && any(iszero, λ) && throw(LinearAlgebra.SingularException(0)) |
| 95 | + else |
| 96 | + atol = something(get(alg.kwargs, :domain_atol, nothing), default_domain_atol(λ)) |
| 97 | + p < 0 && _check_nonzero_eigenvalues(λ, atol) |
| 98 | + eltype(λ) <: Real && _clamp_domain_eigenvalues!(λ, atol) |
| 99 | + end |
| 100 | + λ .= λ .^ p |
| 101 | + return powA |
| 102 | +end |
0 commit comments