|
|
|
|
|
|
|
|
|
|
| module bob_measurement
|
| use bob_kinds
|
| use bob_errors
|
| use bob_state
|
| use bob_rng
|
| implicit none
|
| private
|
|
|
|
|
| type, public :: bob_measurement_result
|
| integer(i8) :: num_qubits
|
| integer(i8), allocatable :: outcomes(:)
|
| real(wp), allocatable :: probabilities(:)
|
| integer(i8) :: num_shots
|
| integer(i8), allocatable :: counts(:)
|
| real(wp) :: measurement_time
|
| logical(lk) :: collapsed
|
| contains
|
| procedure :: init => measurement_result_init
|
| procedure :: destroy => measurement_result_destroy
|
| procedure :: get_outcome => measurement_result_get_outcome
|
| procedure :: get_probability => measurement_result_get_probability
|
| procedure :: get_count => measurement_result_get_count
|
| end type bob_measurement_result
|
|
|
| public :: measure_state
|
| public :: measure_qubit
|
| public :: measure_basis
|
| public :: measure_shots
|
| public :: calculate_probabilities
|
| public :: collapse_state
|
|
|
| contains
|
|
|
|
|
| subroutine measurement_result_init(this, num_qubits, num_shots)
|
| class(bob_measurement_result), intent(inout) :: this
|
| integer(i8), intent(in) :: num_qubits, num_shots
|
| integer :: stat
|
| integer(i8) :: num_outcomes
|
|
|
| this%num_qubits = num_qubits
|
| this%num_shots = num_shots
|
| this%collapsed = .false.
|
| this%measurement_time = ZERO
|
|
|
|
|
| num_outcomes = ishft(1_i8, int(num_qubits))
|
|
|
|
|
| if (allocated(this%outcomes)) deallocate(this%outcomes)
|
| if (allocated(this%probabilities)) deallocate(this%probabilities)
|
| if (allocated(this%counts)) deallocate(this%counts)
|
|
|
| allocate(this%outcomes(num_outcomes), stat=stat)
|
| if (stat /= 0) then
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate outcomes", "measurement_result_init")
|
| return
|
| end if
|
|
|
| allocate(this%probabilities(num_outcomes), stat=stat)
|
| if (stat /= 0) then
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate probabilities", "measurement_result_init")
|
| return
|
| end if
|
|
|
| allocate(this%counts(num_outcomes), stat=stat)
|
| if (stat /= 0) then
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate counts", "measurement_result_init")
|
| return
|
| end if
|
|
|
|
|
| this%outcomes = 0
|
| this%probabilities = ZERO
|
| this%counts = 0
|
|
|
| call bob_clear_error()
|
| end subroutine measurement_result_init
|
|
|
|
|
| subroutine measurement_result_destroy(this)
|
| class(bob_measurement_result), intent(inout) :: this
|
|
|
| if (allocated(this%outcomes)) deallocate(this%outcomes)
|
| if (allocated(this%probabilities)) deallocate(this%probabilities)
|
| if (allocated(this%counts)) deallocate(this%counts)
|
|
|
| this%num_qubits = 0
|
| this%num_shots = 0
|
| end subroutine measurement_result_destroy
|
|
|
|
|
| function measurement_result_get_outcome(this, index) result(outcome)
|
| class(bob_measurement_result), intent(in) :: this
|
| integer(i8), intent(in) :: index
|
| integer(i8) :: outcome
|
|
|
| if (index < 1 .or. index > size(this%outcomes, kind=i8)) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Outcome index out of range", "measurement_result_get_outcome")
|
| outcome = 0
|
| return
|
| end if
|
|
|
| outcome = this%outcomes(index)
|
| call bob_clear_error()
|
| end function measurement_result_get_outcome
|
|
|
|
|
| function measurement_result_get_probability(this, index) result(prob)
|
| class(bob_measurement_result), intent(in) :: this
|
| integer(i8), intent(in) :: index
|
| real(wp) :: prob
|
|
|
| if (index < 1 .or. index > size(this%probabilities, kind=i8)) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Probability index out of range", "measurement_result_get_probability")
|
| prob = ZERO
|
| return
|
| end if
|
|
|
| prob = this%probabilities(index)
|
| call bob_clear_error()
|
| end function measurement_result_get_probability
|
|
|
|
|
| function measurement_result_get_count(this, index) result(count)
|
| class(bob_measurement_result), intent(in) :: this
|
| integer(i8), intent(in) :: index
|
| integer(i8) :: count
|
|
|
| if (index < 1 .or. index > size(this%counts, kind=i8)) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Count index out of range", "measurement_result_get_count")
|
| count = 0
|
| return
|
| end if
|
|
|
| count = this%counts(index)
|
| call bob_clear_error()
|
| end function measurement_result_get_count
|
|
|
|
|
| subroutine measure_state(state, rng, result, collapse)
|
| type(bob_quantum_state), intent(inout) :: state
|
| type(bob_rng_state), intent(inout) :: rng
|
| type(bob_measurement_result), intent(out) :: result
|
| logical(lk), intent(in), optional :: collapse
|
|
|
| integer(i8) :: num_qubits, i
|
| real(wp) :: cumulative_prob, random_val
|
| integer(i8) :: measured_outcome
|
| logical(lk) :: do_collapse
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot measure invalid state", "measure_state")
|
| return
|
| end if
|
|
|
| do_collapse = .true.
|
| if (present(collapse)) do_collapse = collapse
|
|
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
|
|
| call result%init(num_qubits, 1_i8)
|
|
|
|
|
| call calculate_probabilities(state, result)
|
|
|
|
|
| random_val = rng%uniform()
|
| cumulative_prob = ZERO
|
| measured_outcome = 0
|
|
|
| do i = 1, state%dim
|
| cumulative_prob = cumulative_prob + result%probabilities(i)
|
| if (random_val <= cumulative_prob) then
|
| measured_outcome = i - 1
|
| exit
|
| end if
|
| end do
|
|
|
|
|
| result%outcomes(measured_outcome + 1) = measured_outcome
|
| result%counts(measured_outcome + 1) = 1
|
| result%collapsed = do_collapse
|
|
|
|
|
| if (do_collapse) then
|
| call collapse_state(state, measured_outcome)
|
| end if
|
|
|
| call bob_clear_error()
|
| end subroutine measure_state
|
|
|
|
|
| subroutine measure_qubit(state, qubit_index, rng, result, collapse)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i8), intent(in) :: qubit_index
|
| type(bob_rng_state), intent(inout) :: rng
|
| integer(i8), intent(out) :: result
|
| logical(lk), intent(in), optional :: collapse
|
|
|
| integer(i8) :: num_qubits, i, bit_mask, qubit_bit
|
| real(wp) :: prob_0, prob_1, random_val
|
| logical(lk) :: do_collapse
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot measure invalid state", "measure_qubit")
|
| result = 0
|
| 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", "measure_qubit")
|
| result = 0
|
| return
|
| end if
|
|
|
| do_collapse = .true.
|
| if (present(collapse)) do_collapse = collapse
|
|
|
|
|
| bit_mask = ishft(1_i8, int(qubit_index))
|
| prob_0 = ZERO
|
| prob_1 = ZERO
|
|
|
| do i = 0, state%dim - 1
|
| qubit_bit = iand(i, bit_mask)
|
|
|
| if (qubit_bit == 0) then
|
| prob_0 = prob_0 + real(state%amplitudes(i + 1) * conjg(state%amplitudes(i + 1)))
|
| else
|
| prob_1 = prob_1 + real(state%amplitudes(i + 1) * conjg(state%amplitudes(i + 1)))
|
| end if
|
| end do
|
|
|
|
|
| random_val = rng%uniform()
|
|
|
| if (random_val < prob_0) then
|
| result = 0
|
| else
|
| result = 1
|
| end if
|
|
|
|
|
| if (do_collapse) then
|
| call collapse_qubit(state, qubit_index, result)
|
| end if
|
|
|
| call bob_clear_error()
|
| end subroutine measure_qubit
|
|
|
|
|
| subroutine measure_basis(state, basis_vectors, rng, result, collapse)
|
| type(bob_quantum_state), intent(inout) :: state
|
| complex(cwp), intent(in) :: basis_vectors(:,:)
|
| type(bob_rng_state), intent(inout) :: rng
|
| integer(i8), intent(out) :: result
|
| logical(lk), intent(in), optional :: collapse
|
|
|
| integer(i8) :: num_basis, i, j
|
| real(wp), allocatable :: probabilities(:)
|
| real(wp) :: cumulative_prob, random_val
|
| complex(cwp) :: inner_prod
|
| logical(lk) :: do_collapse
|
| integer :: stat
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot measure invalid state", "measure_basis")
|
| result = 0
|
| return
|
| end if
|
|
|
| num_basis = size(basis_vectors, 2, kind=i8)
|
|
|
| if (size(basis_vectors, 1, kind=i8) /= state%dim) then
|
| call bob_set_error(BOB_ERROR_DIMENSION_MISMATCH, &
|
| "Basis vectors dimension mismatch", "measure_basis")
|
| result = 0
|
| return
|
| end if
|
|
|
| do_collapse = .true.
|
| if (present(collapse)) do_collapse = collapse
|
|
|
|
|
| allocate(probabilities(num_basis), stat=stat)
|
| if (stat /= 0) then
|
| call bob_set_error(BOB_ERROR_ALLOCATION, &
|
| "Failed to allocate probabilities", "measure_basis")
|
| result = 0
|
| return
|
| end if
|
|
|
|
|
| do i = 1, num_basis
|
| inner_prod = CZERO
|
|
|
| do j = 1, state%dim
|
| inner_prod = inner_prod + conjg(basis_vectors(j, i)) * state%amplitudes(j)
|
| end do
|
|
|
| probabilities(i) = real(inner_prod * conjg(inner_prod))
|
| end do
|
|
|
|
|
| random_val = rng%uniform()
|
| cumulative_prob = ZERO
|
| result = 0
|
|
|
| do i = 1, num_basis
|
| cumulative_prob = cumulative_prob + probabilities(i)
|
| if (random_val <= cumulative_prob) then
|
| result = i - 1
|
| exit
|
| end if
|
| end do
|
|
|
|
|
| if (do_collapse) then
|
| state%amplitudes = basis_vectors(:, result + 1)
|
| call state%normalize()
|
| end if
|
|
|
| deallocate(probabilities)
|
| call bob_clear_error()
|
| end subroutine measure_basis
|
|
|
|
|
| subroutine measure_shots(state, num_shots, rng, result)
|
| type(bob_quantum_state), intent(in) :: state
|
| integer(i8), intent(in) :: num_shots
|
| type(bob_rng_state), intent(inout) :: rng
|
| type(bob_measurement_result), intent(out) :: result
|
|
|
| integer(i8) :: num_qubits, shot, i
|
| real(wp) :: cumulative_prob, random_val
|
| integer(i8) :: measured_outcome
|
| type(bob_quantum_state) :: temp_state
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot measure invalid state", "measure_shots")
|
| return
|
| end if
|
|
|
| if (num_shots <= 0) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Number of shots must be positive", "measure_shots")
|
| return
|
| end if
|
|
|
| num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
|
|
|
|
|
| call result%init(num_qubits, num_shots)
|
|
|
|
|
| call calculate_probabilities(state, result)
|
|
|
|
|
| do shot = 1, num_shots
|
| random_val = rng%uniform()
|
| cumulative_prob = ZERO
|
| measured_outcome = 0
|
|
|
| do i = 1, state%dim
|
| cumulative_prob = cumulative_prob + result%probabilities(i)
|
| if (random_val <= cumulative_prob) then
|
| measured_outcome = i - 1
|
| exit
|
| end if
|
| end do
|
|
|
|
|
| result%counts(measured_outcome + 1) = result%counts(measured_outcome + 1) + 1
|
| end do
|
|
|
| result%collapsed = .false.
|
| call bob_clear_error()
|
| end subroutine measure_shots
|
|
|
|
|
| subroutine calculate_probabilities(state, result)
|
| type(bob_quantum_state), intent(in) :: state
|
| type(bob_measurement_result), intent(inout) :: result
|
|
|
| integer(i8) :: i
|
| real(wp) :: total_prob
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot calculate probabilities for invalid state", &
|
| "calculate_probabilities")
|
| return
|
| end if
|
|
|
|
|
| total_prob = ZERO
|
| do i = 1, state%dim
|
| result%probabilities(i) = real(state%amplitudes(i) * conjg(state%amplitudes(i)))
|
| total_prob = total_prob + result%probabilities(i)
|
| end do
|
|
|
|
|
| if (abs(total_prob - ONE) > TOL_NORM) then
|
| call bob_set_error(BOB_ERROR_NOT_NORMALIZED, &
|
| "State probabilities do not sum to 1", "calculate_probabilities")
|
| return
|
| end if
|
|
|
| call bob_clear_error()
|
| end subroutine calculate_probabilities
|
|
|
|
|
| subroutine collapse_state(state, outcome)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i8), intent(in) :: outcome
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot collapse invalid state", "collapse_state")
|
| return
|
| end if
|
|
|
| if (outcome < 0 .or. outcome >= state%dim) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Outcome out of range", "collapse_state")
|
| return
|
| end if
|
|
|
|
|
| state%amplitudes = CZERO
|
| state%amplitudes(outcome + 1) = CONE
|
| state%is_normalized = .true.
|
|
|
| call bob_clear_error()
|
| end subroutine collapse_state
|
|
|
|
|
| subroutine collapse_qubit(state, qubit_index, outcome)
|
| type(bob_quantum_state), intent(inout) :: state
|
| integer(i8), intent(in) :: qubit_index, outcome
|
|
|
| integer(i8) :: i, bit_mask, qubit_bit
|
| real(wp) :: norm_factor
|
|
|
| if (.not. state%is_valid) then
|
| call bob_set_error(BOB_ERROR_INVALID_STATE, &
|
| "Cannot collapse invalid state", "collapse_qubit")
|
| return
|
| end if
|
|
|
| if (outcome /= 0 .and. outcome /= 1) then
|
| call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
|
| "Qubit outcome must be 0 or 1", "collapse_qubit")
|
| return
|
| end if
|
|
|
| bit_mask = ishft(1_i8, int(qubit_index))
|
|
|
|
|
| do i = 0, state%dim - 1
|
| qubit_bit = iand(i, bit_mask)
|
|
|
| if ((outcome == 0 .and. qubit_bit /= 0) .or. &
|
| (outcome == 1 .and. qubit_bit == 0)) then
|
| state%amplitudes(i + 1) = CZERO
|
| end if
|
| end do
|
|
|
|
|
| call state%normalize()
|
|
|
| call bob_clear_error()
|
| end subroutine collapse_qubit
|
|
|
|
|
| function bob_state_measure(state_ptr, rng_ptr, outcome) result(status) &
|
| bind(C, name="bob_state_measure")
|
| use, intrinsic :: iso_c_binding
|
| type(c_ptr), value :: state_ptr, rng_ptr
|
| integer(c_int64_t), intent(out) :: outcome
|
| integer(c_int) :: status
|
|
|
| type(bob_quantum_state), pointer :: state
|
| type(bob_rng_state), pointer :: rng
|
| type(bob_measurement_result) :: result
|
|
|
| if (.not. c_associated(state_ptr) .or. .not. c_associated(rng_ptr)) then
|
| status = BOB_ERROR_INVALID_ARGUMENT
|
| return
|
| end if
|
|
|
| call c_f_pointer(state_ptr, state)
|
| call c_f_pointer(rng_ptr, rng)
|
|
|
| call measure_state(state, rng, result, collapse=.true._lk)
|
|
|
| if (bob_get_last_error() == BOB_SUCCESS) then
|
| outcome = result%outcomes(1)
|
| else
|
| outcome = 0
|
| end if
|
|
|
| call result%destroy()
|
| status = bob_get_last_error()
|
| end function bob_state_measure
|
|
|
| end module bob_measurement
|
|
|
|
|
|
|