|
|
|
|
|
|
|
|
| use ndarray::Array2;
|
|
|
| #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
| pub enum Pauli { I, X, Y, Z }
|
|
|
| impl Pauli {
|
| fn bits(self) -> (u8, u8) {
|
| match self {
|
| Pauli::I => (0, 0),
|
| Pauli::X => (1, 0),
|
| Pauli::Y => (1, 1),
|
| Pauli::Z => (0, 1),
|
| }
|
| }
|
| fn from_bits(x: u8, z: u8) -> Self {
|
| match (x & 1, z & 1) {
|
| (0, 0) => Pauli::I,
|
| (1, 0) => Pauli::X,
|
| (1, 1) => Pauli::Y,
|
| (0, 1) => Pauli::Z,
|
| _ => unreachable!(),
|
| }
|
| }
|
| }
|
|
|
| #[derive(Debug, Clone)]
|
| pub struct StabilizerTableau {
|
| pub n_qubits: usize,
|
|
|
| pub matrix: Array2<u8>,
|
| }
|
|
|
| impl StabilizerTableau {
|
|
|
| pub fn new(n_qubits: usize) -> Self {
|
| let n = n_qubits;
|
| let mut matrix = Array2::<u8>::zeros((n, 2 * n));
|
| for i in 0..n {
|
| matrix[(i, n + i)] = 1;
|
| }
|
| Self { n_qubits, matrix }
|
| }
|
|
|
| pub fn from_generators(gens: Vec<Vec<u8>>) -> Self {
|
| let n_qubits = gens[0].len() / 2;
|
| let n_gen = gens.len();
|
| let mut matrix = Array2::<u8>::zeros((n_gen, 2 * n_qubits));
|
| for (i, row) in gens.iter().enumerate() {
|
| for (j, &v) in row.iter().enumerate() {
|
| matrix[(i, j)] = v;
|
| }
|
| }
|
| Self { n_qubits, matrix }
|
| }
|
|
|
| pub fn row(&self, i: usize) -> Vec<u8> {
|
| (0..2 * self.n_qubits).map(|j| self.matrix[(i, j)]).collect()
|
| }
|
| }
|
|
|
|
|
| pub fn apply_hadamard(t: &mut StabilizerTableau, qubit: usize) {
|
| let n = t.n_qubits;
|
| let rows = t.matrix.nrows();
|
| for i in 0..rows {
|
| let x = t.matrix[(i, qubit)];
|
| let z = t.matrix[(i, n + qubit)];
|
| t.matrix[(i, qubit)] = z;
|
| t.matrix[(i, n + qubit)] = x;
|
| }
|
| }
|
|
|
|
|
| pub fn apply_cnot(t: &mut StabilizerTableau, ctrl: usize, tgt: usize) {
|
| let n = t.n_qubits;
|
| let rows = t.matrix.nrows();
|
| for i in 0..rows {
|
| t.matrix[(i, tgt)] ^= t.matrix[(i, ctrl)];
|
| t.matrix[(i, n + ctrl)] ^= t.matrix[(i, n + tgt)];
|
| }
|
| }
|
|
|
|
|
| pub fn check_commutativity(g1: &[u8], g2: &[u8]) -> bool {
|
| let n = g1.len() / 2;
|
| let mut s = 0u8;
|
| for i in 0..n {
|
| s ^= (g1[i] & g2[n + i]) ^ (g1[n + i] & g2[i]);
|
| }
|
| s == 0
|
| }
|
|
|
|
|
|
|
|
|
|
|
| pub fn estimate_distance(t: &StabilizerTableau) -> u32 {
|
| let n = t.n_qubits;
|
| let n_gen = t.matrix.nrows();
|
| let stabilizers: Vec<Vec<u8>> = (0..n_gen).map(|i| t.row(i)).collect();
|
|
|
| for weight in 1..=n {
|
| if search_weight(n, weight, &stabilizers).is_some() {
|
| return weight as u32;
|
| }
|
| }
|
| n as u32
|
| }
|
|
|
| fn search_weight(n: usize, weight: usize, stabs: &[Vec<u8>]) -> Option<Vec<u8>> {
|
| let mut pauli = vec![0u8; 2 * n];
|
| recurse(0, 0, weight, n, &mut pauli, stabs)
|
| }
|
|
|
| fn recurse(
|
| pos: usize,
|
| chosen: usize,
|
| target: usize,
|
| n: usize,
|
| pauli: &mut Vec<u8>,
|
| stabs: &[Vec<u8>],
|
| ) -> Option<Vec<u8>> {
|
| if chosen == target {
|
| return if is_logical_op(pauli, stabs) { Some(pauli.clone()) } else { None };
|
| }
|
| if pos >= n || n - pos < target - chosen {
|
| return None;
|
| }
|
|
|
| for p in [Pauli::X, Pauli::Y, Pauli::Z] {
|
| let (x, z) = p.bits();
|
| pauli[pos] = x;
|
| pauli[n + pos] = z;
|
| if let Some(res) = recurse(pos + 1, chosen + 1, target, n, pauli, stabs) {
|
| return Some(res);
|
| }
|
| }
|
| pauli[pos] = 0;
|
| pauli[n + pos] = 0;
|
| recurse(pos + 1, chosen, target, n, pauli, stabs)
|
| }
|
|
|
| fn is_logical_op(pauli: &[u8], stabs: &[Vec<u8>]) -> bool {
|
|
|
| if pauli.iter().all(|&v| v == 0) {
|
| return false;
|
| }
|
|
|
| for s in stabs {
|
| if !check_commutativity(pauli, s) {
|
| return false;
|
| }
|
| }
|
|
|
|
|
| !stabs.iter().any(|s| s.as_slice() == pauli)
|
| }
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
|
|
| #[test]
|
| fn test_hadamard_swap() {
|
| let mut t = StabilizerTableau::new(2);
|
|
|
| apply_hadamard(&mut t, 0);
|
|
|
| assert_eq!(t.matrix[(0, 0)], 1);
|
| assert_eq!(t.matrix[(0, 2)], 0);
|
| }
|
|
|
| #[test]
|
| fn test_commutativity_xx_zz() {
|
|
|
| let g1 = vec![1u8, 1, 0, 0];
|
| let g2 = vec![0u8, 0, 1, 1];
|
|
|
| assert!(check_commutativity(&g1, &g2));
|
| }
|
|
|
| #[test]
|
| fn test_distance_1qubit() {
|
|
|
| let t = StabilizerTableau::from_generators(vec![vec![0u8, 1]]);
|
| let d = estimate_distance(&t);
|
| assert_eq!(d, 1);
|
| }
|
| }
|
|
|