Skip to content

Commit 0a2e94f

Browse files
committed
Add real powers of a matrix
1 parent 1f97591 commit 0a2e94f

File tree

4 files changed

+279
-91
lines changed

4 files changed

+279
-91
lines changed

base/linalg/dense.jl

+57-8
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,63 @@ kron(a::Vector, b::Vector)=vec(kron(reshape(a,length(a),1),reshape(b,length(b),1
169169
kron(a::Matrix, b::Vector)=kron(a,reshape(b,length(b),1))
170170
kron(a::Vector, b::Matrix)=kron(reshape(a,length(a),1),b)
171171

172-
^(A::Matrix, p::Integer) = p < 0 ? inv(A^-p) : Base.power_by_squaring(A,p)
173-
174-
function ^(A::Matrix, p::Number)
172+
# Matrix power
173+
^(A::AbstractMatrix, p::Integer) = p < 0 ? Base.power_by_squaring(inv(A),-p) : Base.power_by_squaring(A,p)
174+
function ^(A::AbstractMatrix, p::Real)
175+
# For integer powers, use repeated squaring
175176
if isinteger(p)
176177
return A^Integer(real(p))
177178
end
178-
chksquare(A)
179-
v, X = eig(A)
180-
any(v.<0) && (v = complex(v))
181-
Xinv = ishermitian(A) ? X' : inv(X)
182-
scale(X, v.^p)*Xinv
179+
180+
# If possible, use diagonalization
181+
if issym(A)
182+
return full(Symmetric(A)^p)
183+
end
184+
if ishermitian(A)
185+
return full(Hermitian(A)^p)
186+
end
187+
188+
# General matrices
189+
p1 = p - floor(p)
190+
if cond(A) >= exp(log(p1/(1 - p1)) / p1)
191+
return powm(A,p1) * A^floor(p)
192+
else
193+
return powm(A,p - ceil(p)) * A^ceil(A)
194+
end
195+
end
196+
function powm(A::StridedMatrix, p::Real)
197+
if abs(p) >= 1
198+
ArgumentError("p must be a real number in (-1,1), got $p")
199+
end
200+
201+
# Use Schur decomposition
202+
n = chksquare(A)
203+
if istriu(A)
204+
retmat = full(powm(UpperTriangular(complex(A)),p))
205+
d = diag(A)
206+
else
207+
S,Q,d = schur(complex(A))
208+
R = powm(UpperTriangular(S),p)
209+
retmat = Q * R * Q'
210+
end
211+
212+
# Check whether the matrix has nonpositive real eigs
213+
np_real_eigs = false
214+
for i = 1:n
215+
if imag(d[i]) < eps() && real(d[i]) <= 0
216+
np_real_eigs = true
217+
break
218+
end
219+
end
220+
if np_real_eigs
221+
warn("Matrix with nonpositive real eigenvalues, a nonprincipal matrix power will be returned.")
222+
end
223+
224+
if isreal(A) && ~np_real_eigs
225+
return real(retmat)
226+
else
227+
return retmat
228+
end
183229
end
184230

185231
# Matrix exponential
@@ -286,6 +332,9 @@ expm(x::Number) = exp(x)
286332

287333
function logm(A::StridedMatrix)
288334
# If possible, use diagonalization
335+
if issym(A)
336+
return full(logm(Symmetric(A)))
337+
end
289338
if ishermitian(A)
290339
return full(logm(Hermitian(A)))
291340
end

base/linalg/diagonal.jl

+8-3
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,14 @@ end
118118
# identity matrices via eye(Diagonal{type},n)
119119
eye{T}(::Type{Diagonal{T}}, n::Int) = Diagonal(ones(T,n))
120120

121-
expm(D::Diagonal) = Diagonal(exp(D.diag))
122-
logm(D::Diagonal) = Diagonal(log(D.diag))
123-
sqrtm(D::Diagonal) = Diagonal(sqrt(D.diag))
121+
# Matrix functions
122+
^(D::Diagonal, p::Integer) = Diagonal((D.diag).^p)
123+
^(D::Diagonal, p::Real) = Diagonal((D.diag).^p)
124+
for (funm, func) in ([:expm,:exp], [:sqrtm,:sqrt], [:logm,:log])
125+
@eval begin
126+
($funm)(D::Diagonal) = Diagonal(($func)(D.diag))
127+
end
128+
end
124129

125130
#Linear solver
126131
function A_ldiv_B!(D::Diagonal, B::StridedVecOrMat)

base/linalg/symmetric.jl

+48-5
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,54 @@ function svdvals!{T<:Real,S}(A::Union{Hermitian{T,S}, Symmetric{T,S}, Hermitian{
131131
return sort!(vals, rev = true)
132132
end
133133

134-
#Matrix-valued functions
134+
#Matrix functions
135+
function ^(A::Symmetric, p::Integer)
136+
if p < 0
137+
return Symmetric(Base.power_by_squaring(inv(A), -p))
138+
else
139+
return Symmetric(Base.power_by_squaring(A, p))
140+
end
141+
end
142+
function ^(A::Symmetric, p::Real)
143+
F = eigfact(full(A))
144+
if isposdef(F)
145+
retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors'
146+
else
147+
retmat = (F.vectors * Diagonal((complex(F.values)).^p)) * F.vectors'
148+
end
149+
return Symmetric(retmat)
150+
end
151+
function ^(A::Hermitian, p::Integer)
152+
n = chksquare(A)
153+
if p < 0
154+
retmat = Base.power_by_squaring(inv(A), -p)
155+
else
156+
retmat = Base.power_by_squaring(A, p)
157+
end
158+
for i = 1:n
159+
retmat[i,i] = real(retmat[i,i])
160+
end
161+
return Hermitian(retmat)
162+
end
163+
function ^{T}(A::Hermitian{T}, p::Real)
164+
n = chksquare(A)
165+
F = eigfact(A)
166+
if isposdef(F)
167+
retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors'
168+
if T <: Real
169+
return Hermitian(retmat)
170+
else
171+
for i = 1:n
172+
retmat[i,i] = real(retmat[i,i])
173+
end
174+
return Hermitian(retmat)
175+
end
176+
else
177+
retmat = (F.vectors * Diagonal((complex(F.values).^p))) * F.vectors'
178+
return retmat
179+
end
180+
end
181+
135182
function expm(A::Symmetric)
136183
F = eigfact(full(A))
137184
return Symmetric((F.vectors * Diagonal(exp(F.values))) * F.vectors')
@@ -151,9 +198,7 @@ function expm{T}(A::Hermitian{T})
151198
end
152199

153200
for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt])
154-
155201
@eval begin
156-
157202
function ($funm)(A::Symmetric)
158203
F = eigfact(full(A))
159204
if isposdef(F)
@@ -182,7 +227,5 @@ for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt])
182227
return retmat
183228
end
184229
end
185-
186230
end
187-
188231
end

0 commit comments

Comments
 (0)