use std::ptr::NonNull;
use std::mem::MaybeUninit;
use num_complex::Complex64;
use moonlab_sys as ffi;
use crate::error::{QuantumError, Result};
pub const MAX_QUBITS: usize = 32;
#[derive(Debug)]
pub struct QuantumState {
inner: NonNull<ffi::quantum_state_t>,
num_qubits: usize,
}
unsafe impl Send for QuantumState {}
impl QuantumState {
pub fn new(num_qubits: usize) -> Result<Self> {
if num_qubits == 0 || num_qubits > MAX_QUBITS {
return Err(QuantumError::InvalidQubit {
index: num_qubits,
max: MAX_QUBITS,
});
}
unsafe {
let mut state = MaybeUninit::<ffi::quantum_state_t>::uninit();
let result = ffi::quantum_state_init(state.as_mut_ptr(), num_qubits);
if result != 0 {
return Err(QuantumError::AllocationFailed(num_qubits));
}
let state_ptr = Box::into_raw(Box::new(state.assume_init()));
Ok(Self {
inner: NonNull::new(state_ptr).ok_or(QuantumError::NullPointer)?,
num_qubits,
})
}
}
#[inline]
pub fn num_qubits(&self) -> usize {
self.num_qubits
}
#[inline]
pub fn state_dim(&self) -> usize {
1 << self.num_qubits
}
#[inline]
pub(crate) fn as_ptr(&self) -> *mut ffi::quantum_state_t {
self.inner.as_ptr()
}
fn check_qubit(&self, qubit: usize) -> Result<()> {
if qubit >= self.num_qubits {
Err(QuantumError::InvalidQubit {
index: qubit,
max: self.num_qubits,
})
} else {
Ok(())
}
}
pub fn probabilities(&self) -> Vec<f64> {
let dim = self.state_dim();
let mut probs = vec![0.0; dim];
unsafe {
let state = self.inner.as_ref();
for i in 0..dim {
let amp = *state.amplitudes.add(i);
let re = amp.re;
let im = amp.im;
probs[i] = re * re + im * im;
}
}
probs
}
pub fn amplitudes(&self) -> Vec<Complex64> {
let dim = self.state_dim();
let mut amps = Vec::with_capacity(dim);
unsafe {
let state = self.inner.as_ref();
for i in 0..dim {
let amp = *state.amplitudes.add(i);
amps.push(Complex64::new(amp.re, amp.im));
}
}
amps
}
pub fn prob_zero(&self, qubit: usize) -> Result<f64> {
self.check_qubit(qubit)?;
unsafe {
Ok(ffi::measurement_probability_zero(self.as_ptr(), qubit as i32))
}
}
pub fn prob_one(&self, qubit: usize) -> Result<f64> {
self.check_qubit(qubit)?;
unsafe {
Ok(ffi::measurement_probability_one(self.as_ptr(), qubit as i32))
}
}
pub fn entanglement_entropy(&self, subsystem_a: &[usize]) -> Result<f64> {
for &q in subsystem_a {
self.check_qubit(q)?;
}
let indices: Vec<i32> = subsystem_a.iter().map(|&q| q as i32).collect();
unsafe {
Ok(ffi::quantum_state_entanglement_entropy(
self.as_ptr(),
indices.as_ptr(),
indices.len(),
))
}
}
pub fn concurrence(&self) -> Result<f64> {
if self.num_qubits != 2 {
return Err(QuantumError::InvalidQubit {
index: 0, max: 2,
});
}
Ok(unsafe { ffi::entanglement_concurrence_2qubit(self.as_ptr()) })
}
pub fn negativity(&self) -> Result<f64> {
if self.num_qubits != 2 {
return Err(QuantumError::InvalidQubit {
index: 0, max: 2,
});
}
Ok(unsafe { ffi::entanglement_negativity_2qubit(self.as_ptr()) })
}
pub fn mutual_information(
&self,
qubits_a: &[usize],
qubits_b: &[usize],
) -> Result<f64> {
for &q in qubits_a.iter().chain(qubits_b.iter()) {
self.check_qubit(q)?;
}
let a: Vec<i32> = qubits_a.iter().map(|&q| q as i32).collect();
let b: Vec<i32> = qubits_b.iter().map(|&q| q as i32).collect();
Ok(unsafe {
ffi::entanglement_mutual_information(
self.as_ptr(),
a.as_ptr(),
a.len() as i32,
b.as_ptr(),
b.len() as i32,
)
})
}
pub fn purity(&self) -> f64 {
unsafe { ffi::quantum_state_purity(self.as_ptr()) }
}
pub fn entropy(&self) -> f64 {
unsafe { ffi::quantum_state_entropy(self.as_ptr()) }
}
pub fn reset(&mut self) -> &mut Self {
unsafe {
ffi::quantum_state_reset(self.as_ptr());
}
self
}
pub fn x(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_pauli_x(self.as_ptr(), qubit as i32); }
}
self
}
pub fn y(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_pauli_y(self.as_ptr(), qubit as i32); }
}
self
}
pub fn z(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_pauli_z(self.as_ptr(), qubit as i32); }
}
self
}
pub fn h(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_hadamard(self.as_ptr(), qubit as i32); }
}
self
}
pub fn s(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_s(self.as_ptr(), qubit as i32); }
}
self
}
pub fn sdg(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_s_dagger(self.as_ptr(), qubit as i32); }
}
self
}
pub fn t(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_t(self.as_ptr(), qubit as i32); }
}
self
}
pub fn tdg(&mut self, qubit: usize) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_t_dagger(self.as_ptr(), qubit as i32); }
}
self
}
pub fn rx(&mut self, qubit: usize, theta: f64) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_rx(self.as_ptr(), qubit as i32, theta); }
}
self
}
pub fn ry(&mut self, qubit: usize, theta: f64) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_ry(self.as_ptr(), qubit as i32, theta); }
}
self
}
pub fn rz(&mut self, qubit: usize, theta: f64) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_rz(self.as_ptr(), qubit as i32, theta); }
}
self
}
pub fn phase(&mut self, qubit: usize, phi: f64) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_phase(self.as_ptr(), qubit as i32, phi); }
}
self
}
pub fn u3(&mut self, qubit: usize, theta: f64, phi: f64, lambda: f64) -> &mut Self {
if self.check_qubit(qubit).is_ok() {
unsafe { ffi::gate_u3(self.as_ptr(), qubit as i32, theta, phi, lambda); }
}
self
}
pub fn cnot(&mut self, control: usize, target: usize) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_cnot(self.as_ptr(), control as i32, target as i32); }
}
}
self
}
#[inline]
pub fn cx(&mut self, control: usize, target: usize) -> &mut Self {
self.cnot(control, target)
}
pub fn cz(&mut self, control: usize, target: usize) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_cz(self.as_ptr(), control as i32, target as i32); }
}
}
self
}
pub fn cy(&mut self, control: usize, target: usize) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_cy(self.as_ptr(), control as i32, target as i32); }
}
}
self
}
pub fn swap(&mut self, qubit1: usize, qubit2: usize) -> &mut Self {
if self.check_qubit(qubit1).is_ok() && self.check_qubit(qubit2).is_ok() {
if qubit1 != qubit2 {
unsafe { ffi::gate_swap(self.as_ptr(), qubit1 as i32, qubit2 as i32); }
}
}
self
}
pub fn crx(&mut self, control: usize, target: usize, theta: f64) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_crx(self.as_ptr(), control as i32, target as i32, theta); }
}
}
self
}
pub fn cry(&mut self, control: usize, target: usize, theta: f64) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_cry(self.as_ptr(), control as i32, target as i32, theta); }
}
}
self
}
pub fn crz(&mut self, control: usize, target: usize, theta: f64) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_crz(self.as_ptr(), control as i32, target as i32, theta); }
}
}
self
}
pub fn cphase(&mut self, control: usize, target: usize, phi: f64) -> &mut Self {
if self.check_qubit(control).is_ok() && self.check_qubit(target).is_ok() {
if control != target {
unsafe { ffi::gate_cphase(self.as_ptr(), control as i32, target as i32, phi); }
}
}
self
}
pub fn toffoli(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self {
if self.check_qubit(control1).is_ok()
&& self.check_qubit(control2).is_ok()
&& self.check_qubit(target).is_ok()
{
unsafe { ffi::gate_toffoli(self.as_ptr(), control1 as i32, control2 as i32, target as i32); }
}
self
}
#[inline]
pub fn ccx(&mut self, control1: usize, control2: usize, target: usize) -> &mut Self {
self.toffoli(control1, control2, target)
}
pub fn fredkin(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self {
if self.check_qubit(control).is_ok()
&& self.check_qubit(target1).is_ok()
&& self.check_qubit(target2).is_ok()
{
unsafe { ffi::gate_fredkin(self.as_ptr(), control as i32, target1 as i32, target2 as i32); }
}
self
}
#[inline]
pub fn cswap(&mut self, control: usize, target1: usize, target2: usize) -> &mut Self {
self.fredkin(control, target1, target2)
}
pub fn qft(&mut self, qubits: &[usize]) -> &mut Self {
for &q in qubits {
if self.check_qubit(q).is_err() {
return self;
}
}
let indices: Vec<i32> = qubits.iter().map(|&q| q as i32).collect();
unsafe {
ffi::gate_qft(self.as_ptr(), indices.as_ptr(), indices.len());
}
self
}
pub fn iqft(&mut self, qubits: &[usize]) -> &mut Self {
for &q in qubits {
if self.check_qubit(q).is_err() {
return self;
}
}
let indices: Vec<i32> = qubits.iter().map(|&q| q as i32).collect();
unsafe {
ffi::gate_iqft(self.as_ptr(), indices.as_ptr(), indices.len());
}
self
}
pub fn expectation_z(&self, qubit: usize) -> Result<f64> {
self.check_qubit(qubit)?;
unsafe {
Ok(ffi::measurement_expectation_z(self.as_ptr(), qubit as i32))
}
}
pub fn expectation_x(&self, qubit: usize) -> Result<f64> {
self.check_qubit(qubit)?;
unsafe {
Ok(ffi::measurement_expectation_x(self.as_ptr(), qubit as i32))
}
}
pub fn expectation_y(&self, qubit: usize) -> Result<f64> {
self.check_qubit(qubit)?;
unsafe {
Ok(ffi::measurement_expectation_y(self.as_ptr(), qubit as i32))
}
}
pub fn correlation_zz(&self, qubit_i: usize, qubit_j: usize) -> Result<f64> {
self.check_qubit(qubit_i)?;
self.check_qubit(qubit_j)?;
unsafe {
Ok(ffi::measurement_correlation_zz(self.as_ptr(), qubit_i as i32, qubit_j as i32))
}
}
}
impl Clone for QuantumState {
fn clone(&self) -> Self {
unsafe {
let mut new_state = MaybeUninit::<ffi::quantum_state_t>::uninit();
ffi::quantum_state_clone(new_state.as_mut_ptr(), self.as_ptr());
let state_ptr = Box::into_raw(Box::new(new_state.assume_init()));
Self {
inner: NonNull::new(state_ptr).expect("Clone returned null"),
num_qubits: self.num_qubits,
}
}
}
}
impl Drop for QuantumState {
fn drop(&mut self) {
unsafe {
ffi::quantum_state_free(self.as_ptr());
let _ = Box::from_raw(self.inner.as_ptr());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_state() {
let state = QuantumState::new(3).unwrap();
assert_eq!(state.num_qubits(), 3);
assert_eq!(state.state_dim(), 8);
}
#[test]
fn test_invalid_qubits() {
assert!(QuantumState::new(0).is_err());
assert!(QuantumState::new(33).is_err());
}
#[test]
fn test_initial_state() {
let state = QuantumState::new(2).unwrap();
let probs = state.probabilities();
assert!((probs[0] - 1.0).abs() < 1e-10);
assert!(probs[1] < 1e-10);
assert!(probs[2] < 1e-10);
assert!(probs[3] < 1e-10);
}
#[test]
fn test_hadamard() {
let mut state = QuantumState::new(1).unwrap();
state.h(0);
let probs = state.probabilities();
assert!((probs[0] - 0.5).abs() < 1e-10);
assert!((probs[1] - 0.5).abs() < 1e-10);
}
#[test]
fn test_bell_state() {
let mut state = QuantumState::new(2).unwrap();
state.h(0).cnot(0, 1);
let probs = state.probabilities();
assert!((probs[0] - 0.5).abs() < 1e-10);
assert!(probs[1] < 1e-10);
assert!(probs[2] < 1e-10);
assert!((probs[3] - 0.5).abs() < 1e-10);
}
#[test]
fn test_ghz_state() {
let mut state = QuantumState::new(3).unwrap();
state.h(0).cnot(0, 1).cnot(0, 2);
let probs = state.probabilities();
assert!((probs[0] - 0.5).abs() < 1e-10);
assert!((probs[7] - 0.5).abs() < 1e-10);
}
#[test]
fn test_entanglement_entropy() {
let mut state = QuantumState::new(2).unwrap();
let entropy_product = state.entanglement_entropy(&[0]).unwrap();
assert!(entropy_product < 1e-10);
state.h(0).cnot(0, 1);
let entropy_bell = state.entanglement_entropy(&[0]).unwrap();
assert!(entropy_bell > 0.6); }
#[test]
fn test_method_chaining() {
let mut state = QuantumState::new(4).unwrap();
state
.h(0)
.h(1)
.cnot(0, 2)
.cnot(1, 3)
.rz(2, std::f64::consts::PI / 4.0)
.reset()
.x(0);
let probs = state.probabilities();
assert!((probs[1] - 1.0).abs() < 1e-10);
}
#[test]
fn test_clone() {
let mut state = QuantumState::new(2).unwrap();
state.h(0).cnot(0, 1);
let cloned = state.clone();
let orig_probs = state.probabilities();
let clone_probs = cloned.probabilities();
for i in 0..4 {
assert!((orig_probs[i] - clone_probs[i]).abs() < 1e-10);
}
}
}