use crate::error::{QuantumError, Result};
use moonlab_sys::{
moonlab_libirrep_available, moonlab_libirrep_bb_144_12_12_new,
moonlab_libirrep_bb_288_12_18_new, moonlab_libirrep_bb_72_12_6_new,
moonlab_libirrep_color_hamming_15_7_3_new, moonlab_libirrep_color_steane_new,
moonlab_libirrep_hgp_repetition_new, moonlab_libirrep_qec_distance,
moonlab_libirrep_qec_free, moonlab_libirrep_qec_get_x_check_row,
moonlab_libirrep_qec_get_z_check_row, moonlab_libirrep_qec_logical_qubits,
moonlab_libirrep_qec_n_qubits, moonlab_libirrep_qec_n_x_stabs,
moonlab_libirrep_qec_n_z_stabs, moonlab_libirrep_qec_t,
moonlab_libirrep_surface_code_new, moonlab_libirrep_toric_code_new,
};
use std::ptr;
pub fn is_available() -> bool {
unsafe { moonlab_libirrep_available() == 1 }
}
pub const MOONLAB_LIBIRREP_NOT_BUILT: i32 = -201;
pub const MOONLAB_LIBIRREP_BAD_ARG: i32 = -202;
pub const MOONLAB_LIBIRREP_INTERNAL: i32 = -203;
pub const MOONLAB_LIBIRREP_OOM: i32 = -204;
pub struct QecCode {
ptr: *mut moonlab_libirrep_qec_t,
}
impl QecCode {
fn from_rc(rc: i32, ptr: *mut moonlab_libirrep_qec_t, ctx: &str) -> Result<Self> {
if rc == 0 && !ptr.is_null() {
return Ok(Self { ptr });
}
if rc == MOONLAB_LIBIRREP_NOT_BUILT {
return Err(QuantumError::Ffi(format!(
"{ctx}: moonlab was compiled without libirrep \
(rebuild with -DQSIM_ENABLE_LIBIRREP=ON)"
)));
}
Err(QuantumError::Ffi(format!("{ctx}: rc={rc}")))
}
pub fn surface(distance: i32) -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_surface_code_new(distance, &mut p) };
Self::from_rc(rc, p, &format!("surface(distance={distance})"))
}
pub fn toric(lx: i32, ly: i32) -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_toric_code_new(lx, ly, &mut p) };
Self::from_rc(rc, p, &format!("toric(Lx={lx}, Ly={ly})"))
}
pub fn steane() -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_color_steane_new(&mut p) };
Self::from_rc(rc, p, "steane()")
}
pub fn hamming_15_7_3() -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_color_hamming_15_7_3_new(&mut p) };
Self::from_rc(rc, p, "hamming_15_7_3()")
}
pub fn bb_72_12_6() -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_bb_72_12_6_new(&mut p) };
Self::from_rc(rc, p, "bb_72_12_6()")
}
pub fn bb_144_12_12() -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_bb_144_12_12_new(&mut p) };
Self::from_rc(rc, p, "bb_144_12_12()")
}
pub fn bb_288_12_18() -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_bb_288_12_18_new(&mut p) };
Self::from_rc(rc, p, "bb_288_12_18()")
}
pub fn hgp_repetition(d: i32) -> Result<Self> {
let mut p: *mut moonlab_libirrep_qec_t = ptr::null_mut();
let rc = unsafe { moonlab_libirrep_hgp_repetition_new(d, &mut p) };
Self::from_rc(rc, p, &format!("hgp_repetition(d={d})"))
}
pub fn n_qubits(&self) -> i32 {
unsafe { moonlab_libirrep_qec_n_qubits(self.ptr) }
}
pub fn n_x_stabs(&self) -> i32 {
unsafe { moonlab_libirrep_qec_n_x_stabs(self.ptr) }
}
pub fn n_z_stabs(&self) -> i32 {
unsafe { moonlab_libirrep_qec_n_z_stabs(self.ptr) }
}
pub fn logical_qubits(&self) -> i32 {
unsafe { moonlab_libirrep_qec_logical_qubits(self.ptr) }
}
pub fn distance(&self) -> i32 {
unsafe { moonlab_libirrep_qec_distance(self.ptr) }
}
pub fn x_check_row(&self, row: i32) -> Result<Vec<u8>> {
let n = self.n_qubits() as usize;
let mut buf = vec![0u8; n];
let rc = unsafe {
moonlab_libirrep_qec_get_x_check_row(self.ptr, row, buf.as_mut_ptr())
};
if rc != 0 {
return Err(QuantumError::Ffi(format!("x_check_row({row}): rc={rc}")));
}
Ok(buf)
}
pub fn z_check_row(&self, row: i32) -> Result<Vec<u8>> {
let n = self.n_qubits() as usize;
let mut buf = vec![0u8; n];
let rc = unsafe {
moonlab_libirrep_qec_get_z_check_row(self.ptr, row, buf.as_mut_ptr())
};
if rc != 0 {
return Err(QuantumError::Ffi(format!("z_check_row({row}): rc={rc}")));
}
Ok(buf)
}
}
impl Drop for QecCode {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { moonlab_libirrep_qec_free(self.ptr) };
self.ptr = ptr::null_mut();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn require_libirrep() -> bool {
if !is_available() {
eprintln!("libirrep not linked -- test skipped");
return false;
}
true
}
#[test]
fn surface_d3() {
if !require_libirrep() { return; }
let code = QecCode::surface(3).unwrap();
assert_eq!(code.n_qubits(), 9);
assert_eq!(code.n_x_stabs(), 4);
assert_eq!(code.n_z_stabs(), 4);
assert_eq!(code.logical_qubits(), 1);
assert_eq!(code.distance(), 3);
}
#[test]
fn toric_l3() {
if !require_libirrep() { return; }
let code = QecCode::toric(3, 3).unwrap();
assert_eq!(code.n_qubits(), 18);
assert_eq!(code.logical_qubits(), 2);
}
#[test]
fn steane() {
if !require_libirrep() { return; }
let code = QecCode::steane().unwrap();
assert_eq!(code.n_qubits(), 7);
assert_eq!(code.logical_qubits(), 1);
assert_eq!(code.distance(), 3);
}
#[test]
fn ibm_gross_72() {
if !require_libirrep() { return; }
let code = QecCode::bb_72_12_6().unwrap();
assert_eq!(code.n_qubits(), 72);
assert_eq!(code.logical_qubits(), 12);
}
#[test]
fn ibm_gross_288() {
if !require_libirrep() { return; }
let code = QecCode::bb_288_12_18().unwrap();
assert_eq!(code.n_qubits(), 288);
assert_eq!(code.logical_qubits(), 12);
}
#[test]
fn hgp_rep_ladder() {
if !require_libirrep() { return; }
for (d, n) in [(3, 13), (4, 25), (5, 41)] {
let code = QecCode::hgp_repetition(d).unwrap();
assert_eq!(code.n_qubits(), n);
assert_eq!(code.logical_qubits(), 1);
}
}
#[test]
fn check_row_weight() {
if !require_libirrep() { return; }
let code = QecCode::steane().unwrap();
for row in 0..code.n_x_stabs() {
let support = code.x_check_row(row).unwrap();
let weight: u32 = support.iter().map(|&b| b as u32).sum();
assert_eq!(weight, 4, "Steane X-row {row} weight = {weight}");
}
}
#[test]
fn hgp_rejects_out_of_range() {
if !require_libirrep() { return; }
assert!(QecCode::hgp_repetition(6).is_err());
}
}