sov-kernel-monster / src /bob_measurement.f90
SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
19.2 kB
! BOB Quantum Civilization Engine - Quantum Measurement
! Module: bob_measurement
! Purpose: Basis measurement, probability distributions, state collapse
! Standard: Fortran 2018
module bob_measurement
use bob_kinds
use bob_errors
use bob_state
use bob_rng
implicit none
private
!> Measurement result
type, public :: bob_measurement_result
integer(i8) :: num_qubits ! Number of qubits measured
integer(i8), allocatable :: outcomes(:) ! Measurement outcomes (0 or 1)
real(wp), allocatable :: probabilities(:) ! Probability of each outcome
integer(i8) :: num_shots ! Number of measurement shots
integer(i8), allocatable :: counts(:) ! Count of each outcome
real(wp) :: measurement_time ! When measurement occurred
logical(lk) :: collapsed ! Whether state 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
!> Initialize measurement result
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
! Number of possible outcomes: 2^num_qubits
num_outcomes = ishft(1_i8, int(num_qubits))
! Allocate arrays
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
! Initialize to zero
this%outcomes = 0
this%probabilities = ZERO
this%counts = 0
call bob_clear_error()
end subroutine measurement_result_init
!> Destroy measurement result
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
!> Get measurement outcome
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
!> Get outcome probability
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
!> Get outcome count
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
!> Measure entire quantum state
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
! Calculate number of qubits
num_qubits = int(log(real(state%dim, wp)) / log(TWO), i8)
! Initialize result
call result%init(num_qubits, 1_i8)
! Calculate probabilities
call calculate_probabilities(state, result)
! Sample from probability distribution
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
! Store outcome
result%outcomes(measured_outcome + 1) = measured_outcome
result%counts(measured_outcome + 1) = 1
result%collapsed = do_collapse
! Collapse state if requested
if (do_collapse) then
call collapse_state(state, measured_outcome)
end if
call bob_clear_error()
end subroutine measure_state
!> Measure single qubit
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
! Calculate probabilities for |0⟩ and |1⟩
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
! Sample measurement outcome
random_val = rng%uniform()
if (random_val < prob_0) then
result = 0
else
result = 1
end if
! Collapse state if requested
if (do_collapse) then
call collapse_qubit(state, qubit_index, result)
end if
call bob_clear_error()
end subroutine measure_qubit
!> Measure in arbitrary basis
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
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
! Calculate probabilities: P(i) = |⟨basis_i|ψ⟩|²
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
! Sample from probability distribution
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
! Collapse to measured basis state if requested
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
!> Perform multiple measurement shots
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)
! Initialize result
call result%init(num_qubits, num_shots)
! Calculate probabilities (once)
call calculate_probabilities(state, result)
! Perform shots
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
! Increment count for this outcome
result%counts(measured_outcome + 1) = result%counts(measured_outcome + 1) + 1
end do
result%collapsed = .false.
call bob_clear_error()
end subroutine measure_shots
!> Calculate measurement probabilities
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
! Calculate P(i) = |ψᵢ|²
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
! Verify normalization
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
!> Collapse state to measured outcome
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
! Set all amplitudes to zero except measured outcome
state%amplitudes = CZERO
state%amplitudes(outcome + 1) = CONE
state%is_normalized = .true.
call bob_clear_error()
end subroutine collapse_state
!> Collapse single qubit
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))
! Zero out amplitudes inconsistent with measurement
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
! Renormalize
call state%normalize()
call bob_clear_error()
end subroutine collapse_qubit
!> C ABI: Measure state
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
! Made with Bob