|
|
|
|
|
|
|
|
|
|
| module bob_gates
|
| use bob_kinds
|
| use bob_errors
|
| use bob_state
|
| implicit none
|
| private
|
|
|
|
|
| integer(i4), parameter, public :: GATE_I = 0
|
| integer(i4), parameter, public :: GATE_X = 1
|
| integer(i4), parameter, public :: GATE_Y = 2
|
| integer(i4), parameter, public :: GATE_Z = 3
|
| integer(i4), parameter, public :: GATE_H = 4
|
| integer(i4), parameter, public :: GATE_S = 5
|
| integer(i4), parameter, public :: GATE_T = 6
|
| integer(i4), parameter, public :: GATE_CNOT = 7
|
| integer(i4), parameter, public :: GATE_CZ = 8
|
| integer(i4), parameter, public :: GATE_SWAP = 9
|
| integer(i4), parameter, public :: GATE_RX = 10
|
| integer(i4), parameter, public :: GATE_RY = 11
|
| integer(i4), parameter, public :: GATE_RZ = 12
|
|
|
|
|
| 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])
|
|
|
| public :: apply_single_qubit_gate
|
| public :: apply_two_qubit_gate
|
| public :: apply_rotation_gate
|
| public :: apply_controlled_gate
|
| public :: apply_arbitrary_unitary
|
| public :: verify_unitary
|
|
|
| contains
|
|
|
|
|
| subroutine apply_single_qubit_gate(state, gate_type, qubit_index)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i4), intent(in) :: gate_type
|
| integer(i8), intent(in) :: qubit_index
|
|
|
| complex(cwp) :: gate_matrix(2,2)
|
| complex(cwp) :: new_amplitudes(state%dim)
|
| integer(i8) :: i, j, k, bit_mask, qubit_bit
|
| integer(i8) :: num_qubits, state_0, state_1
|
| complex(cwp) :: amp_0, amp_1
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot apply gate to invalid state", "apply_single_qubit_gate")
|
| return
|
| end if
|
|
|
|
|
| select case (gate_type)
|
| case (GATE_I)
|
| gate_matrix = PAULI_I
|
| case (GATE_X)
|
| gate_matrix = PAULI_X
|
| case (GATE_Y)
|
| gate_matrix = PAULI_Y
|
| case (GATE_Z)
|
| gate_matrix = PAULI_Z
|
| case (GATE_H)
|
|
|
| gate_matrix(1,1) = CONE / sqrt(TWO)
|
| gate_matrix(1,2) = CONE / sqrt(TWO)
|
| gate_matrix(2,1) = CONE / sqrt(TWO)
|
| gate_matrix(2,2) = -CONE / sqrt(TWO)
|
| case (GATE_S)
|
|
|
| gate_matrix(1,1) = CONE
|
| gate_matrix(1,2) = CZERO
|
| gate_matrix(2,1) = CZERO
|
| gate_matrix(2,2) = CI
|
| case (GATE_T)
|
|
|
| gate_matrix(1,1) = CONE
|
| gate_matrix(1,2) = CZERO
|
| gate_matrix(2,1) = CZERO
|
| gate_matrix(2,2) = exp(CI * PI / 4.0_wp)
|
| case default
|
| call bob_set_error(BOB_ERROR_INVALID_GATE, &
|
| "Unknown gate type", "apply_single_qubit_gate")
|
| return
|
| end select
|
|
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
| if (qubit_index < 0 .or. qubit_index >= num_qubits) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Qubit index out of range", "apply_single_qubit_gate")
|
| return
|
| end if
|
|
|
|
|
| bit_mask = ishft(1_i8, int(qubit_index))
|
|
|
| do i = 0, state%dim - 1
|
| qubit_bit = iand(i, bit_mask)
|
|
|
| if (qubit_bit == 0) then
|
|
|
| state_0 = i
|
| state_1 = ior(i, bit_mask)
|
|
|
| amp_0 = state%amplitudes(state_0 + 1)
|
| amp_1 = state%amplitudes(state_1 + 1)
|
|
|
|
|
| new_amplitudes(state_0 + 1) = gate_matrix(1,1) * amp_0 + gate_matrix(1,2) * amp_1
|
| new_amplitudes(state_1 + 1) = gate_matrix(2,1) * amp_0 + gate_matrix(2,2) * amp_1
|
| end if
|
| end do
|
|
|
| state%amplitudes = new_amplitudes
|
| state%is_normalized = .false.
|
|
|
| call bob_clear_error()
|
| end subroutine apply_single_qubit_gate
|
|
|
|
|
| subroutine apply_two_qubit_gate(state, gate_type, control_qubit, target_qubit)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i4), intent(in) :: gate_type
|
| integer(i8), intent(in) :: control_qubit, target_qubit
|
|
|
| complex(cwp) :: new_amplitudes(state%dim)
|
| integer(i8) :: i, control_mask, target_mask
|
| integer(i8) :: num_qubits, control_bit, target_bit
|
| integer(i8) :: state_00, state_01, state_10, state_11
|
| complex(cwp) :: amp_00, amp_01, amp_10, amp_11
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot apply gate to invalid state", "apply_two_qubit_gate")
|
| return
|
| end if
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
| if (control_qubit < 0 .or. control_qubit >= num_qubits .or. &
|
| target_qubit < 0 .or. target_qubit >= num_qubits .or. &
|
| control_qubit == target_qubit) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Invalid qubit indices", "apply_two_qubit_gate")
|
| return
|
| end if
|
|
|
| control_mask = ishft(1_i8, int(control_qubit))
|
| target_mask = ishft(1_i8, int(target_qubit))
|
|
|
| new_amplitudes = state%amplitudes
|
|
|
| select case (gate_type)
|
| case (GATE_CNOT)
|
|
|
| do i = 0, state%dim - 1
|
| control_bit = iand(i, control_mask)
|
|
|
| if (control_bit /= 0) then
|
|
|
| target_bit = iand(i, target_mask)
|
|
|
| if (target_bit == 0) then
|
| state_01 = i
|
| state_11 = ior(i, target_mask)
|
| else
|
| state_11 = i
|
| state_01 = iand(i, not(target_mask))
|
| end if
|
|
|
|
|
| amp_01 = state%amplitudes(state_01 + 1)
|
| amp_11 = state%amplitudes(state_11 + 1)
|
| new_amplitudes(state_01 + 1) = amp_11
|
| new_amplitudes(state_11 + 1) = amp_01
|
| end if
|
| end do
|
|
|
| case (GATE_CZ)
|
|
|
| do i = 0, state%dim - 1
|
| control_bit = iand(i, control_mask)
|
| target_bit = iand(i, target_mask)
|
|
|
| if (control_bit /= 0 .and. target_bit /= 0) then
|
|
|
| new_amplitudes(i + 1) = -state%amplitudes(i + 1)
|
| end if
|
| end do
|
|
|
| case (GATE_SWAP)
|
|
|
| do i = 0, state%dim - 1
|
| control_bit = iand(i, control_mask)
|
| target_bit = iand(i, target_mask)
|
|
|
|
|
| if (control_bit == 0 .and. target_bit /= 0) then
|
| state_01 = i
|
| state_10 = ior(iand(i, not(target_mask)), control_mask)
|
|
|
| amp_01 = state%amplitudes(state_01 + 1)
|
| amp_10 = state%amplitudes(state_10 + 1)
|
| new_amplitudes(state_01 + 1) = amp_10
|
| new_amplitudes(state_10 + 1) = amp_01
|
| end if
|
| end do
|
|
|
| case default
|
| call bob_set_error(BOB_ERROR_INVALID_GATE, &
|
| "Unknown two-qubit gate type", "apply_two_qubit_gate")
|
| return
|
| end select
|
|
|
| state%amplitudes = new_amplitudes
|
| state%is_normalized = .false.
|
|
|
| call bob_clear_error()
|
| end subroutine apply_two_qubit_gate
|
|
|
|
|
| subroutine apply_rotation_gate(state, gate_type, qubit_index, angle)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i4), intent(in) :: gate_type
|
| integer(i8), intent(in) :: qubit_index
|
| real(wp), intent(in) :: angle
|
|
|
| complex(cwp) :: gate_matrix(2,2)
|
| real(wp) :: half_angle, cos_half, sin_half
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot apply gate to invalid state", "apply_rotation_gate")
|
| return
|
| end if
|
|
|
| half_angle = angle / TWO
|
| cos_half = cos(half_angle)
|
| sin_half = sin(half_angle)
|
|
|
| select case (gate_type)
|
| case (GATE_RX)
|
|
|
| gate_matrix(1,1) = cmplx(cos_half, ZERO, cwp)
|
| gate_matrix(1,2) = cmplx(ZERO, -sin_half, cwp)
|
| gate_matrix(2,1) = cmplx(ZERO, -sin_half, cwp)
|
| gate_matrix(2,2) = cmplx(cos_half, ZERO, cwp)
|
|
|
| case (GATE_RY)
|
|
|
| gate_matrix(1,1) = cmplx(cos_half, ZERO, cwp)
|
| gate_matrix(1,2) = cmplx(-sin_half, ZERO, cwp)
|
| gate_matrix(2,1) = cmplx(sin_half, ZERO, cwp)
|
| gate_matrix(2,2) = cmplx(cos_half, ZERO, cwp)
|
|
|
| case (GATE_RZ)
|
|
|
| gate_matrix(1,1) = exp(-CI * half_angle)
|
| gate_matrix(1,2) = CZERO
|
| gate_matrix(2,1) = CZERO
|
| gate_matrix(2,2) = exp(CI * half_angle)
|
|
|
| case default
|
| call bob_set_error(BOB_ERROR_INVALID_GATE, &
|
| "Unknown rotation gate type", "apply_rotation_gate")
|
| return
|
| end select
|
|
|
|
|
| call apply_arbitrary_unitary(state, gate_matrix, qubit_index)
|
|
|
| call bob_clear_error()
|
| end subroutine apply_rotation_gate
|
|
|
|
|
| subroutine apply_controlled_gate(state, gate_matrix, control_qubit, target_qubit)
|
| type(bob_quantum_state), intent(inout) :: state
|
| complex(cwp), intent(in) :: gate_matrix(2,2)
|
| integer(i8), intent(in) :: control_qubit, target_qubit
|
|
|
| complex(cwp) :: new_amplitudes(state%dim)
|
| integer(i8) :: i, control_mask, target_mask
|
| integer(i8) :: num_qubits, control_bit, target_bit
|
| integer(i8) :: state_0, state_1
|
| complex(cwp) :: amp_0, amp_1
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot apply gate to invalid state", "apply_controlled_gate")
|
| return
|
| end if
|
|
|
|
|
| if (.not. verify_unitary(gate_matrix, 2_i8)) then
|
| call bob_set_error(BOB_ERROR_NOT_UNITARY, &
|
| "Gate matrix is not unitary", "apply_controlled_gate")
|
| return
|
| end if
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
| if (control_qubit < 0 .or. control_qubit >= num_qubits .or. &
|
| target_qubit < 0 .or. target_qubit >= num_qubits .or. &
|
| control_qubit == target_qubit) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Invalid qubit indices", "apply_controlled_gate")
|
| return
|
| end if
|
|
|
| control_mask = ishft(1_i8, int(control_qubit))
|
| target_mask = ishft(1_i8, int(target_qubit))
|
|
|
| new_amplitudes = state%amplitudes
|
|
|
|
|
| do i = 0, state%dim - 1
|
| control_bit = iand(i, control_mask)
|
|
|
| if (control_bit /= 0) then
|
|
|
| target_bit = iand(i, target_mask)
|
|
|
| if (target_bit == 0) then
|
| state_0 = i
|
| state_1 = ior(i, target_mask)
|
|
|
| amp_0 = state%amplitudes(state_0 + 1)
|
| amp_1 = state%amplitudes(state_1 + 1)
|
|
|
| new_amplitudes(state_0 + 1) = gate_matrix(1,1) * amp_0 + gate_matrix(1,2) * amp_1
|
| new_amplitudes(state_1 + 1) = gate_matrix(2,1) * amp_0 + gate_matrix(2,2) * amp_1
|
| end if
|
| end if
|
| end do
|
|
|
| state%amplitudes = new_amplitudes
|
| state%is_normalized = .false.
|
|
|
| call bob_clear_error()
|
| end subroutine apply_controlled_gate
|
|
|
|
|
| subroutine apply_arbitrary_unitary(state, gate_matrix, qubit_index)
|
| type(bob_quantum_state), intent(inout) :: state
|
| complex(cwp), intent(in) :: gate_matrix(2,2)
|
| integer(i8), intent(in) :: qubit_index
|
|
|
| complex(cwp) :: new_amplitudes(state%dim)
|
| integer(i8) :: i, bit_mask, qubit_bit
|
| integer(i8) :: num_qubits, state_0, state_1
|
| complex(cwp) :: amp_0, amp_1
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot apply gate to invalid state", "apply_arbitrary_unitary")
|
| return
|
| end if
|
|
|
|
|
| if (.not. verify_unitary(gate_matrix, 2_i8)) then
|
| call bob_set_error(BOB_ERROR_NOT_UNITARY, &
|
| "Gate matrix is not unitary", "apply_arbitrary_unitary")
|
| return
|
| end if
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
| if (qubit_index < 0 .or. qubit_index >= num_qubits) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Qubit index out of range", "apply_arbitrary_unitary")
|
| return
|
| end if
|
|
|
| bit_mask = ishft(1_i8, int(qubit_index))
|
|
|
| do i = 0, state%dim - 1
|
| qubit_bit = iand(i, bit_mask)
|
|
|
| if (qubit_bit == 0) then
|
| state_0 = i
|
| state_1 = ior(i, bit_mask)
|
|
|
| amp_0 = state%amplitudes(state_0 + 1)
|
| amp_1 = state%amplitudes(state_1 + 1)
|
|
|
| new_amplitudes(state_0 + 1) = gate_matrix(1,1) * amp_0 + gate_matrix(1,2) * amp_1
|
| new_amplitudes(state_1 + 1) = gate_matrix(2,1) * amp_0 + gate_matrix(2,2) * amp_1
|
| end if
|
| end do
|
|
|
| state%amplitudes = new_amplitudes
|
| state%is_normalized = .false.
|
|
|
| call bob_clear_error()
|
| end subroutine apply_arbitrary_unitary
|
|
|
|
|
| function verify_unitary(matrix, dim) result(is_unitary)
|
| integer(i8), intent(in) :: dim
|
| complex(cwp), intent(in) :: matrix(dim, dim)
|
| logical(lk) :: is_unitary
|
|
|
| complex(cwp) :: product(dim, dim)
|
| complex(cwp) :: identity(dim, dim)
|
| integer(i8) :: i, j, k
|
| real(wp) :: max_error
|
|
|
| is_unitary = .false.
|
|
|
|
|
| product = CZERO
|
| do i = 1, dim
|
| do j = 1, dim
|
| do k = 1, dim
|
| product(i,j) = product(i,j) + conjg(matrix(k,i)) * matrix(k,j)
|
| end do
|
| end do
|
| end do
|
|
|
|
|
| identity = CZERO
|
| do i = 1, dim
|
| identity(i,i) = CONE
|
| end do
|
|
|
|
|
| max_error = ZERO
|
| do i = 1, dim
|
| do j = 1, dim
|
| max_error = max(max_error, abs(product(i,j) - identity(i,j)))
|
| end do
|
| end do
|
|
|
| is_unitary = (max_error < TOL_UNITARY)
|
| end function verify_unitary
|
|
|
|
|
| function bob_gate_apply(state_ptr, gate_type, qubit_index) result(status) &
|
| bind(C, name="bob_gate_apply")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: state_ptr
|
| integer(c_int), value :: gate_type
|
| integer(c_int64_t), value :: qubit_index
|
| integer(c_int) :: status
|
|
|
| type(bob_quantum_state), pointer :: state
|
|
|
| if (.not. c_associated(state_ptr)) then
|
| status = BOB_ERROR_INVALID_ARGUMENT
|
| return
|
| end if
|
|
|
| call c_f_pointer(state_ptr, state)
|
| call apply_single_qubit_gate(state, gate_type, qubit_index)
|
| status = bob_get_last_error()
|
| end function bob_gate_apply
|
|
|
| end module bob_gates
|
|
|
|
|
|
|