|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|