4
C Brute force exponentiation of a complex operator A via a power
5
C series. Works for any square complex matrix.
7
C e^A = \sum_{k=0}^{\infty} 1/k! A^k
9
C We first scale A by 2^m (such that ||A|| <= 1), then square the
10
C result m times to recover the answer:
12
C [ e^{A/2^m} ]^{2^m} = e^A
14
C We compute the necessary m each time via the infinity norm of ||A||.
16
subroutine exp_pseries (params, g_za, g_zexpa)
22
#include "mafdecls.fh"
24
#include "matutils.fh"
25
#include "rt_tddft.fh"
29
type(rt_params_t), intent(in) :: params
30
integer, intent(in) :: g_za
34
integer, intent(in) :: g_zexpa
38
character(*), parameter :: pname = "exp_pseries: "
39
integer, parameter :: maxk = 1000 !max num terms in expansion
40
integer, parameter :: max_mscale = 16 !=65536, norm should never be this big
48
double precision scale_val
50
integer g_new_term, g_prev_term
55
character*120 outstring
56
double precision elapsed
60
double precision ratio
61
integer mscale !divide A by 2^m, then square result m times
64
if (params%prof) call prof_start (elapsed)
72
call ga_check_handle (g_za,
73
$ "first argument of "//pname//" not a valid GA")
75
call ga_inquire (g_za, dtype, n1, n2)
77
if (dtype .ne. mt_dcpl) call errquit (
78
$ pname//" only valid for complex matricies", 0, 0)
81
$ call errquit (pname//"n1 must equal n2")
83
if (.not. ga_duplicate(g_za, g_za_scaled, "g_za_scaled"))
84
$ call errquit (pname//"alloc failed", 0, GA_ERR)
85
if (.not. ga_duplicate(g_za, g_new_term, "g_new_term"))
86
$ call errquit (pname//"alloc failed", 0, GA_ERR)
87
if (.not. ga_duplicate(g_za, g_prev_term, "g_prev_term"))
88
$ call errquit (pname//"alloc failed", 0, GA_ERR)
93
C Determine 2^m factor and scale input matrix.
96
C We technically need to compute the scaling factor from the
97
C spectral range of the W operator, but a simple norm will suffice.
99
C If L is the largest abs eigenvalue (or the norm as we do it
100
C below), then we pick scaling factor "m" such that:
103
C => m log 2 = log (2.5*L)
104
C m = log (2.5*L) / log 2
106
C (we multiply by 2.5 to ensure ||W|| < 1)
109
call ga_norm_infinity (g_za, norm)
111
ratio = dlog (2.5d0*norm) / dlog (2d0)
112
mscale = max (int (ratio), 0)
116
$ call errquit (pname//"negative mscale", 0, 0)
118
if (mscale .gt. max_mscale)
119
$ call errquit (pname//"mscale exceeds 16 (2^16 = 65536)",
123
scale_val = 1d0/(2d0**mscale)
124
zscale = dcmplx (scale_val, 0d0)
126
call ga_zero (g_za_scaled)
127
call ga_copy (g_za, g_za_scaled)
128
call ga_scale (g_za_scaled, zscale)
132
C k = 0 term is just I
134
call ga_zero (g_prev_term)
135
call mat_set_ident (g_prev_term)
137
call ga_zero (g_zexpa)
138
call ga_copy (g_prev_term, g_zexpa)
144
do while (.not. converged)
148
zinvk = z1 / dcmplx (ik)
152
C New term is 1/k A times previous term
154
call ga_zero (g_new_term)
155
call ga_zgemm ("N", "N", n1, n1, n1,
156
$ zinvk, g_prev_term, g_za_scaled, z0, g_new_term)
161
C Add new term to sum and store new term as previous.
163
call ga_add (z1, g_zexpa, z1, g_new_term, g_zexpa)
164
call ga_zero (g_prev_term)
165
call ga_copy (g_new_term, g_prev_term)
169
C Check if we have converged
171
norm = mat_norm (g_new_term)
173
if (norm < params%tol_series) num_zeroterms = num_zeroterms + 1
175
if (num_zeroterms .ge. params%terms_series)
180
C Stop if we have failed to converge after max num terms.
183
$ call errquit (pname//"failed to converge", 0, 0)
189
C Square output matrix m times to recover unscaled result. Use
190
C g_prev_term as scratch space.
194
call ga_zero (g_prev_term)
195
call ga_zgemm ("N", "N", n1, n1, n1,
196
$ z1, g_zexpa, g_zexpa, z0, g_prev_term)
197
call ga_zero (g_zexpa)
198
call ga_copy (g_prev_term, g_zexpa)
205
if (.not. ga_destroy(g_new_term))
206
$ call errquit (pname//"destroy failed", 0, GA_ERR)
207
if (.not. ga_destroy(g_prev_term))
208
$ call errquit (pname//"destroy failed", 0, GA_ERR)
209
if (.not. ga_destroy(g_za_scaled))
210
$ call errquit (pname//"destroy failed", 0, GA_ERR)
214
C If profiling is enabled print convergence data.
216
if (params%prof) then
217
write(outstring,"(a,i0,a,i0,a)")
218
$ "Power series with scaling 2^",
219
$ mscale, " converged after ", ik," terms;"
220
call prof_end (elapsed, trim(outstring))