|
|
|
|
|
|
|
|
|
|
| module bob_integrator
|
| use bob_kinds
|
| use bob_errors
|
| use bob_state
|
| use bob_hamiltonian
|
| implicit none
|
| private
|
|
|
|
|
| integer(i4), parameter, public :: INTEGRATOR_EULER = 1
|
| integer(i4), parameter, public :: INTEGRATOR_RK2 = 2
|
| integer(i4), parameter, public :: INTEGRATOR_RK4 = 3
|
| integer(i4), parameter, public :: INTEGRATOR_EXPM = 4
|
| integer(i4), parameter, public :: INTEGRATOR_TROTTER = 5
|
|
|
|
|
| type, public :: bob_time_integrator
|
| integer(i4) :: method
|
| real(wp) :: dt
|
| real(wp) :: time
|
| integer(i8) :: steps_taken
|
| real(wp) :: error_estimate
|
| logical(lk) :: adaptive
|
| real(wp) :: tolerance
|
| character(len=64) :: label
|
| contains
|
| procedure :: init => integrator_init
|
| procedure :: step => integrator_step
|
| procedure :: evolve => integrator_evolve
|
| procedure :: reset => integrator_reset
|
| end type bob_time_integrator
|
|
|
| public :: bob_integrator_create
|
| public :: bob_integrator_destroy
|
| public :: bob_integrator_evolve
|
|
|
| contains
|
|
|
|
|
| subroutine integrator_init(this, method, dt, label)
|
| class(bob_time_integrator), intent(inout) :: this
|
| integer(i4), intent(in) :: method
|
| real(wp), intent(in) :: dt
|
| character(len=*), intent(in), optional :: label
|
|
|
| if (dt <= ZERO) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Time step must be positive", "integrator_init")
|
| return
|
| end if
|
|
|
| this%method = method
|
| this%dt = dt
|
| this%time = ZERO
|
| this%steps_taken = 0
|
| this%error_estimate = ZERO
|
| this%adaptive = .false.
|
| this%tolerance = 1.0e-8_wp
|
|
|
| if (present(label)) then
|
| this%label = trim(label)
|
| else
|
| this%label = "unnamed_integrator"
|
| end if
|
|
|
| call bob_clear_error()
|
| end subroutine integrator_init
|
|
|
|
|
| subroutine integrator_step(this, state, hamiltonian)
|
| class(bob_time_integrator), intent(inout) :: this
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
|
|
| select case (this%method)
|
| case (INTEGRATOR_EULER)
|
| call step_euler(state, hamiltonian, this%dt)
|
| case (INTEGRATOR_RK2)
|
| call step_rk2(state, hamiltonian, this%dt)
|
| case (INTEGRATOR_RK4)
|
| call step_rk4(state, hamiltonian, this%dt)
|
| case (INTEGRATOR_EXPM)
|
| call step_expm(state, hamiltonian, this%dt)
|
| case (INTEGRATOR_TROTTER)
|
| call step_trotter(state, hamiltonian, this%dt)
|
| case default
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Unknown integration method", "integrator_step")
|
| return
|
| end select
|
|
|
| this%time = this%time + this%dt
|
| this%steps_taken = this%steps_taken + 1
|
|
|
| call bob_clear_error()
|
| end subroutine integrator_step
|
|
|
|
|
| subroutine integrator_evolve(this, state, hamiltonian, total_time)
|
| class(bob_time_integrator), intent(inout) :: this
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: total_time
|
|
|
| integer(i8) :: num_steps, step
|
| real(wp) :: remaining_time
|
|
|
| if (total_time <= ZERO) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Total time must be positive", "integrator_evolve")
|
| return
|
| end if
|
|
|
| num_steps = int(total_time / this%dt, i8)
|
| remaining_time = total_time - real(num_steps, wp) * this%dt
|
|
|
|
|
| do step = 1, num_steps
|
| call this%step(state, hamiltonian)
|
|
|
| if (bob_get_last_error() /= BOB_SUCCESS) return
|
|
|
|
|
| if (mod(step, 100_i8) == 0) then
|
| call state%normalize()
|
| end if
|
| end do
|
|
|
|
|
| if (remaining_time > TOL_NORM) then
|
| select case (this%method)
|
| case (INTEGRATOR_EULER)
|
| call step_euler(state, hamiltonian, remaining_time)
|
| case (INTEGRATOR_RK2)
|
| call step_rk2(state, hamiltonian, remaining_time)
|
| case (INTEGRATOR_RK4)
|
| call step_rk4(state, hamiltonian, remaining_time)
|
| case (INTEGRATOR_EXPM)
|
| call step_expm(state, hamiltonian, remaining_time)
|
| case (INTEGRATOR_TROTTER)
|
| call step_trotter(state, hamiltonian, remaining_time)
|
| end select
|
|
|
| this%time = this%time + remaining_time
|
| end if
|
|
|
|
|
| call state%normalize()
|
|
|
| call bob_clear_error()
|
| end subroutine integrator_evolve
|
|
|
|
|
| subroutine integrator_reset(this)
|
| class(bob_time_integrator), intent(inout) :: this
|
|
|
| this%time = ZERO
|
| this%steps_taken = 0
|
| this%error_estimate = ZERO
|
| end subroutine integrator_reset
|
|
|
|
|
| subroutine step_euler(state, hamiltonian, dt)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: dt
|
|
|
| type(bob_quantum_state) :: h_psi
|
| integer(i8) :: i
|
|
|
|
|
| call hamiltonian%apply_to_state(state, h_psi)
|
|
|
| if (bob_get_last_error() /= BOB_SUCCESS) return
|
|
|
|
|
| do i = 1, state%dim
|
| state%amplitudes(i) = state%amplitudes(i) - CI * dt * h_psi%amplitudes(i)
|
| end do
|
|
|
| call h_psi%deallocate()
|
| state%is_normalized = .false.
|
| end subroutine step_euler
|
|
|
|
|
| subroutine step_rk2(state, hamiltonian, dt)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: dt
|
|
|
| type(bob_quantum_state) :: k1, k2, temp_state
|
| integer(i8) :: i
|
|
|
|
|
| call hamiltonian%apply_to_state(state, k1)
|
| if (bob_get_last_error() /= BOB_SUCCESS) return
|
|
|
| do i = 1, k1%dim
|
| k1%amplitudes(i) = -CI * k1%amplitudes(i)
|
| end do
|
|
|
|
|
| call temp_state%allocate(state%dim, "temp")
|
| do i = 1, state%dim
|
| temp_state%amplitudes(i) = state%amplitudes(i) + (dt / TWO) * k1%amplitudes(i)
|
| end do
|
| temp_state%is_valid = .true.
|
|
|
|
|
| call hamiltonian%apply_to_state(temp_state, k2)
|
| if (bob_get_last_error() /= BOB_SUCCESS) then
|
| call k1%deallocate()
|
| call temp_state%deallocate()
|
| return
|
| end if
|
|
|
| do i = 1, k2%dim
|
| k2%amplitudes(i) = -CI * k2%amplitudes(i)
|
| end do
|
|
|
|
|
| do i = 1, state%dim
|
| state%amplitudes(i) = state%amplitudes(i) + dt * k2%amplitudes(i)
|
| end do
|
|
|
| call k1%deallocate()
|
| call k2%deallocate()
|
| call temp_state%deallocate()
|
| state%is_normalized = .false.
|
| end subroutine step_rk2
|
|
|
|
|
| subroutine step_rk4(state, hamiltonian, dt)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: dt
|
|
|
| type(bob_quantum_state) :: k1, k2, k3, k4, temp_state
|
| integer(i8) :: i
|
|
|
|
|
| call hamiltonian%apply_to_state(state, k1)
|
| if (bob_get_last_error() /= BOB_SUCCESS) return
|
| do i = 1, k1%dim
|
| k1%amplitudes(i) = -CI * k1%amplitudes(i)
|
| end do
|
|
|
|
|
| call temp_state%allocate(state%dim, "temp")
|
| do i = 1, state%dim
|
| temp_state%amplitudes(i) = state%amplitudes(i) + (dt / TWO) * k1%amplitudes(i)
|
| end do
|
| temp_state%is_valid = .true.
|
|
|
|
|
| call hamiltonian%apply_to_state(temp_state, k2)
|
| if (bob_get_last_error() /= BOB_SUCCESS) goto 999
|
| do i = 1, k2%dim
|
| k2%amplitudes(i) = -CI * k2%amplitudes(i)
|
| end do
|
|
|
|
|
| do i = 1, state%dim
|
| temp_state%amplitudes(i) = state%amplitudes(i) + (dt / TWO) * k2%amplitudes(i)
|
| end do
|
|
|
|
|
| call hamiltonian%apply_to_state(temp_state, k3)
|
| if (bob_get_last_error() /= BOB_SUCCESS) goto 999
|
| do i = 1, k3%dim
|
| k3%amplitudes(i) = -CI * k3%amplitudes(i)
|
| end do
|
|
|
|
|
| do i = 1, state%dim
|
| temp_state%amplitudes(i) = state%amplitudes(i) + dt * k3%amplitudes(i)
|
| end do
|
|
|
|
|
| call hamiltonian%apply_to_state(temp_state, k4)
|
| if (bob_get_last_error() /= BOB_SUCCESS) goto 999
|
| do i = 1, k4%dim
|
| k4%amplitudes(i) = -CI * k4%amplitudes(i)
|
| end do
|
|
|
|
|
| do i = 1, state%dim
|
| state%amplitudes(i) = state%amplitudes(i) + &
|
| (dt / 6.0_wp) * (k1%amplitudes(i) + TWO * k2%amplitudes(i) + &
|
| TWO * k3%amplitudes(i) + k4%amplitudes(i))
|
| end do
|
|
|
| 999 continue
|
| call k1%deallocate()
|
| call k2%deallocate()
|
| call k3%deallocate()
|
| call k4%deallocate()
|
| call temp_state%deallocate()
|
| state%is_normalized = .false.
|
| end subroutine step_rk4
|
|
|
|
|
| subroutine step_expm(state, hamiltonian, dt)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: dt
|
|
|
| complex(cwp), allocatable :: exp_matrix(:,:)
|
| complex(cwp), allocatable :: new_amplitudes(:)
|
| integer(i8) :: i, j, k
|
| integer :: stat
|
| real(wp) :: factorial
|
| complex(cwp) :: term_coeff
|
| integer, parameter :: MAX_TERMS = 20
|
|
|
|
|
| allocate(exp_matrix(state%dim, state%dim), stat=stat)
|
| if (stat /= 0) then
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate exponential matrix", "step_expm")
|
| return
|
| end if
|
|
|
| allocate(new_amplitudes(state%dim), stat=stat)
|
| if (stat /= 0) then
|
| deallocate(exp_matrix)
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate new amplitudes", "step_expm")
|
| return
|
| end if
|
|
|
|
|
|
|
|
|
|
|
| exp_matrix = CZERO
|
| do i = 1, state%dim
|
| exp_matrix(i,i) = CONE
|
| end do
|
|
|
|
|
| factorial = ONE
|
| do k = 1, MAX_TERMS
|
| factorial = factorial * real(k, wp)
|
| term_coeff = (-CI * dt) ** k / factorial
|
|
|
|
|
|
|
| do i = 1, state%dim
|
| do j = 1, state%dim
|
| exp_matrix(i,j) = exp_matrix(i,j) + &
|
| term_coeff * hamiltonian%matrix(i,j)
|
| end do
|
| end do
|
| end do
|
|
|
|
|
| new_amplitudes = CZERO
|
| do i = 1, state%dim
|
| do j = 1, state%dim
|
| new_amplitudes(i) = new_amplitudes(i) + &
|
| exp_matrix(i,j) * state%amplitudes(j)
|
| end do
|
| end do
|
|
|
| state%amplitudes = new_amplitudes
|
|
|
| deallocate(exp_matrix, new_amplitudes)
|
| state%is_normalized = .false.
|
| end subroutine step_expm
|
|
|
|
|
| subroutine step_trotter(state, hamiltonian, dt)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_hamiltonian_operator), intent(in) :: hamiltonian
|
| real(wp), intent(in) :: dt
|
|
|
| integer(i8) :: i
|
| complex(cwp) :: phase_factor
|
|
|
|
|
|
|
|
|
| do i = 1, state%dim
|
| phase_factor = exp(-CI * hamiltonian%matrix(i,i) * dt)
|
| state%amplitudes(i) = state%amplitudes(i) * phase_factor
|
| end do
|
|
|
|
|
|
|
|
|
| state%is_normalized = .false.
|
| end subroutine step_trotter
|
|
|
|
|
| function bob_integrator_create(method, dt) result(int_ptr) &
|
| bind(C, name="bob_integrator_create")
|
| use, intrinsic :: iso_c_binding
|
| integer(c_int), value :: method
|
| real(c_double), value :: dt
|
| type(c_ptr) :: int_ptr
|
|
|
| type(bob_time_integrator), pointer :: integrator
|
|
|
| allocate(integrator)
|
| call integrator%init(method, dt)
|
| int_ptr = c_loc(integrator)
|
| end function bob_integrator_create
|
|
|
|
|
| subroutine bob_integrator_destroy(int_ptr) bind(C, name="bob_integrator_destroy")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: int_ptr
|
| type(bob_time_integrator), pointer :: integrator
|
|
|
| if (.not. c_associated(int_ptr)) return
|
|
|
| call c_f_pointer(int_ptr, integrator)
|
| deallocate(integrator)
|
| end subroutine bob_integrator_destroy
|
|
|
|
|
| function bob_integrator_evolve(int_ptr, state_ptr, ham_ptr, total_time) result(status) &
|
| bind(C, name="bob_integrator_evolve")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: int_ptr, state_ptr, ham_ptr
|
| real(c_double), value :: total_time
|
| integer(c_int) :: status
|
|
|
| type(bob_time_integrator), pointer :: integrator
|
| type(bob_quantum_state), pointer :: state
|
| type(bob_hamiltonian_operator), pointer :: hamiltonian
|
|
|
| if (.not. c_associated(int_ptr) .or. &
|
| .not. c_associated(state_ptr) .or. &
|
| .not. c_associated(ham_ptr)) then
|
| status = BOB_ERROR_INVALID_ARGUMENT
|
| return
|
| end if
|
|
|
| call c_f_pointer(int_ptr, integrator)
|
| call c_f_pointer(state_ptr, state)
|
| call c_f_pointer(ham_ptr, hamiltonian)
|
|
|
| call integrator%evolve(state, hamiltonian, total_time)
|
| status = bob_get_last_error()
|
| end function bob_integrator_evolve
|
|
|
| end module bob_integrator
|
|
|
|
|
|
|