@@ -28,6 +28,7 @@ function expmchk()
2828 return m_vals, theta
2929end
3030
31+
3132function getPadeCoefficients (m)
3233# GETPADECOEFFICIENTS Coefficients of numerator P of Pade approximant
3334# C = GETPADECOEFFICIENTS returns coefficients of numerator
@@ -51,7 +52,9 @@ function getPadeCoefficients(m)
5152 return c
5253end
5354
55+
5456struct MatrixExponentialWorkspace{T<: Real ,N}
57+ Ascaled:: BlochMcConnellDynamicsMatrix{T,N} # A / 2^s
5558 expA2:: BlochMcConnellMatrix{T,N}
5659 A2:: BlochMcConnellMatrix{T,N}
5760 A4:: BlochMcConnellMatrix{T,N}
@@ -65,18 +68,15 @@ struct MatrixExponentialWorkspace{T<:Real,N}
6568 mat2:: Matrix{T}
6669end
6770
68- MatrixExponentialWorkspace {T} (N) where {T} =
69- MatrixExponentialWorkspace (BlochMcConnellMatrix {T} (N),
70- BlochMcConnellMatrix {T} (N),
71- BlochMcConnellMatrix {T} (N),
72- BlochMcConnellMatrix {T} (N),
73- BlochMcConnellMatrix {T} (N),
74- BlochMcConnellMatrix {T} (N),
75- BlochMcConnellMatrix {T} (N),
76- BlochMcConnellMatrix {T} (N),
77- BlochMcConnellMatrix {T} (N),
78- Matrix {T} (undef, 3 N, 3 N),
79- Matrix {T} (undef, 3 N, 3 N))
71+
72+ # constructor
73+ MatrixExponentialWorkspace {T} (N) where {T} = MatrixExponentialWorkspace (
74+ BlochMcConnellDynamicsMatrix {T} (N),
75+ ntuple (_ -> BlochMcConnellMatrix {T} (N), 9 )... ,
76+ Matrix {T} (undef, 3 N, 3 N),
77+ Matrix {T} (undef, 3 N, 3 N),
78+ )
79+
8080
8181"""
8282 expm!(expA, A, [workspace])
@@ -101,10 +101,10 @@ function expm!(
101101
102102 normA = absolutesum (A)
103103
104- if normA <= theta[end ]
104+ if normA ≤ theta[end ]
105105 # no scaling and squaring is required
106- for i = 1 : length (m_vals)
107- if normA <= theta[i]
106+ for i in 1 : length (m_vals)
107+ if normA ≤ theta[i]
108108 PadeApproximantOfDegree! (expA, A, workspace, m_vals[i])
109109 break
110110 end
@@ -114,28 +114,35 @@ function expm!(
114114 t = frexp1 (tmp)
115115 s = frexp2 (tmp)
116116 s = s - (t == 0.5 ) # adjust s if normA / theta[end] is a power of 2
117- mul! (A, 1 / 2 ^ s) # Scaling
118- PadeApproximantOfDegree! (expA, A , workspace, m_vals[end ])
117+ mul! (workspace . Ascaled, A, 1 / 2 ^ s) # scaling
118+ PadeApproximantOfDegree! (expA, workspace . Ascaled , workspace, m_vals[end ])
119119
120- for i = 1 : s
121- mul! (workspace. expA2, expA, expA) # Squaring
120+ for i in 1 : s
121+ mul! (workspace. expA2, expA, expA) # squaring
122122 copyto! (expA, workspace. expA2)
123123 end
124124 end
125125
126126end
127127
128+
129+ """
130+ PadeApproximantOfDegree!(expA, A, workspace, m)
131+
132+ Pade approximant to exponential.
133+
134+ Based on `PADEAPPROXIMANTOFDEGREE`
135+ `F = PADEAPPROXIMANTOFDEGREE(M)` is the degree M diagonal
136+ Pade approximant to EXP(A), where M = 3, 5, 7, 9 or 13.
137+ Series are evaluated in decreasing order of powers,
138+ which is in approx. increasing order of maximum norms of the terms.
139+ """
128140function PadeApproximantOfDegree! (
129141 expA:: BlochMcConnellMatrix{T1,N} ,
130142 A:: BlochMcConnellDynamicsMatrix{T2,N,M} ,
131143 workspace:: MatrixExponentialWorkspace{T3,N} ,
132144 m:: Integer
133145) where {T1,T2,T3,N,M}
134- # PADEAPPROXIMANTOFDEGREE Pade approximant to exponential.
135- # F = PADEAPPROXIMANTOFDEGREE(M) is the degree M diagonal
136- # Pade approximant to EXP(A), where M = 3, 5, 7, 9 or 13.
137- # Series are evaluated in decreasing order of powers, which is
138- # in approx. increasing order of maximum norms of the terms.
139146
140147 n = 3 N
141148 c = getPadeCoefficients (m)
@@ -146,7 +153,7 @@ function PadeApproximantOfDegree!(
146153
147154 # Evaluate Pade approximant
148155 if m == 13
149- # For optimal evaluation need different formula for m >= 12
156+ # For optimal evaluation need different formula for m ≥ 12
150157 mul! (workspace. tmp1, workspace. A6, c[14 ])
151158 muladd! (workspace. tmp1, workspace. A4, c[12 ])
152159 muladd! (workspace. tmp1, workspace. A2, c[10 ])
@@ -169,16 +176,16 @@ function PadeApproximantOfDegree!(
169176 fill! (workspace. tmp1, zero (T3))
170177 fill! (workspace. V, zero (T3))
171178
172- if m >= 9
179+ if m ≥ 9
173180 mul! (workspace. A8, workspace. A2, workspace. A6)
174181 muladd! (workspace. tmp1, workspace. A8, c[10 ])
175182 muladd! (workspace. V, workspace. A8, c[9 ])
176183 end
177- if m >= 7
184+ if m ≥ 7
178185 muladd! (workspace. tmp1, workspace. A6, c[8 ])
179186 muladd! (workspace. V, workspace. A6, c[7 ])
180187 end
181- if m >= 5
188+ if m ≥ 5
182189 muladd! (workspace. tmp1, workspace. A4, c[6 ])
183190 muladd! (workspace. V, workspace. A4, c[5 ])
184191 end
@@ -197,6 +204,25 @@ function PadeApproximantOfDegree!(
197204
198205end
199206
207+
208+ """
209+ expA = expm(A, [workspace])
210+
211+ Return the matrix exponential of `BlochMcConnellDynamicsMatrix` `A`,
212+ where
213+ `workspace isa MatrixExponentialWorkspace`.
214+ """
215+ function expm (
216+ A:: BlochMcConnellDynamicsMatrix{Ta,N} ,
217+ workspace:: MatrixExponentialWorkspace{Tw,N} = MatrixExponentialWorkspace {Ta} (N)
218+ ) where {Ta,Tw,N}
219+ expA = BlochMcConnellMatrix {Tw} (N)
220+ expm! (expA, A, workspace)
221+ return expA
222+ end
223+
224+
225+ # helpers
200226frexp1 (x) = frexp (x)[1 ]
201227frexp2 (x) = frexp (x)[2 ]
202228dfrexp1 (x) = 2.0 ^ (- floor (log2 (abs (x))) - 1 )
0 commit comments