qec-code 0.3.0

Rust primitives for constructing and analyzing quantum error-correcting codes
Documentation
use crate::error::{QecError, Result};
use crate::symplectic;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pauli {
    x: Vec<u8>,
    z: Vec<u8>,
}

impl Pauli {
    pub fn from_xz_bits(x: Vec<u8>, z: Vec<u8>) -> Result<Self> {
        if x.len() != z.len() {
            return Err(QecError::InvalidPauliWidth {
                x_width: x.len(),
                z_width: z.len(),
            });
        }
        validate_pauli_bits("X", &x)?;
        validate_pauli_bits("Z", &z)?;
        Ok(Self { x, z })
    }

    pub fn from_symplectic_row(row: Vec<u8>) -> Result<Self> {
        if row.len() % 2 != 0 {
            return Err(QecError::InvalidSymplecticRowWidth { width: row.len() });
        }

        let qubits = row.len() / 2;
        let (x, z) = row.split_at(qubits);
        Self::from_xz_bits(x.to_vec(), z.to_vec())
    }

    pub fn n(&self) -> usize {
        self.x.len()
    }

    pub fn x_bits(&self) -> &[u8] {
        &self.x
    }

    pub fn z_bits(&self) -> &[u8] {
        &self.z
    }

    pub fn try_symplectic_product(&self, other: &Self) -> Result<u8> {
        if self.n() != other.n() {
            return Err(QecError::InvalidPauliWidth {
                x_width: self.n(),
                z_width: other.n(),
            });
        }

        symplectic::symplectic_product(&self.to_symplectic_row(), &other.to_symplectic_row())
    }

    pub fn symplectic_product(&self, other: &Self) -> u8 {
        self.try_symplectic_product(other)
            .expect("Pauli widths must match")
    }

    pub fn weight(&self) -> usize {
        self.x
            .iter()
            .zip(&self.z)
            .filter(|(x, z)| (**x | **z) == 1)
            .count()
    }

    pub fn try_commutes_with(&self, other: &Self) -> Result<bool> {
        if self.n() != other.n() {
            return Err(QecError::InvalidPauliWidth {
                x_width: self.n(),
                z_width: other.n(),
            });
        }

        symplectic::commutes(&self.to_symplectic_row(), &other.to_symplectic_row())
    }

    pub fn commutes_with(&self, other: &Self) -> bool {
        self.try_commutes_with(other)
            .expect("Pauli widths must match")
    }

    pub fn try_anticommutes_with(&self, other: &Self) -> Result<bool> {
        Ok(!self.try_commutes_with(other)?)
    }

    pub fn anticommutes_with(&self, other: &Self) -> bool {
        self.try_anticommutes_with(other)
            .expect("Pauli widths must match")
    }

    pub fn to_symplectic_row(&self) -> Vec<u8> {
        let mut row = Vec::with_capacity(self.n() * 2);
        row.extend_from_slice(&self.x);
        row.extend_from_slice(&self.z);
        row
    }
}

fn validate_pauli_bits(which: &'static str, bits: &[u8]) -> Result<()> {
    for (index, bit) in bits.iter().enumerate() {
        if *bit > 1 {
            return Err(QecError::InvalidPauliBit {
                which,
                index,
                value: *bit,
            });
        }
    }
    Ok(())
}