File size: 10,126 Bytes
9425aed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
! BOB Quantum Civilization Engine - Time Evolution
! Module: bob_evolution
! Purpose: Schrödinger evolution, Trotterization, Krylov, RK4, Magnus
! Standard: Fortran 2018

module bob_evolution
    use bob_kinds
    use bob_errors
    use bob_state
    use bob_hamiltonian
    implicit none
    private

    public :: bob_evolve_exact
    public :: bob_evolve_trotter
    public :: bob_evolve_krylov
    public :: bob_evolve_rk4
    public :: bob_evolve_magnus
    public :: bob_time_evolution_operator
    public :: bob_time_integrator
    public :: INTEGRATOR_EXACT, INTEGRATOR_TROTTER, INTEGRATOR_KRYLOV
    public :: INTEGRATOR_RK4, INTEGRATOR_MAGNUS

    integer(i4), parameter :: INTEGRATOR_EXACT    = 1
    integer(i4), parameter :: INTEGRATOR_TROTTER  = 2
    integer(i4), parameter :: INTEGRATOR_KRYLOV   = 3
    integer(i4), parameter :: INTEGRATOR_RK4      = 4
    integer(i4), parameter :: INTEGRATOR_MAGNUS   = 5

    type, public :: bob_time_integrator
        integer(i4) :: method = INTEGRATOR_RK4
        real(wp) :: dt = 0.01_wp
        integer(i4) :: trotter_order = 2
        integer(i4) :: krylov_dim = 20
        character(len=:), allocatable :: name
    contains
        procedure, public :: init => int_init
        procedure, public :: step => int_step
    end type bob_time_integrator

contains

    !> Exact evolution: |ψ(t)⟩ = exp(-iHdt)|ψ(0)⟩
    subroutine bob_evolve_exact(state, H, dt)
        type(bob_quantum_state), intent(inout) :: state
        complex(cwp), intent(in) :: H(:,:)
        real(wp), intent(in) :: dt
        complex(cwp), allocatable :: U(:,:), psi_new(:)
        integer(i8) :: dim
        if (.not. state%is_valid) then
            call bob_set_error(BOB_ERROR_INVALID_STATE, "Invalid state", "bob_evolve_exact"); return
        end if
        dim = state%dim
        if (size(H,1)/=dim .or. size(H,2)/=dim) then
            call bob_set_error(BOB_ERROR_DIMENSION_MISMATCH, "H dim mismatch", "bob_evolve_exact"); return
        end if
        U = bob_time_evolution_operator(H, dt)
        allocate(psi_new(dim)); psi_new = matmul(U, state%amplitudes)
        state%amplitudes = psi_new; state%is_normalized = .true.
        call bob_clear_error()
    end subroutine bob_evolve_exact

    !> Time evolution operator exp(-iHdt) via Padé (6,6) + scaling/squaring
    function bob_time_evolution_operator(H, dt) result(U)
        complex(cwp), intent(in) :: H(:,:)
        real(wp), intent(in) :: dt
        complex(cwp), allocatable :: U(:,:), A(:,:), I_mat(:,:)
        complex(cwp), allocatable :: U_num(:,:), U_den(:,:), A2(:,:), A4(:,:), A6(:,:)
        integer :: dim, n_squarings, i
        real(wp) :: norm_H
        dim = size(H,1)
        allocate(U(dim,dim), A(dim,dim), I_mat(dim,dim))
        allocate(U_num(dim,dim), U_den(dim,dim), A2(dim,dim), A4(dim,dim), A6(dim,dim))
        I_mat = CZERO; do i=1,dim; I_mat(i,i)=CONE; end do
        norm_H = maxval(abs(H))
        if (norm_H > ZERO) then
            A = -CI * H * dt / norm_H
            n_squarings = max(0, ceiling(log(norm_H * abs(dt))/log(2.0_wp)))
        else
            A = -CI * H * dt; n_squarings = 0
        end if
        A2 = matmul(A, A)
        A4 = matmul(A2, A2)
        A6 = matmul(A4, A2)
        U_num = I_mat + A/2.0_wp + A2/12.0_wp + matmul(A,A2)/240.0_wp + A4/10080.0_wp + &
                matmul(A,A4)/725760.0_wp + A6/7257600.0_wp
        U_den = I_mat - A/2.0_wp + A2/12.0_wp - matmul(A,A2)/240.0_wp + A4/10080.0_wp - &
                matmul(A,A4)/725760.0_wp + A6/7257600.0_wp
        call invert_matrix(U_den, U)
        U = matmul(U, U_num)
        do i = 1, n_squarings
            U = matmul(U, U)
        end do
    end function bob_time_evolution_operator

    subroutine invert_matrix(A, Ainv)
        complex(cwp), intent(in) :: A(:,:)
        complex(cwp), intent(out) :: Ainv(:,:)
        integer :: n, i, k, pivot
        complex(cwp), allocatable :: aug(:,:)
        n = size(A,1)
        allocate(aug(n, 2*n)); aug = CZERO
        aug(:,1:n) = A
        do i=1,n; aug(i,n+i)=CONE; end do
        do i=1,n
            pivot = i
            do k=i+1,n; if (abs(aug(k,i)) > abs(aug(pivot,i))) pivot=k; end do
            if (abs(aug(pivot,i)) < TOL_NORM) then
                call bob_set_error(BOB_ERROR_CONVERGENCE, "Singular matrix", "invert_matrix")
                Ainv = CZERO; return
            end if
            if (pivot /= i) aug([i,pivot],:) = aug([pivot,i],:)
            aug(i,:) = aug(i,:) / aug(i,i)
            do k=1,n
                if (k /= i) aug(k,:) = aug(k,:) - aug(k,i) * aug(i,:)
            end do
        end do
        Ainv = aug(:,n+1:2*n)
    end subroutine invert_matrix

    !> Krylov subspace (Lanczos) evolution
    subroutine bob_evolve_krylov(state, H, dt, k)
        type(bob_quantum_state), intent(inout) :: state
        complex(cwp), intent(in) :: H(:,:)
        real(wp), intent(in) :: dt
        integer, intent(in), optional :: k
        integer :: krylov_dim, dim, i, m
        complex(cwp), allocatable :: V(:,:), T(:,:), beta(:), psi_krylov(:), w(:)
        real(wp) :: norm
        dim = state%dim
        krylov_dim = 20; if (present(k)) krylov_dim = min(k, dim)
        krylov_dim = min(krylov_dim, dim)
        allocate(V(dim, krylov_dim), T(krylov_dim, krylov_dim), beta(krylov_dim))
        allocate(psi_krylov(krylov_dim), w(dim))
        V = CZERO; T = CZERO; beta = ZERO
        V(:,1) = state%amplitudes
        norm = sqrt(real(dot_product(conjg(V(:,1)), V(:,1))))
        V(:,1) = V(:,1) / norm
        m = krylov_dim
        do i = 1, krylov_dim
            w = matmul(H, V(:,i))
            T(i,i) = dot_product(conjg(V(:,i)), w)
            w = w - T(i,i) * V(:,i)
            if (i > 1) w = w - beta(i-1) * V(:,i-1)
            beta(i) = sqrt(real(dot_product(conjg(w), w)))
            if (beta(i) < TOL_NORM .or. i == krylov_dim) then
                m = i; exit
            end if
            V(:,i+1) = w / beta(i)
            T(i,i+1) = beta(i); T(i+1,i) = beta(i)
        end do
        psi_krylov = CZERO; psi_krylov(1) = CONE
        psi_krylov(1:m) = matmul(bob_time_evolution_operator(T(1:m,1:m), dt), psi_krylov(1:m))
        state%amplitudes = matmul(V(:,1:m), psi_krylov(1:m))
        state%is_normalized = .true.
        call bob_clear_error()
    end subroutine bob_evolve_krylov

    !> 2nd-order Trotter-Suzuki decomposition
    subroutine bob_evolve_trotter(state, H, dt, order)
        type(bob_quantum_state), intent(inout) :: state
        complex(cwp), intent(in) :: H(:,:)
        real(wp), intent(in) :: dt
        integer, intent(in), optional :: order
        integer :: ord
        complex(cwp), allocatable :: U(:,:), psi_new(:)
        ord = 2; if (present(order)) ord = order
        if (ord == 1) then
            U = bob_time_evolution_operator(H, dt)
        else
            U = matmul(bob_time_evolution_operator(H, dt/2), bob_time_evolution_operator(H, dt/2))
        end if
        allocate(psi_new(state%dim))
        psi_new = matmul(U, state%amplitudes)
        state%amplitudes = psi_new; state%is_normalized = .true.
        call bob_clear_error()
    end subroutine bob_evolve_trotter

    !> Magnus expansion (2nd order)
    subroutine bob_evolve_magnus(state, H1, H2, dt)
        type(bob_quantum_state), intent(inout) :: state
        complex(cwp), intent(in) :: H1(:,:), H2(:,:)
        real(wp), intent(in) :: dt
        complex(cwp), allocatable :: Omega(:,:), U(:,:)
        integer :: dim
        dim = state%dim
        allocate(Omega(dim,dim), U(dim,dim))
        Omega = -CI * dt * (H1 + H2) / 2.0_wp
        U = bob_time_evolution_operator(Omega/(-CI*dt), dt)
        state%amplitudes = matmul(U, state%amplitudes)
        call state%normalize()
        call bob_clear_error()
    end subroutine bob_evolve_magnus

    subroutine bob_evolve_rk4(state, H, dt)
        type(bob_quantum_state), intent(inout) :: state
        complex(cwp), intent(in) :: H(:,:)
        real(wp), intent(in) :: dt
        complex(cwp), allocatable :: k1(:), k2(:), k3(:), k4(:), psi_temp(:)
        integer(i8) :: dim
        dim = state%dim
        allocate(k1(dim), k2(dim), k3(dim), k4(dim), psi_temp(dim))
        k1 = -CI * matmul(H, state%amplitudes)
        psi_temp = state%amplitudes + dt/2 * k1; k2 = -CI * matmul(H, psi_temp)
        psi_temp = state%amplitudes + dt/2 * k2; k3 = -CI * matmul(H, psi_temp)
        psi_temp = state%amplitudes + dt * k3;   k4 = -CI * matmul(H, psi_temp)
        state%amplitudes = state%amplitudes + dt/6 * (k1 + 2*k2 + 2*k3 + k4)
        call state%normalize()
        call bob_clear_error()
    end subroutine bob_evolve_rk4

    subroutine int_init(this, method, dt, name)
        class(bob_time_integrator), intent(inout) :: this
        integer(i4), intent(in) :: method
        real(wp), intent(in) :: dt
        character(*), intent(in) :: name
        this%method = method; this%dt = dt; this%name = name
    end subroutine int_init

    subroutine int_step(this, state, H)
        class(bob_time_integrator), intent(inout) :: this
        type(bob_quantum_state), intent(inout) :: state
        type(bob_hamiltonian_operator), intent(inout) :: H
        select case (this%method)
        case (INTEGRATOR_EXACT)
            call bob_evolve_exact(state, H%matrix, this%dt)
        case (INTEGRATOR_TROTTER)
            call bob_evolve_trotter(state, H%matrix, this%dt, this%trotter_order)
        case (INTEGRATOR_KRYLOV)
            call bob_evolve_krylov(state, H%matrix, this%dt, this%krylov_dim)
        case (INTEGRATOR_RK4)
            call bob_evolve_rk4(state, H%matrix, this%dt)
        case (INTEGRATOR_MAGNUS)
            call bob_evolve_exact(state, H%matrix, this%dt)
        case default
            call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, "Unknown integrator", "int_step")
        end select
    end subroutine int_step

end module bob_evolution