Skip to content

Commit 9bb3ecb

Browse files
committed
expm implementation for sizes >= 3, relies on LU-based ldiv (\)
1 parent 9b6427f commit 9bb3ecb

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

src/expm.jl

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,58 @@ end
6666
(newtype)((m11, m21, m12, m22))
6767
end
6868

69-
# TODO add special case for 3x3 matrices
70-
if VERSION < v"0.7-"
71-
@inline _exp(s::Size, A::StaticArray) = s(Base.expm(Array(A)))
72-
else
73-
@inline _exp(s::Size, A::StaticArray) = s(Base.exp(Array(A)))
69+
# Adapted from implementation in Base; algorithm from
70+
# Higham, "Functions of Matrices: Theory and Computation", SIAM, 2008
71+
function _exp(::Size, A::StaticMatrix{<:Any,<:Any,T}) where T
72+
# omitted: matrix balancing, i.e., LAPACK.gebal!
73+
nA = maximum(sum(abs.(A), Val{1})) # marginally more performant than norm(A, 1)
74+
## For sufficiently small nA, use lower order Padé-Approximations
75+
if (nA <= 2.1)
76+
A2 = A*A
77+
if nA > 0.95
78+
U = @evalpoly(A2, T(8821612800)*I, T(302702400)*I, T(2162160)*I, T(3960)*I, T(1)*I)
79+
U = A*U
80+
V = @evalpoly(A2, T(17643225600)*I, T(2075673600)*I, T(30270240)*I, T(110880)*I, T(90)*I)
81+
elseif nA > 0.25
82+
U = @evalpoly(A2, T(8648640)*I, T(277200)*I, T(1512)*I, T(1)*I)
83+
U = A*U
84+
V = @evalpoly(A2, T(17297280)*I, T(1995840)*I, T(25200)*I, T(56)*I)
85+
elseif nA > 0.015
86+
U = @evalpoly(A2, T(15120)*I, T(420)*I, T(1)*I)
87+
U = A*U
88+
V = @evalpoly(A2, T(30240)*I, T(3360)*I, T(30)*I)
89+
else
90+
U = @evalpoly(A2, T(60)*I, T(1)*I)
91+
U = A*U
92+
V = @evalpoly(A2, T(120)*I, T(12)*I)
93+
end
94+
expA = (V - U) \ (V + U)
95+
else
96+
s = log2(nA/5.4) # power of 2 later reversed by squaring
97+
if s > 0
98+
si = ceil(Int,s)
99+
A = A / T(2^si)
100+
end
101+
102+
A2 = A*A
103+
A4 = A2*A2
104+
A6 = A2*A4
105+
106+
U = A6*(T(1)*A6 + T(16380)*A4 + T(40840800)*A2) +
107+
(T(33522128640)*A6 + T(10559470521600)*A4 + T(1187353796428800)*A2) +
108+
T(32382376266240000)*I
109+
U = A*U
110+
V = A6*(T(182)*A6 + T(960960)*A4 + T(1323241920)*A2) +
111+
(T(670442572800)*A6 + T(129060195264000)*A4 + T(7771770303897600)*A2) +
112+
T(64764752532480000)*I
113+
expA = (V - U) \ (V + U)
114+
115+
if s > 0 # squaring to reverse dividing by power of 2
116+
for t=1:si
117+
expA = expA*expA
118+
end
119+
end
120+
end
121+
122+
expA
74123
end

test/expm.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,15 @@
44
@test expm(@SMatrix [4 2; -2 1])::SMatrix expm([4 2; -2 1])
55
@test expm(@SMatrix [4 2; 2 1])::SMatrix expm([4 2; 2 1])
66
@test expm(@SMatrix [ -3+1im -1+2im;-4-5im 5-2im])::SMatrix expm(Complex{Float64}[ -3+1im -1+2im;-4-5im 5-2im])
7-
@test expm(@SMatrix [1 2 0; 2 1 0; 0 0 1])::SizedArray{Tuple{3,3}} expm([1 2 0; 2 1 0; 0 0 1])
7+
@test expm(@SMatrix [1 2 0; 2 1 0; 0 0 1]) expm([1 2 0; 2 1 0; 0 0 1])
8+
9+
for sz in (3,4), typ in (Float64, Complex{Float64})
10+
A = rand(typ, sz, sz)
11+
nA = norm(A, 1)
12+
for nB in (0.005, 0.1, 0.5, 3.0, 20.0)
13+
B = A*nB/nA
14+
SB = SMatrix{sz,sz,typ}(B)
15+
@test expm(B) expm(SB)
16+
end
17+
end
818
end

0 commit comments

Comments
 (0)