use crate::error::{QuantumError, Result};
use moonlab_sys::{
moonlab_mpdo_apply_amplitude_damping_1q, moonlab_mpdo_apply_bit_flip_1q,
moonlab_mpdo_apply_bit_phase_flip_1q, moonlab_mpdo_apply_depolarizing_1q,
moonlab_mpdo_apply_phase_damping_1q, moonlab_mpdo_apply_phase_flip_1q,
moonlab_mpdo_clone, moonlab_mpdo_create, moonlab_mpdo_current_bond_dim,
moonlab_mpdo_expect_pauli_1q, moonlab_mpdo_free, moonlab_mpdo_max_bond_dim,
moonlab_mpdo_num_qubits, moonlab_mpdo_t, moonlab_mpdo_trace,
};
use std::ptr;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum PauliCode {
I = 0,
X = 1,
Y = 2,
Z = 3,
}
pub struct Mpdo {
handle: *mut moonlab_mpdo_t,
}
unsafe impl Send for Mpdo {}
impl Mpdo {
pub fn new(num_qubits: u32, max_bond_dim: u32) -> Result<Self> {
if num_qubits == 0 {
return Err(QuantumError::InvalidQubit { index: 0, max: 1 });
}
let handle = unsafe { moonlab_mpdo_create(num_qubits, max_bond_dim) };
if handle.is_null() {
return Err(QuantumError::AllocationFailed(num_qubits as usize));
}
Ok(Self { handle })
}
pub fn num_qubits(&self) -> u32 {
unsafe { moonlab_mpdo_num_qubits(self.handle) }
}
pub fn max_bond_dim(&self) -> u32 {
unsafe { moonlab_mpdo_max_bond_dim(self.handle) }
}
pub fn current_bond_dim(&self) -> u32 {
unsafe { moonlab_mpdo_current_bond_dim(self.handle) }
}
pub fn trace(&self) -> f64 {
unsafe { moonlab_mpdo_trace(self.handle) }
}
pub fn apply_depolarizing(&mut self, qubit: u32, p: f64) -> Result<()> {
check_rc(unsafe { moonlab_mpdo_apply_depolarizing_1q(self.handle, qubit, p) })
}
pub fn apply_amplitude_damping(&mut self, qubit: u32, gamma: f64) -> Result<()> {
check_rc(unsafe {
moonlab_mpdo_apply_amplitude_damping_1q(self.handle, qubit, gamma)
})
}
pub fn apply_phase_damping(&mut self, qubit: u32, lambda: f64) -> Result<()> {
check_rc(unsafe {
moonlab_mpdo_apply_phase_damping_1q(self.handle, qubit, lambda)
})
}
pub fn apply_bit_flip(&mut self, qubit: u32, p: f64) -> Result<()> {
check_rc(unsafe { moonlab_mpdo_apply_bit_flip_1q(self.handle, qubit, p) })
}
pub fn apply_phase_flip(&mut self, qubit: u32, p: f64) -> Result<()> {
check_rc(unsafe { moonlab_mpdo_apply_phase_flip_1q(self.handle, qubit, p) })
}
pub fn apply_bit_phase_flip(&mut self, qubit: u32, p: f64) -> Result<()> {
check_rc(unsafe {
moonlab_mpdo_apply_bit_phase_flip_1q(self.handle, qubit, p)
})
}
pub fn expect_pauli(&self, qubit: u32, pauli: PauliCode) -> Result<f64> {
let mut out: f64 = 0.0;
let rc = unsafe {
moonlab_mpdo_expect_pauli_1q(self.handle, qubit, pauli as u8, &mut out as *mut f64)
};
check_rc(rc)?;
Ok(out)
}
}
impl Clone for Mpdo {
fn clone(&self) -> Self {
let handle = unsafe { moonlab_mpdo_clone(self.handle) };
if handle.is_null() {
panic!("moonlab_mpdo_clone failed (out of memory)");
}
Self { handle }
}
}
impl Drop for Mpdo {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { moonlab_mpdo_free(self.handle) };
self.handle = ptr::null_mut();
}
}
}
fn check_rc(rc: moonlab_sys::mpdo_error_t) -> Result<()> {
if rc == moonlab_sys::mpdo_error_t_MPDO_SUCCESS {
return Ok(());
}
let label = match rc {
x if x == moonlab_sys::mpdo_error_t_MPDO_ERR_INVALID => "invalid argument",
x if x == moonlab_sys::mpdo_error_t_MPDO_ERR_QUBIT => "qubit out of range",
x if x == moonlab_sys::mpdo_error_t_MPDO_ERR_OOM => "out of memory",
x if x == moonlab_sys::mpdo_error_t_MPDO_ERR_BACKEND => "backend failure",
_ => "unknown",
};
Err(QuantumError::Ffi(format!("MPDO: {label} (rc={rc})")))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initial_state_traces_to_one() {
let m = Mpdo::new(3, 16).unwrap();
assert!((m.trace() - 1.0).abs() < 1e-12);
for q in 0..3 {
let z = m.expect_pauli(q, PauliCode::Z).unwrap();
assert!((z - 1.0).abs() < 1e-12);
}
}
#[test]
fn depolarizing_z_decay() {
let mut m = Mpdo::new(1, 16).unwrap();
m.apply_depolarizing(0, 0.4).unwrap();
let z = m.expect_pauli(0, PauliCode::Z).unwrap();
assert!((z - (1.0 - 4.0 * 0.4 / 3.0)).abs() < 1e-12);
assert!((m.trace() - 1.0).abs() < 1e-12);
}
#[test]
fn amplitude_damping_full_reset() {
let mut m = Mpdo::new(1, 16).unwrap();
m.apply_bit_flip(0, 1.0).unwrap();
let z_after_flip = m.expect_pauli(0, PauliCode::Z).unwrap();
assert!((z_after_flip + 1.0).abs() < 1e-12);
m.apply_amplitude_damping(0, 1.0).unwrap();
let z = m.expect_pauli(0, PauliCode::Z).unwrap();
assert!((z - 1.0).abs() < 1e-12);
}
}