File size: 11,090 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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | ! BOB Quantum Civilization Engine - Hamiltonian Construction
! Module: bob_hamiltonian
! Purpose: Pauli operators, tensor products, Hamiltonian assembly for quantum simulation
! Standard: Fortran 2018
module bob_hamiltonian
use bob_kinds
use bob_errors
use bob_state
implicit none
private
! Public interface
public :: bob_hamiltonian_operator
public :: bob_pauli_matrix
public :: bob_tensor_product
public :: bob_kron
public :: bob_ising_hamiltonian
public :: bob_heisenberg_hamiltonian
public :: bob_tfim_hamiltonian
public :: bob_xy_hamiltonian
public :: bob_expectation
public :: bob_variance
public :: bob_ground_state
! Pauli matrices (2x2)
complex(cwp), parameter :: PAULI_I(2,2) = reshape([CONE, CZERO, CZERO, CONE], [2,2])
complex(cwp), parameter :: PAULI_X(2,2) = reshape([CZERO, CONE, CONE, CZERO], [2,2])
complex(cwp), parameter :: PAULI_Y(2,2) = reshape([CZERO, -CI, CI, CZERO], [2,2])
complex(cwp), parameter :: PAULI_Z(2,2) = reshape([CONE, CZERO, CZERO, -CONE], [2,2])
type, public :: bob_hamiltonian_operator
integer(i8) :: dim = 0
complex(cwp), allocatable :: matrix(:,:)
integer(i8) :: num_qubits = 0
character(len=:), allocatable :: name
logical(lk) :: is_hermitian = .true.
real(wp) :: energy_offset = ZERO
contains
procedure, public :: init => ham_init
procedure, public :: destroy => ham_destroy
procedure, public :: add_term => ham_add_term
procedure, public :: build_matrix => ham_build_matrix
procedure, public :: expectation => ham_expectation
procedure, public :: ground_state => ham_ground_state
end type bob_hamiltonian_operator
contains
!> Get Pauli matrix by name
pure function bob_pauli_matrix(name) result(mat)
character(*), intent(in) :: name
complex(cwp) :: mat(2,2)
select case (name)
case ('I','i','identity'); mat = PAULI_I
case ('X','x','sigma_x'); mat = PAULI_X
case ('Y','y','sigma_y'); mat = PAULI_Y
case ('Z','z','sigma_z'); mat = PAULI_Z
case default
call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, "Unknown Pauli: "//name, "bob_pauli_matrix")
mat = CZERO
end select
end function bob_pauli_matrix
!> Kronecker product A ⊗ B
pure function bob_kron(A, B) result(C)
complex(cwp), intent(in) :: A(:,:), B(:,:)
complex(cwp), allocatable :: C(:,:)
integer :: m1, n1, m2, n2, i, j, k, l
m1 = size(A,1); n1 = size(A,2); m2 = size(B,1); n2 = size(B,2)
allocate(C(m1*m2, n1*n2)); C = CZERO
do i = 1, m1; do j = 1, n1
if (abs(A(i,j)) > TOL_NORM) then
do k = 1, m2; do l = 1, n2
C((i-1)*m2+k, (j-1)*n2+l) = A(i,j) * B(k,l)
end do; end do
end if
end do; end do
end function bob_kron
!> Tensor product: embed operator on specified qubits into full Hilbert space
function bob_tensor_product(operators, qubits, num_qubits) result(op)
complex(cwp), intent(in) :: operators(:,:,:) ! (2,2,n_ops)
integer(i8), intent(in) :: qubits(:)
integer(i8), intent(in) :: num_qubits
complex(cwp), allocatable :: op(:,:)
integer(i8) :: n_ops, i, j, target
complex(cwp), allocatable :: current(:,:), next(:,:)
n_ops = size(qubits)
if (n_ops == 0) then; allocate(op(1,1)); op = CONE; return; end if
allocate(current(1,1)); current = CONE
do i = 1, num_qubits
target = -1
do j = 1, n_ops; if (qubits(j) == i) then; target = j; exit; end if; end do
if (target > 0) then
next = bob_kron(current, operators(:,:,target))
else
next = bob_kron(current, PAULI_I)
end if
if (allocated(current)) deallocate(current)
allocate(current(size(next,1), size(next,2))); current = next
if (allocated(next)) deallocate(next)
end do
op = current
end function bob_tensor_product
subroutine ham_init(this, num_qubits, name)
class(bob_hamiltonian_operator), intent(inout) :: this
integer(i8), intent(in) :: num_qubits
character(*), intent(in) :: name
integer :: stat
this%num_qubits = num_qubits
this%dim = ishft(1_i8, int(num_qubits))
this%name = name
if (allocated(this%matrix)) deallocate(this%matrix)
allocate(this%matrix(this%dim, this%dim), stat=stat)
if (stat /= 0) call bob_set_error(BOB_ERROR_ALLOCATION, "H matrix alloc", name)
this%matrix = CZERO
end subroutine ham_init
subroutine ham_destroy(this)
class(bob_hamiltonian_operator), intent(inout) :: this
if (allocated(this%matrix)) deallocate(this%matrix)
this%dim = 0; this%num_qubits = 0
end subroutine ham_destroy
subroutine ham_add_term(this, op_matrix, coeff, qubits, term_name)
class(bob_hamiltonian_operator), intent(inout) :: this
complex(cwp), intent(in) :: op_matrix(:,:)
real(wp), intent(in) :: coeff
integer(i8), intent(in) :: qubits(:)
character(*), intent(in), optional :: term_name
complex(cwp), allocatable :: full_op(:,:)
full_op = bob_tensor_product(reshape(op_matrix, [2,2,1]), qubits, this%num_qubits)
this%matrix = this%matrix + coeff * full_op
if (allocated(full_op)) deallocate(full_op)
end subroutine ham_add_term
function ham_build_matrix(this) result(H)
class(bob_hamiltonian_operator), intent(in) :: this
complex(cwp), allocatable :: H(:,:)
allocate(H(this%dim, this%dim)); H = this%matrix
end function ham_build_matrix
function ham_expectation(this, state) result(expval)
class(bob_hamiltonian_operator), intent(in) :: this
type(bob_quantum_state), intent(in) :: state
real(wp) :: expval
complex(cwp), allocatable :: Hpsi(:)
if (.not. state%is_valid .or. state%dim /= this%dim) then
call bob_set_error(BOB_ERROR_DIMENSION_MISMATCH, "State/H dim mismatch", "ham_expectation")
expval = ZERO; return
end if
allocate(Hpsi(state%dim))
Hpsi = matmul(this%matrix, state%amplitudes)
expval = real(dot_product(conjg(state%amplitudes), Hpsi))
end function ham_expectation
function bob_variance(H, state) result(var)
complex(cwp), intent(in) :: H(:,:)
type(bob_quantum_state), intent(in) :: state
real(wp) :: var
complex(cwp), allocatable :: Hpsi(:), H2psi(:)
real(wp) :: e1, e2
allocate(Hpsi(state%dim), H2psi(state%dim))
Hpsi = matmul(H, state%amplitudes)
H2psi = matmul(H, Hpsi)
e1 = real(dot_product(conjg(state%amplitudes), Hpsi))
e2 = real(dot_product(conjg(state%amplitudes), H2psi))
var = e2 - e1*e1
end function bob_variance
!> Ground state via power iteration
function bob_ground_state(H, max_iter, tol) result(ground)
complex(cwp), intent(in) :: H(:,:)
integer, intent(in), optional :: max_iter
real(wp), intent(in), optional :: tol
type(bob_quantum_state) :: ground
integer(i8) :: dim, iter, max_i
real(wp) :: tol_v
complex(cwp), allocatable :: psi(:), psi_new(:)
real(wp) :: norm, overlap
dim = size(H,1)
call ground%init(int(log(real(dim))/log(TWO)))
max_i = 1000; if (present(max_iter)) max_i = max_iter
tol_v = 1.0e-10_wp; if (present(tol)) tol_v = tol
psi = ground%amplitudes
do iter = 1, max_i
psi_new = matmul(H, psi)
norm = sqrt(real(dot_product(conjg(psi_new), psi_new)))
if (norm > ZERO) psi_new = psi_new / norm
overlap = abs(dot_product(conjg(psi), psi_new))
psi = psi_new
if (abs(overlap - ONE) < tol_v) exit
end do
ground%amplitudes = psi; ground%is_normalized = .true.
end function bob_ground_state
!> Transverse Field Ising Model: H = -J Σ Z_i Z_{i+1} - h Σ X_i
function bob_tfim_hamiltonian(num_qubits, J, h) result(H)
integer(i8), intent(in) :: num_qubits
real(wp), intent(in) :: J, h
type(bob_hamiltonian_operator) :: H
integer(i8) :: i
complex(cwp), allocatable :: ZZ(:,:,:)
call H%init(num_qubits, "TFIM")
allocate(ZZ(2,2,2)); ZZ(:,:,1) = PAULI_Z; ZZ(:,:,2) = PAULI_Z
do i = 1, num_qubits - 1
call H%add_term(ZZ, -J, [i, i+1])
end do
do i = 1, num_qubits
call H%add_term(PAULI_X, -h, [i])
end do
end function bob_tfim_hamiltonian
!> Heisenberg Model: H = Σ (Jx X_i X_{i+1} + Jy Y_i Y_{i+1} + Jz Z_i Z_{i+1})
function bob_heisenberg_hamiltonian(num_qubits, Jx, Jy, Jz) result(H)
integer(i8), intent(in) :: num_qubits
real(wp), intent(in) :: Jx, Jy, Jz
type(bob_hamiltonian_operator) :: H
integer(i8) :: i
complex(cwp), allocatable :: XX(:,:,:), YY(:,:,:), ZZ(:,:,:)
call H%init(num_qubits, "Heisenberg")
allocate(XX(2,2,2)); XX(:,:,1)=PAULI_X; XX(:,:,2)=PAULI_X
allocate(YY(2,2,2)); YY(:,:,1)=PAULI_Y; YY(:,:,2)=PAULI_Y
allocate(ZZ(2,2,2)); ZZ(:,:,1)=PAULI_Z; ZZ(:,:,2)=PAULI_Z
do i = 1, num_qubits - 1
call H%add_term(XX, Jx, [i, i+1])
call H%add_term(YY, Jy, [i, i+1])
call H%add_term(ZZ, Jz, [i, i+1])
end do
end function bob_heisenberg_hamiltonian
!> XY Model: H = J Σ (X_i X_{i+1} + Y_i Y_{i+1}) + h Σ Z_i
function bob_xy_hamiltonian(num_qubits, J, h) result(H)
integer(i8), intent(in) :: num_qubits
real(wp), intent(in) :: J, h
type(bob_hamiltonian_operator) :: H
integer(i8) :: i
complex(cwp), allocatable :: XX(:,:,:), YY(:,:,:)
call H%init(num_qubits, "XY")
allocate(XX(2,2,2)); XX(:,:,1)=PAULI_X; XX(:,:,2)=PAULI_X
allocate(YY(2,2,2)); YY(:,:,1)=PAULI_Y; YY(:,:,2)=PAULI_Y
do i = 1, num_qubits - 1
call H%add_term(XX, J, [i, i+1])
call H%add_term(YY, J, [i, i+1])
end do
do i = 1, num_qubits
call H%add_term(PAULI_Z, h, [i])
end do
end function bob_xy_hamiltonian
function ham_ground_state(this, max_iter, tol) result(ground)
class(bob_hamiltonian_operator), intent(in) :: this
integer, intent(in), optional :: max_iter
real(wp), intent(in), optional :: tol
type(bob_quantum_state) :: ground
ground = bob_ground_state(this%matrix, max_iter, tol)
end function ham_ground_state
end module bob_hamiltonian
|