use crate::error::{QuantumError, Result};
use crate::state::QuantumState;
use moonlab_sys as ffi;
fn check_qubit(state: &QuantumState, q: usize) -> Result<()> {
if q >= state.num_qubits() {
return Err(QuantumError::InvalidQubit {
index: q,
max: state.num_qubits(),
});
}
Ok(())
}
pub fn depolarizing_single(
state: &mut QuantumState,
qubit: usize,
probability: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_depolarizing_single(
state.as_ptr(),
qubit as i32,
probability,
random_value,
);
}
Ok(())
}
pub fn depolarizing_two_qubit(
state: &mut QuantumState,
qubit1: usize,
qubit2: usize,
probability: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit1)?;
check_qubit(state, qubit2)?;
unsafe {
ffi::noise_depolarizing_two_qubit(
state.as_ptr(),
qubit1 as i32,
qubit2 as i32,
probability,
random_value,
);
}
Ok(())
}
pub fn amplitude_damping(
state: &mut QuantumState,
qubit: usize,
gamma: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_amplitude_damping(
state.as_ptr(),
qubit as i32,
gamma,
random_value,
);
}
Ok(())
}
pub fn phase_damping(
state: &mut QuantumState,
qubit: usize,
gamma: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_phase_damping(
state.as_ptr(),
qubit as i32,
gamma,
random_value,
);
}
Ok(())
}
pub fn pure_dephasing(
state: &mut QuantumState,
qubit: usize,
sigma: f64,
random_phase: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_pure_dephasing(
state.as_ptr(),
qubit as i32,
sigma,
random_phase,
);
}
Ok(())
}
pub fn bit_flip(
state: &mut QuantumState,
qubit: usize,
probability: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_bit_flip(
state.as_ptr(),
qubit as i32,
probability,
random_value,
);
}
Ok(())
}
pub fn phase_flip(
state: &mut QuantumState,
qubit: usize,
probability: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_phase_flip(
state.as_ptr(),
qubit as i32,
probability,
random_value,
);
}
Ok(())
}
pub fn bit_phase_flip(
state: &mut QuantumState,
qubit: usize,
probability: f64,
random_value: f64,
) -> Result<()> {
check_qubit(state, qubit)?;
unsafe {
ffi::noise_bit_phase_flip(
state.as_ptr(),
qubit as i32,
probability,
random_value,
);
}
Ok(())
}
pub fn thermal_relaxation(
state: &mut QuantumState,
qubit: usize,
t1: f64,
t2: f64,
time: f64,
random_values: &[f64],
) -> Result<()> {
check_qubit(state, qubit)?;
if random_values.len() < 2 {
return Err(QuantumError::Ffi(format!(
"thermal_relaxation needs at least 2 random_values, got {}",
random_values.len()
)));
}
unsafe {
ffi::noise_thermal_relaxation(
state.as_ptr(),
qubit as i32,
t1,
t2,
time,
random_values.as_ptr(),
);
}
Ok(())
}
pub fn readout_error(
outcome: bool,
error_0_to_1: f64,
error_1_to_0: f64,
random_value: f64,
) -> bool {
let v = unsafe {
ffi::noise_readout_error(
outcome as i32,
error_0_to_1,
error_1_to_0,
random_value,
)
};
v != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn depolarizing_single_zero_probability_is_identity() {
let mut state = QuantumState::new(2).unwrap();
state.h(0);
let p_before = state.probabilities();
depolarizing_single(&mut state, 0, 0.0, 0.5).unwrap();
let p_after = state.probabilities();
for (a, b) in p_before.iter().zip(p_after.iter()) {
assert!((a - b).abs() < 1e-12);
}
}
#[test]
fn bit_flip_full_probability_flips_qubit() {
let mut state = QuantumState::new(1).unwrap();
bit_flip(&mut state, 0, 1.0, 0.0).unwrap();
let p = state.probabilities();
assert!(p[0].abs() < 1e-12);
assert!((p[1] - 1.0).abs() < 1e-12);
}
#[test]
fn phase_flip_full_probability_preserves_basis_probabilities() {
let mut state = QuantumState::new(1).unwrap();
state.h(0);
phase_flip(&mut state, 0, 1.0, 0.0).unwrap();
let p = state.probabilities();
assert!((p[0] - 0.5).abs() < 1e-12);
assert!((p[1] - 0.5).abs() < 1e-12);
}
#[test]
fn amplitude_damping_eventually_collapses_excited_state() {
let mut state = QuantumState::new(1).unwrap();
state.x(0);
amplitude_damping(&mut state, 0, 1.0, 0.5).unwrap();
let p = state.probabilities();
assert!((p[0] - 1.0).abs() < 1e-6);
}
#[test]
fn rejects_out_of_range_qubit() {
let mut state = QuantumState::new(2).unwrap();
assert!(depolarizing_single(&mut state, 5, 0.1, 0.5).is_err());
assert!(bit_flip(&mut state, 5, 0.1, 0.5).is_err());
}
#[test]
fn readout_error_threshold_pins_behaviour() {
assert_eq!(readout_error(false, 0.1, 0.1, 0.05), true);
assert_eq!(readout_error(false, 0.1, 0.1, 0.5), false);
}
#[test]
fn mutual_info_bell_pair_equals_one_bit_per_subsystem() {
let mut state = QuantumState::new(2).unwrap();
state.h(0).cnot(0, 1);
let i_ab = state.mutual_information(&[0], &[1]).unwrap();
assert!(
(i_ab - 2.0).abs() < 1e-6,
"I(A:B) = {} on |Phi+>; expected 2 bits",
i_ab
);
}
}