From bb9563605c75732b23da864d26013b20f9f27757 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 13:59:11 +0200 Subject: [PATCH 01/46] Add helper function in PauliString to calculate hamming weight --- synir/src/data_structures/pauli_string.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/synir/src/data_structures/pauli_string.rs b/synir/src/data_structures/pauli_string.rs index 69a143b8..a5743e69 100644 --- a/synir/src/data_structures/pauli_string.rs +++ b/synir/src/data_structures/pauli_string.rs @@ -72,6 +72,14 @@ impl PauliString { self.x.read().unwrap()[i] } + pub fn x_weight(&self) -> usize { + self.x.read().unwrap().count_ones() + } + + pub fn z_weight(&self) -> usize { + self.z.read().unwrap().count_ones() + } + pub fn z(&self, i: usize) -> bool { self.z.read().unwrap()[i] } From 55ae0b7c7bfcfa0a41c42278c7430af29af13c58 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:01:40 +0200 Subject: [PATCH 02/46] Update connectivity to fix unintended behavior Moved to StableGraph, remove iterations from 0..graph_size as graphs no longer compact. Ensure unweighted edges have a defaulta weight of 1. Add helper function to find edge and node count of Connectivity. Remove asserts to check if node index is less than node count as graphs are no longer compact. --- synir/src/architecture.rs | 2 +- synir/src/architecture/connectivity.rs | 56 ++++++++++++-------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/synir/src/architecture.rs b/synir/src/architecture.rs index b96a4c96..465c713a 100644 --- a/synir/src/architecture.rs +++ b/synir/src/architecture.rs @@ -14,7 +14,7 @@ pub trait Architecture { fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec; fn distance(&self, i: GraphIndex, j: GraphIndex) -> usize; fn neighbors(&self, i: GraphIndex) -> Vec; - fn non_cutting(&mut self) -> &Vec; + fn non_cutting(&self) -> &Vec; fn get_cx_ladder( &self, nodes: &[GraphIndex], diff --git a/synir/src/architecture/connectivity.rs b/synir/src/architecture/connectivity.rs index d6f0c963..48e88152 100644 --- a/synir/src/architecture/connectivity.rs +++ b/synir/src/architecture/connectivity.rs @@ -28,7 +28,7 @@ fn get_non_cutting_vertices( .collect() } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Connectivity { graph: StableUnGraph, non_cutting: Vec, @@ -80,7 +80,8 @@ impl Connectivity { } pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self { - let graph = StableUnGraph::from_edges(edges); + let mut graph = StableUnGraph::from_edges(edges); + graph.edge_weights_mut().for_each(|weight| *weight = 1); // Default weight of 1 for unweighted edges Connectivity::from_graph(graph) } @@ -109,6 +110,14 @@ impl Connectivity { .collect() } + pub fn node_bound(&self) -> usize { + self.graph.node_count() + } + + pub fn edge_bound(&self) -> usize { + self.graph.edge_count() + } + pub fn edges(&self) -> Vec<(GraphIndex, GraphIndex)> { self.graph .edge_references() @@ -158,41 +167,21 @@ impl Connectivity { impl Architecture for Connectivity { fn best_path(&self, i: GraphIndex, j: GraphIndex) -> Vec { - assert!( - i < self.graph.node_count(), - "architecture does not contain node {i}" - ); - assert!( - j < self.graph.node_count(), - "architecture does not contain node {j}" - ); self.path_from_shortest_path_tree(i, j) } fn distance(&self, i: GraphIndex, j: GraphIndex) -> usize { - assert!( - i < self.graph.node_count(), - "architecture does not contain node {i}" - ); - assert!( - j < self.graph.node_count(), - "architecture does not contain node {j}" - ); self.distance[&(self.graph.from_index(i), self.graph.from_index(j))] } fn neighbors(&self, i: GraphIndex) -> Vec { - assert!( - i < self.graph.node_count(), - "architecture does not contain node {i}" - ); self.graph .neighbors(self.graph.from_index(i)) .map(|neighbor| neighbor.index()) .collect() } - fn non_cutting(&mut self) -> &Vec { + fn non_cutting(&self) -> &Vec { &self.non_cutting } @@ -440,7 +429,7 @@ mod tests { } #[test] - #[should_panic = "architecture does not contain node 6"] + #[should_panic = "index out of bounds: the len is 6 but the index is 6"] fn test_best_path_missing() { let new_architecture = Connectivity::from_edges(&setup_simple()); new_architecture.best_path(5, 6); @@ -455,7 +444,14 @@ mod tests { } #[test] - #[should_panic = "architecture does not contain node 6"] + fn test_simple_distance() { + let new_architecture = Connectivity::from_edges(&setup_simple()); + assert_eq!(1, new_architecture.distance(0, 1)); + assert_eq!(2, new_architecture.distance(1, 4)); + } + + #[test] + #[should_panic = "no entry found for key"] fn test_distance_missing() { let new_architecture = Connectivity::from_edges(&setup_simple()); new_architecture.distance(5, 6); @@ -468,7 +464,7 @@ mod tests { } #[test] - #[should_panic = "architecture does not contain node 7"] + #[should_panic = "no entry found for key"] fn test_neighbor_missing() { let new_architecture = Connectivity::from_edges(&setup_simple()); new_architecture.distance(2, 7); @@ -476,19 +472,19 @@ mod tests { #[test] fn test_non_cutting() { - let mut new_architecture = Connectivity::from_edges(&setup_simple()); + let new_architecture = Connectivity::from_edges(&setup_simple()); assert_eq!(&new_architecture.nodes(), new_architecture.non_cutting()); } #[test] fn test_non_cutting_line() { - let mut line_architecture = Connectivity::line(5); + let line_architecture = Connectivity::line(5); assert_eq!(*line_architecture.non_cutting(), vec![0, 4]); } #[test] fn test_non_cutting_grid() { - let mut line_architecture = Connectivity::grid(3, 3); + let line_architecture = Connectivity::grid(3, 3); assert_eq!( *line_architecture.non_cutting(), vec![0, 1, 2, 3, 4, 5, 6, 7, 8] @@ -497,7 +493,7 @@ mod tests { #[test] fn test_non_cutting_complete() { - let mut line_architecture = Connectivity::complete(3); + let line_architecture = Connectivity::complete(3); assert_eq!(*line_architecture.non_cutting(), vec![0, 1, 2]); } From eb0154437168cc977c89c4237d56cd4da7c48162 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:04:03 +0200 Subject: [PATCH 03/46] Correct pivot selection for custom callback Mixup between rows and columns fixed. Added functionality to perform x and z cleanup interchangeably. --- .../ir/clifford_tableau/custom_callback.rs | 4 +- synir/src/ir/clifford_tableau/helper.rs | 112 ++++++++++++------ synir/src/ir/clifford_tableau/naive.rs | 5 +- 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index 8e6a3a50..b21251ca 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -84,7 +84,7 @@ where clean_x_observables( repr, &mut clifford_tableau, - &remaining_rows, + &remaining_columns, pivot_column, pivot_row, ); @@ -94,7 +94,7 @@ where clean_z_observables( repr, &mut clifford_tableau, - &remaining_rows, + &remaining_columns, pivot_column, pivot_row, ); diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index c2c8f657..f94773d4 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -1,6 +1,7 @@ -use std::iter::zip; +use std::{iter::zip, usize}; use crate::{ + architecture::{connectivity::Connectivity, Architecture}, data_structures::{CliffordTableau, PauliLetter, PauliString, PropagateClifford}, ir::CliffordGates, }; @@ -43,7 +44,7 @@ fn is_not_z(pauli_letter: PauliLetter) -> bool { pauli_letter != PauliLetter::Z } -pub(super) fn clean_pivot( +pub(super) fn clean_naive_pivot( repr: &mut G, ct: &mut CliffordTableau, pivot_column: usize, @@ -68,6 +69,51 @@ pub(super) fn clean_pivot( } } +pub(super) fn clean_pivot( + repr: &mut G, + clifford_tableau: &mut CliffordTableau, + pivot_column: usize, + pivot_row: usize, + letter: PauliLetter, +) where + G: CliffordGates, +{ + match letter { + PauliLetter::X => clean_x_pivot(repr, clifford_tableau, pivot_column, pivot_row), + PauliLetter::Z => clean_z_pivot(repr, clifford_tableau, pivot_column, pivot_row), + _ => panic!("Invalid Pauli letter for pivot cleaning"), + } +} + +pub(super) fn clean_observables( + repr: &mut G, + clifford_tableau: &mut CliffordTableau, + remaining_rows: &[usize], + pivot_column: usize, + pivot_row: usize, + letter: PauliLetter, +) where + G: CliffordGates, +{ + match letter { + PauliLetter::X => clean_x_observables( + repr, + clifford_tableau, + remaining_rows, + pivot_column, + pivot_row, + ), + PauliLetter::Z => clean_z_observables( + repr, + clifford_tableau, + remaining_rows, + pivot_column, + pivot_row, + ), + _ => panic!("Invalid Pauli letter for observable cleaning"), + } +} + pub(super) fn clean_x_pivot( repr: &mut G, clifford_tableau: &mut CliffordTableau, @@ -77,15 +123,15 @@ pub(super) fn clean_x_pivot( G: CliffordGates, { // These are switched around because of implementation - if check_pauli(&*clifford_tableau, pivot_row, pivot_column, is_y) { - clifford_tableau.s(pivot_row); - repr.s(pivot_row); + if check_pauli(&*clifford_tableau, pivot_column, pivot_row, is_y) { + clifford_tableau.s(pivot_column); + repr.s(pivot_column); } // These are switched around because of implementation - if check_pauli(&*clifford_tableau, pivot_row, pivot_column, is_z) { - clifford_tableau.h(pivot_row); - repr.h(pivot_row); + if check_pauli(&*clifford_tableau, pivot_column, pivot_row, is_z) { + clifford_tableau.h(pivot_column); + repr.h(pivot_column); } } @@ -98,40 +144,39 @@ pub(super) fn clean_z_pivot( G: CliffordGates, { let num_qubits = clifford_tableau.size(); - // These are switched around because of implementation + if check_pauli( &*clifford_tableau, - pivot_row, - pivot_column + num_qubits, + pivot_column, + pivot_row + num_qubits, is_y, ) { - clifford_tableau.v(pivot_row); - repr.v(pivot_row); + clifford_tableau.v(pivot_column); + repr.v(pivot_column); } - // These are switched around because of implementation if check_pauli( &*clifford_tableau, - pivot_row, - pivot_column + num_qubits, + pivot_column, + pivot_row + num_qubits, is_x, ) { - clifford_tableau.h(pivot_row); - repr.h(pivot_row); + clifford_tableau.h(pivot_column); + repr.h(pivot_column); } } pub(super) fn clean_x_observables( repr: &mut G, clifford_tableau: &mut CliffordTableau, - remaining_rows: &[usize], + remaining_columns: &[usize], pivot_column: usize, pivot_row: usize, ) where G: CliffordGates, { let affected_cols = - check_across_columns(&*clifford_tableau, remaining_rows, pivot_column, is_y); + check_across_columns(&*clifford_tableau, remaining_columns, pivot_row, is_y); for col in affected_cols { repr.s(col); @@ -139,7 +184,7 @@ pub(super) fn clean_x_observables( } let affected_cols = - check_across_columns(&*clifford_tableau, remaining_rows, pivot_column, is_z); + check_across_columns(&*clifford_tableau, remaining_columns, pivot_row, is_z); for col in affected_cols { repr.h(col); @@ -147,18 +192,18 @@ pub(super) fn clean_x_observables( } let affected_cols = - check_across_columns(&*clifford_tableau, remaining_rows, pivot_column, is_not_i); + check_across_columns(&*clifford_tableau, remaining_columns, pivot_row, is_not_i); for col in affected_cols { - repr.cx(pivot_row, col); - clifford_tableau.cx(pivot_row, col); + repr.cx(pivot_column, col); + clifford_tableau.cx(pivot_column, col); } } pub(super) fn clean_z_observables( repr: &mut G, clifford_tableau: &mut CliffordTableau, - remaining_rows: &[usize], + remaining_columns: &[usize], pivot_column: usize, pivot_row: usize, ) where @@ -167,8 +212,8 @@ pub(super) fn clean_z_observables( let num_qubits = clifford_tableau.size(); let affected_cols = check_across_columns( &*clifford_tableau, - remaining_rows, - pivot_column + num_qubits, + remaining_columns, + pivot_row + num_qubits, is_y, ); for col in affected_cols { @@ -178,8 +223,8 @@ pub(super) fn clean_z_observables( let affected_cols = check_across_columns( &*clifford_tableau, - remaining_rows, - pivot_column + num_qubits, + remaining_columns, + pivot_row + num_qubits, is_x, ); for col in affected_cols { @@ -189,13 +234,13 @@ pub(super) fn clean_z_observables( let affected_cols = check_across_columns( &*clifford_tableau, - remaining_rows, - pivot_column + num_qubits, + remaining_columns, + pivot_row + num_qubits, is_not_i, ); for col in affected_cols { - repr.cx(col, pivot_row); - clifford_tableau.cx(col, pivot_row); + repr.cx(col, pivot_column); + clifford_tableau.cx(col, pivot_column); } } @@ -258,7 +303,6 @@ pub(super) fn naive_pivot_search( break; } } - pivot_col } diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 9309c525..67628193 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -4,7 +4,8 @@ use crate::{ }; use super::helper::{ - clean_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, swap, + clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, + swap, }; #[derive(Default)] @@ -25,7 +26,7 @@ where } // Cleanup pivot column - clean_pivot(repr, &mut clifford_tableau, row, row); + clean_naive_pivot(repr, &mut clifford_tableau, row, row); let checked_rows = (row + 1..num_qubits).collect::>(); From 3099c2d404e40a17fc44a6ae21fa72b8a2ee8864 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:06:00 +0200 Subject: [PATCH 04/46] Add more helper functions to CliffordTableau Add destabilizer and stabilizer to extract PauliLetter at certain position along particular qubits stabilizer/destabilizer. Add columns to get iterator over PauliColumn. --- synir/src/data_structures/clifford_tableau.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index eaf1d451..533f5035 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -9,6 +9,7 @@ use super::{ pauli_string::{cx, PauliString}, IndexType, PropagateClifford, }; +use crate::data_structures::PauliLetter; #[derive(PartialEq, Eq, Debug, Clone, Default)] pub struct CliffordTableau { @@ -51,11 +52,35 @@ impl CliffordTableau { self.signs[n..].to_bitvec() } + pub(crate) fn destabilizer(&self, qubit: usize, index: usize) -> PauliLetter { + let n = self.size(); + assert!(index < n && qubit < n); + + PauliLetter::new( + self.pauli_columns[qubit].x(index), + self.pauli_columns[qubit].z(index), + ) + } + + pub(crate) fn stabilizer(&self, qubit: usize, index: usize) -> PauliLetter { + let n = self.size(); + assert!(index < n && qubit < n); + + PauliLetter::new( + self.pauli_columns[qubit].x(index + n), + self.pauli_columns[qubit].z(index + n), + ) + } + pub(crate) fn column(&self, i: usize) -> &PauliString { &self.pauli_columns[i] } - pub fn compose(&self, rhs: &Self) -> Self { + pub(crate) fn columns(&self) -> &Vec { + &self.pauli_columns + } + + pub(crate) fn compose(&self, rhs: &Self) -> Self { rhs.prepend(self) } From 72606161751956e85a5456d2e6d5521ffbfb42e4 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:06:17 +0200 Subject: [PATCH 05/46] Add more comments on structure of CliffordTableau --- synir/src/data_structures/clifford_tableau.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index 533f5035..4f70e54a 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -14,6 +14,7 @@ use crate::data_structures::PauliLetter; #[derive(PartialEq, Eq, Debug, Clone, Default)] pub struct CliffordTableau { // We keep track of the pauli letters per qubit not per stabilizer + // Each Pauli Column contains 2 BitStrings of length 2 * n and corresponds to all operators on one qubit (vertical) pauli_columns: Vec, signs: BitVec, size: usize, // https://quantumcomputing.stackexchange.com/questions/28740/tracking-the-signs-of-the-inverse-tableau From d203c63ba769ba05b636232ea5e44b85bff8af55 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:07:17 +0200 Subject: [PATCH 06/46] Add helper functions for PermRowCol Add clean_prc for X and Z. Add pick_column and pick_row. --- synir/src/ir/clifford_tableau/helper.rs | 202 ++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index f94773d4..a453c6e6 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -334,3 +334,205 @@ pub(super) fn check_across_columns( } affected_cols } + +/// function to pick a stabilizer / destabilizer to set to identity in Clifford tableau. +pub(super) fn pick_row( + clifford_tableau: &CliffordTableau, + connectivity: &Connectivity, + remaining_rows: &[usize], +) -> usize { + let mut row_weights = vec![usize::MAX; clifford_tableau.size()]; + for row in remaining_rows { + row_weights[*row] = 0; + for qubit in connectivity.nodes() { + if is_not_i(clifford_tableau.stabilizer(qubit, *row)) { + row_weights[*row] += 1; + } + if is_not_i(clifford_tableau.destabilizer(qubit, *row)) { + row_weights[*row] += 1; + } + } + } + + row_weights + .into_iter() + .enumerate() + .min_by_key(|&(_, weight)| weight) + .map(|(index, _)| index) + .unwrap() +} + +/// function to pick a qubit to disconnect in Clifford tableau. +pub(super) fn pick_column( + clifford_tableau: &CliffordTableau, + connectivity: &Connectivity, +) -> usize { + let mut column_weights = vec![usize::MAX; clifford_tableau.size()]; + + let non_cutting = connectivity.non_cutting(); + + for qubit in non_cutting { + column_weights[*qubit] = 0; + for interaction in connectivity.nodes() { + let mult = (clifford_tableau.stabilizer(*qubit, interaction) != PauliLetter::I) + as usize + + (clifford_tableau.destabilizer(*qubit, interaction) != PauliLetter::I) as usize; + if mult > 0 { + column_weights[*qubit] += connectivity.distance(*qubit, interaction) * mult; + } + } + } + + column_weights + .iter() + .enumerate() + .min_by_key(|&(_, &weight)| weight) + .map(|(index, _)| index) + .unwrap() +} + +pub(super) fn clean_prc( + repr: &mut G, + clifford_tableau: &mut CliffordTableau, + connectivity: &Connectivity, + remaining_rows: &[usize], + pivot_column: usize, + pivot_row: usize, + letter: PauliLetter, +) where + G: CliffordGates, +{ + match letter { + PauliLetter::X => clean_x_prc( + repr, + clifford_tableau, + connectivity, + remaining_rows, + pivot_column, + pivot_row, + ), + PauliLetter::Z => clean_z_prc( + repr, + clifford_tableau, + connectivity, + remaining_rows, + pivot_column, + pivot_row, + ), + _ => panic!("Invalid Pauli letter for observable cleaning"), + } +} + +pub(super) fn clean_x_prc( + repr: &mut G, + clifford_tableau: &mut CliffordTableau, + connectivity: &Connectivity, + remaining_columns: &[usize], + pivot_column: usize, + pivot_row: usize, +) where + G: CliffordGates, +{ + let mut terminals = remaining_columns + .iter() + .filter_map(|qubit| { + if is_not_i(clifford_tableau.destabilizer(*qubit, pivot_row)) { + Some(*qubit) + } else { + None + } + }) + .collect::>(); + + if terminals.is_empty() { + return; + } + terminals.push(pivot_column); + + let traversal = connectivity + .get_cx_ladder(&terminals, &pivot_column) + .unwrap(); + + let affected_cols = check_across_columns(&*clifford_tableau, &terminals, pivot_row, is_y); + println!("affected cols: {:?}", affected_cols); + for col in affected_cols { + repr.s(col); + clifford_tableau.s(col); + } + + let affected_cols = check_across_columns(&*clifford_tableau, &terminals, pivot_row, is_z); + for col in affected_cols { + repr.h(col); + clifford_tableau.h(col); + } + + for (parent, child) in traversal.iter().rev() { + if is_i(clifford_tableau.destabilizer(*parent, pivot_row)) { + repr.cx(*child, *parent); + clifford_tableau.cx(*child, *parent); + } + } + + for (parent, child) in traversal.iter().rev() { + repr.cx(*parent, *child); + clifford_tableau.cx(*parent, *child); + } +} + +pub(super) fn clean_z_prc( + repr: &mut G, + clifford_tableau: &mut CliffordTableau, + connectivity: &Connectivity, + remaining_columns: &[usize], + pivot_column: usize, + pivot_row: usize, +) where + G: CliffordGates, +{ + let num_qubits = clifford_tableau.size(); + let mut terminals = remaining_columns + .iter() + .filter_map(|qubit| { + if is_not_i(clifford_tableau.stabilizer(*qubit, pivot_row)) { + Some(*qubit) + } else { + None + } + }) + .collect::>(); + if terminals.is_empty() { + return; + } + terminals.push(pivot_column); + + let traversal = connectivity + .get_cx_ladder(&terminals, &pivot_column) + .unwrap(); + + let affected_cols = + check_across_columns(&*clifford_tableau, &terminals, pivot_row + num_qubits, is_y); + + for col in affected_cols { + repr.v(col); + clifford_tableau.v(col); + } + + let affected_cols = + check_across_columns(&*clifford_tableau, &terminals, pivot_row + num_qubits, is_x); + for col in affected_cols { + repr.h(col); + clifford_tableau.h(col); + } + + for (parent, child) in traversal.iter().rev() { + if is_i(clifford_tableau.stabilizer(*parent, pivot_row)) { + repr.cx(*parent, *child); + clifford_tableau.cx(*parent, *child); + } + } + + for (parent, child) in traversal.iter().rev() { + repr.cx(*child, *parent); + clifford_tableau.cx(*child, *parent); + } +} From 4fe69af720b289073b2d631b7b4d0a1c72bb8034 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:07:32 +0200 Subject: [PATCH 07/46] Implement PermRowCol for CliffordTableau --- synir/src/ir/clifford_tableau.rs | 4 +- synir/src/ir/clifford_tableau/permrowcol.rs | 144 ++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 synir/src/ir/clifford_tableau/permrowcol.rs diff --git a/synir/src/ir/clifford_tableau.rs b/synir/src/ir/clifford_tableau.rs index 228cfb60..9d26eef8 100644 --- a/synir/src/ir/clifford_tableau.rs +++ b/synir/src/ir/clifford_tableau.rs @@ -3,10 +3,12 @@ use crate::data_structures::{CliffordTableau, HasAdjoint}; pub use custom_callback::CallbackCliffordSynthesizer; pub use naive::NaiveCliffordSynthesizer; +pub use permrowcol::PermRowColCliffordSynthesizer; mod custom_callback; mod helper; -pub mod naive; +mod naive; +mod permrowcol; #[derive(Default)] pub enum CliffordTableauSynthStrategy { diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs new file mode 100644 index 00000000..b5541561 --- /dev/null +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -0,0 +1,144 @@ +use std::fmt::Debug; + +use crate::{ + architecture::connectivity::Connectivity, + data_structures::{CliffordTableau, PauliLetter}, + ir::{ + clifford_tableau::{ + self, + helper::{ + clean_observables, clean_pivot, clean_prc, clean_x_pivot, clean_z_pivot, + pick_column, pick_row, + }, + }, + AdjointSynthesizer, CliffordGates, + }, +}; + +use super::helper::{ + clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, + swap, +}; + +#[derive(Default)] +pub struct PermRowColCliffordSynthesizer { + connectivity: Connectivity, + permutation: Vec, +} + +impl PermRowColCliffordSynthesizer { + pub fn new(connectivity: Connectivity) -> Self { + let size = connectivity.node_bound(); + + Self { + connectivity, + permutation: (0..size).collect(), + } + } + + pub fn permutation(&self) -> &[usize] { + &self.permutation + } +} + +impl AdjointSynthesizer for PermRowColCliffordSynthesizer +where + G: CliffordGates + Debug, +{ + fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { + let num_qubits = clifford_tableau.size(); + let machine_size = self.connectivity.node_bound(); + assert!( + num_qubits <= machine_size, + "Number of qubits {} exceeds machine size {}", + num_qubits, + machine_size + ); + // Mapping between logical qubit to physical qubit + let mut permutation = (0..num_qubits).collect::>(); + // logical qubit remaining to be disconnected + let mut remaining_columns = (0..num_qubits).collect::>(); + // stabilizers / destabilizers that are not yet identity rows + let mut remaining_rows = (0..num_qubits).collect::>(); + + while !remaining_columns.is_empty() { + let pivot_row = pick_row(&clifford_tableau, &self.connectivity, &remaining_rows); + let pivot_column = pick_column(&clifford_tableau, &self.connectivity); + println!("pivot row: {}", pivot_row); + println!("pivot column: {}", pivot_column); + let column = clifford_tableau.column(pivot_column); + let x_weight = column.x_weight(); + let z_weight = column.z_weight(); + + let (first_letter, second_letter) = if z_weight > x_weight { + (PauliLetter::Z, PauliLetter::X) + } else { + (PauliLetter::X, PauliLetter::Z) + }; + + remaining_columns.retain(|&x| x != pivot_column); + remaining_rows.retain(|&x| x != pivot_row); + { + clean_pivot( + repr, + &mut clifford_tableau, + pivot_column, + pivot_row, + first_letter, + ); + println!("Cleaning: {:?}", first_letter); + println!("repr: {:?}", repr); + println!("clifford_tableau: {}", clifford_tableau); + + // Use the pivot to remove all other terms in the X observable. + clean_prc( + repr, + &mut clifford_tableau, + &self.connectivity, + &remaining_columns, + pivot_column, + pivot_row, + first_letter, + ); + println!("PRC: {:?}", first_letter); + println!("repr: {:?}", repr); + println!("clifford_tableau: {}", clifford_tableau); + + clean_pivot( + repr, + &mut clifford_tableau, + pivot_column, + pivot_row, + second_letter, + ); + println!("Cleaning: {:?}", second_letter); + println!("repr: {:?}", repr); + println!("clifford_tableau: {}", clifford_tableau); + + // Use the pivot to remove all other terms in the Z observable. + clean_prc( + repr, + &mut clifford_tableau, + &self.connectivity, + &remaining_columns, + pivot_column, + pivot_row, + second_letter, + ); + println!("PRC: {:?}", second_letter); + println!("repr: {:?}", repr); + println!("clifford_tableau: {}", clifford_tableau); + } + + // If the pivot row is now an identity row, we can remove it from the tableau. + + permutation[pivot_row] = pivot_column; + self.connectivity.remove_node(pivot_column); + println!("nodes: {:?}", self.connectivity.nodes()); + } + + clean_signs(repr, &mut clifford_tableau, &permutation); + + self.permutation = permutation; + } +} From f84c0de0fe6d50a2696da66197bc6dbabe04d7a4 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:07:43 +0200 Subject: [PATCH 08/46] Add tests for PermRowCol for CliffordTableau --- synir/tests/clifford_tableau.rs | 63 +++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index 11d54d04..b615efb1 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -3,9 +3,13 @@ mod common; use bitvec::bitvec; use bitvec::prelude::Lsb0; use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; -use synir::ir::clifford_tableau::{CallbackCliffordSynthesizer, NaiveCliffordSynthesizer}; -use synir::ir::{AdjointSynthesizer, Synthesizer}; +use syn::architecture::connectivity::Connectivity; +use syn::architecture::Architecture; +use syn::data_structures::{CliffordTableau, PauliString, PropagateClifford}; +use syn::ir::clifford_tableau::{ + CallbackCliffordSynthesizer, NaiveCliffordSynthesizer, PermRowColCliffordSynthesizer, +}; +use syn::ir::{AdjointSynthesizer, CliffordGates, Synthesizer}; fn setup_sample_ct() -> CliffordTableau { // Stab: ZZZ, -YIY, XIX @@ -239,3 +243,56 @@ fn test_custom_clifford_synthesis_simple() { let ref_ct = parse_clifford_commands(3, mock.commands()); assert_eq!(clifford_tableau, ref_ct); } + +#[test] +fn test_prc_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let num_qubits = clifford_tableau.size(); + let mut mock = MockCircuit::new(); + let connectivity = Connectivity::complete(num_qubits); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let mut ref_ct = parse_clifford_commands(3, mock.commands()); + ref_ct.permute(synthesizer.permutation()); + println!("ref_ct: {}", ref_ct); + println!("clifford_tableau: {}", clifford_tableau); + assert_eq!(clifford_tableau, ref_ct); +} + +#[test] +fn test_prc_clifford_synthesis_large() { + let mut clifford_tableau = setup_sample_inverse_ct(); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::grid(2, 2); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(4, mock.commands()); + clifford_tableau.permute(synthesizer.permutation()); + + assert_eq!(clifford_tableau, ref_ct); +} + +#[test] +fn test_prc_clifford_synthesis_simple() { + let num_qubits = 3; + let mut clifford_tableau = CliffordTableau::new(num_qubits); + + clifford_tableau.cx(2, 1); + clifford_tableau.cx(1, 2); + clifford_tableau.cx(0, 2); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::line(num_qubits); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + + clifford_tableau.permute(synthesizer.permutation()); + assert_eq!(clifford_tableau, ref_ct); +} From 98a504cf2d5978b60ca0a13c78b020d5565af654 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:17:08 +0200 Subject: [PATCH 09/46] Update benchmark code Remove unused imports. Switch deprecated functions to new names. Remove uneeded references. --- synir/benches/clifford_tableau.rs | 13 ++++++------- synir/benches/connectivity.rs | 15 +++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/synir/benches/clifford_tableau.rs b/synir/benches/clifford_tableau.rs index dc53d556..fc33bfa2 100644 --- a/synir/benches/clifford_tableau.rs +++ b/synir/benches/clifford_tableau.rs @@ -2,13 +2,12 @@ use crate::connectivity::connectivity_benchmark; use bitvec::bitvec; use bitvec::prelude::Lsb0; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use synir::data_structures::CliffordTableau; -use synir::data_structures::PauliString; -use synir::ir::clifford_tableau::naive::NaiveCliffordSynthesizer; -use synir::ir::clifford_tableau::CallbackCliffordSynthesizer; -use synir::ir::CliffordGates; -use synir::ir::Synthesizer; -use synir::IndexType; +use syn::data_structures::CliffordTableau; +use syn::data_structures::PauliString; +use syn::ir::clifford_tableau::CallbackCliffordSynthesizer; +use syn::ir::clifford_tableau::NaiveCliffordSynthesizer; +use syn::ir::CliffordGates; +use syn::ir::Synthesizer; mod connectivity; diff --git a/synir/benches/connectivity.rs b/synir/benches/connectivity.rs index b2a9903a..7ca72338 100644 --- a/synir/benches/connectivity.rs +++ b/synir/benches/connectivity.rs @@ -1,17 +1,16 @@ use criterion::{black_box, criterion_group, Criterion}; -use petgraph::visit::Walker; use rand::prelude::IndexedRandom; use rand::seq::SliceRandom; -use rand::Rng; -use synir::architecture::connectivity::Connectivity; -use synir::architecture::Architecture; +use rand::{rng, Rng}; +use syn::architecture::connectivity::Connectivity; +use syn::architecture::Architecture; fn random_connected_connectivity( num_nodes: usize, extra_edges: usize, subset_length: usize, ) -> (Connectivity, Vec, usize) { - let mut rng = rand::thread_rng(); + let mut rng = rng(); let mut edges = Vec::new(); let mut nodes: Vec = (0..num_nodes).collect(); @@ -22,8 +21,8 @@ fn random_connected_connectivity( // Add extra random edges: while edges.len() < (num_nodes - 1) + extra_edges { - let a = rng.gen_range(0..num_nodes); - let b = rng.gen_range(0..num_nodes); + let a = rng.random_range(0..num_nodes); + let b = rng.random_range(0..num_nodes); if a != b && !edges.contains(&(a, b)) && !edges.contains(&(b, a)) { edges.push((a, b)); } @@ -39,7 +38,7 @@ fn random_connected_connectivity( } fn get_cx_ladder_connectivity((connectivity, nodes, root): &(Connectivity, Vec, usize)) { - let _ = connectivity.get_cx_ladder(&nodes, &root); + let _ = connectivity.get_cx_ladder(nodes, root); } pub fn connectivity_bench(c: &mut Criterion) { From 29f2a00bf6906a90262704201c522ca2053875fa Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:17:44 +0200 Subject: [PATCH 10/46] Use alias for complex type in CliffordCallback --- synir/src/ir/clifford_tableau/custom_callback.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index b21251ca..f1a2638b 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -12,14 +12,13 @@ use crate::{ use super::helper::{clean_x_observables, clean_z_observables}; +type CliffordCallBack = Box (usize, usize)>; pub struct CallbackCliffordSynthesizer { - custom_callback: Box (usize, usize)>, + custom_callback: CliffordCallBack, } impl CallbackCliffordSynthesizer { - pub fn new( - custom_callback: Box (usize, usize)>, - ) -> Self { + pub fn new(custom_callback: CliffordCallBack) -> Self { Self { custom_callback } } @@ -46,10 +45,7 @@ impl Default for CallbackCliffordSynthesizer { } impl CallbackCliffordSynthesizer { - pub fn set_custom_callback( - &mut self, - callback: Box (usize, usize)>, - ) -> &mut Self { + pub fn set_custom_callback(&mut self, callback: CliffordCallBack) -> &mut Self { self.custom_callback = callback; self } From 38920134104e499980810c157848eb4934f0afca Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:17:58 +0200 Subject: [PATCH 11/46] Remove unused lifetime in CallbackCliffordSynthesizer --- synir/src/ir/clifford_tableau/custom_callback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index f1a2638b..4fabeb44 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -51,7 +51,7 @@ impl CallbackCliffordSynthesizer { } } -impl<'a, G> AdjointSynthesizer for CallbackCliffordSynthesizer +impl AdjointSynthesizer for CallbackCliffordSynthesizer where G: CliffordGates, { From a6fd07c31a70200056d84c5399fc5082cd97ed50 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:18:20 +0200 Subject: [PATCH 12/46] Remove unused import --- synir/src/ir/clifford_tableau/helper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index a453c6e6..507adb4e 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -1,4 +1,4 @@ -use std::{iter::zip, usize}; +use std::iter::zip; use crate::{ architecture::{connectivity::Connectivity, Architecture}, From dd3601a1fbbfff25a0893ae3080427811d24a26c Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:18:30 +0200 Subject: [PATCH 13/46] Remove println statement --- synir/src/ir/clifford_tableau/helper.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 507adb4e..3cfccd99 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -454,7 +454,6 @@ pub(super) fn clean_x_prc( .unwrap(); let affected_cols = check_across_columns(&*clifford_tableau, &terminals, pivot_row, is_y); - println!("affected cols: {:?}", affected_cols); for col in affected_cols { repr.s(col); clifford_tableau.s(col); From 3ac86d0cf83afdb37e6064b64bb9b6e63e87ad63 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 4 Jul 2025 14:18:42 +0200 Subject: [PATCH 14/46] Remove unused imports and print statements --- synir/src/ir/clifford_tableau/permrowcol.rs | 28 ++------------------- synir/tests/clifford_tableau.rs | 6 ++--- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index b5541561..d7379e1e 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -4,21 +4,12 @@ use crate::{ architecture::connectivity::Connectivity, data_structures::{CliffordTableau, PauliLetter}, ir::{ - clifford_tableau::{ - self, - helper::{ - clean_observables, clean_pivot, clean_prc, clean_x_pivot, clean_z_pivot, - pick_column, pick_row, - }, - }, + clifford_tableau::helper::{clean_pivot, clean_prc, pick_column, pick_row}, AdjointSynthesizer, CliffordGates, }, }; -use super::helper::{ - clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, - swap, -}; +use super::helper::clean_signs; #[derive(Default)] pub struct PermRowColCliffordSynthesizer { @@ -64,8 +55,6 @@ where while !remaining_columns.is_empty() { let pivot_row = pick_row(&clifford_tableau, &self.connectivity, &remaining_rows); let pivot_column = pick_column(&clifford_tableau, &self.connectivity); - println!("pivot row: {}", pivot_row); - println!("pivot column: {}", pivot_column); let column = clifford_tableau.column(pivot_column); let x_weight = column.x_weight(); let z_weight = column.z_weight(); @@ -86,9 +75,6 @@ where pivot_row, first_letter, ); - println!("Cleaning: {:?}", first_letter); - println!("repr: {:?}", repr); - println!("clifford_tableau: {}", clifford_tableau); // Use the pivot to remove all other terms in the X observable. clean_prc( @@ -100,9 +86,6 @@ where pivot_row, first_letter, ); - println!("PRC: {:?}", first_letter); - println!("repr: {:?}", repr); - println!("clifford_tableau: {}", clifford_tableau); clean_pivot( repr, @@ -111,9 +94,6 @@ where pivot_row, second_letter, ); - println!("Cleaning: {:?}", second_letter); - println!("repr: {:?}", repr); - println!("clifford_tableau: {}", clifford_tableau); // Use the pivot to remove all other terms in the Z observable. clean_prc( @@ -125,16 +105,12 @@ where pivot_row, second_letter, ); - println!("PRC: {:?}", second_letter); - println!("repr: {:?}", repr); - println!("clifford_tableau: {}", clifford_tableau); } // If the pivot row is now an identity row, we can remove it from the tableau. permutation[pivot_row] = pivot_column; self.connectivity.remove_node(pivot_column); - println!("nodes: {:?}", self.connectivity.nodes()); } clean_signs(repr, &mut clifford_tableau, &permutation); diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index b615efb1..c8b4d529 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -4,12 +4,11 @@ use bitvec::bitvec; use bitvec::prelude::Lsb0; use common::{parse_clifford_commands, MockCircuit, MockCommand}; use syn::architecture::connectivity::Connectivity; -use syn::architecture::Architecture; use syn::data_structures::{CliffordTableau, PauliString, PropagateClifford}; use syn::ir::clifford_tableau::{ CallbackCliffordSynthesizer, NaiveCliffordSynthesizer, PermRowColCliffordSynthesizer, }; -use syn::ir::{AdjointSynthesizer, CliffordGates, Synthesizer}; +use syn::ir::{AdjointSynthesizer, Synthesizer}; fn setup_sample_ct() -> CliffordTableau { // Stab: ZZZ, -YIY, XIX @@ -255,8 +254,7 @@ fn test_prc_clifford_synthesis() { let mut ref_ct = parse_clifford_commands(3, mock.commands()); ref_ct.permute(synthesizer.permutation()); - println!("ref_ct: {}", ref_ct); - println!("clifford_tableau: {}", clifford_tableau); + assert_eq!(clifford_tableau, ref_ct); } From 4492cb85542c3b08af8dcc8c271f2661e5ed04e4 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Tue, 12 Aug 2025 17:50:05 +0200 Subject: [PATCH 15/46] Update test for architecture --- synir/src/architecture/connectivity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synir/src/architecture/connectivity.rs b/synir/src/architecture/connectivity.rs index 48e88152..3afccdcd 100644 --- a/synir/src/architecture/connectivity.rs +++ b/synir/src/architecture/connectivity.rs @@ -418,7 +418,7 @@ mod tests { fn test_best_simple_path() { let new_architecture = Connectivity::from_edges(&setup_simple()); - assert_eq!(new_architecture.best_path(0, 4), vec![0, 1, 2, 4]); + assert_eq!(new_architecture.best_path(0, 4), vec![0, 5, 4]); } #[test] From 4bde6d9397fe06fd479e1b643949973ebc11e296 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Thu, 14 Aug 2025 12:57:52 +0200 Subject: [PATCH 16/46] Set function as public --- synir/src/data_structures/clifford_tableau.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index 4f70e54a..a0ea5d0b 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -81,7 +81,7 @@ impl CliffordTableau { &self.pauli_columns } - pub(crate) fn compose(&self, rhs: &Self) -> Self { + pub fn compose(&self, rhs: &Self) -> Self { rhs.prepend(self) } From 7ac2de6ac8ef2ee122eee577b2575e8023b769d2 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 20 Sep 2025 10:57:35 +0200 Subject: [PATCH 17/46] Rename edge_ and node_bound to edge_ and node_count --- synir/src/architecture/connectivity.rs | 4 ++-- synir/src/ir/clifford_tableau/permrowcol.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/synir/src/architecture/connectivity.rs b/synir/src/architecture/connectivity.rs index 3afccdcd..8095e3ae 100644 --- a/synir/src/architecture/connectivity.rs +++ b/synir/src/architecture/connectivity.rs @@ -110,11 +110,11 @@ impl Connectivity { .collect() } - pub fn node_bound(&self) -> usize { + pub fn node_count(&self) -> usize { self.graph.node_count() } - pub fn edge_bound(&self) -> usize { + pub fn edge_count(&self) -> usize { self.graph.edge_count() } diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index d7379e1e..d2946863 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -19,7 +19,7 @@ pub struct PermRowColCliffordSynthesizer { impl PermRowColCliffordSynthesizer { pub fn new(connectivity: Connectivity) -> Self { - let size = connectivity.node_bound(); + let size = connectivity.node_count(); Self { connectivity, @@ -38,7 +38,7 @@ where { fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { let num_qubits = clifford_tableau.size(); - let machine_size = self.connectivity.node_bound(); + let machine_size = self.connectivity.node_count(); assert!( num_qubits <= machine_size, "Number of qubits {} exceeds machine size {}", From 05539f83816629cb2b4c68aa912eef5682efb218 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 20 Sep 2025 11:02:44 +0200 Subject: [PATCH 18/46] Add clearer error messages calling Clifford Tableau functions --- synir/src/data_structures/clifford_tableau.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index a0ea5d0b..b2f44a1b 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -55,7 +55,7 @@ impl CliffordTableau { pub(crate) fn destabilizer(&self, qubit: usize, index: usize) -> PauliLetter { let n = self.size(); - assert!(index < n && qubit < n); + assert!(index < n && qubit < n, "Given index: ${index} or qubit: ${qubit}$ out of bounds for clifford tableau of size ${n}$"); PauliLetter::new( self.pauli_columns[qubit].x(index), @@ -65,7 +65,7 @@ impl CliffordTableau { pub(crate) fn stabilizer(&self, qubit: usize, index: usize) -> PauliLetter { let n = self.size(); - assert!(index < n && qubit < n); + assert!(index < n && qubit < n, "Given index: ${index} or qubit: ${qubit}$ out of bounds for clifford tableau of size ${n}$"); PauliLetter::new( self.pauli_columns[qubit].x(index + n), From d8cc29022f8d2433a047af336c642ba7eb00ded8 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Mon, 6 Oct 2025 17:06:56 +0200 Subject: [PATCH 19/46] Replace clean_naive_pivot with clean_pivot --- synir/src/ir/clifford_tableau/naive.rs | 9 +++++-- synir/tests/clifford_tableau.rs | 7 ++---- synir/tests/pauli_exponential.rs | 34 ++++++++++++-------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 67628193..142dc853 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -4,10 +4,12 @@ use crate::{ }; use super::helper::{ - clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, + clean_pivot, clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, swap, }; +use crate::data_structures::PauliLetter; + #[derive(Default)] pub struct NaiveCliffordSynthesizer {} @@ -26,13 +28,16 @@ where } // Cleanup pivot column - clean_naive_pivot(repr, &mut clifford_tableau, row, row); + // clean_naive_pivot(repr, &mut clifford_tableau, row, row); + clean_pivot(repr, &mut clifford_tableau, row, row, PauliLetter::X); let checked_rows = (row + 1..num_qubits).collect::>(); // Use the pivot to remove all other terms in the X observable. clean_x_observables(repr, &mut clifford_tableau, &checked_rows, row, row); + clean_pivot(repr, &mut clifford_tableau, row, row, PauliLetter::Z); + // Use the pivot to remove all other terms in the Z observable. clean_z_observables(repr, &mut clifford_tableau, &checked_rows, row, row); } diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index c8b4d529..eacf8ec3 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -111,10 +111,7 @@ fn test_v_synthesis() { assert_eq!( mock.commands(), &vec![ - MockCommand::S(1), - MockCommand::H(1), - MockCommand::S(1), - MockCommand::X(1) + MockCommand::V(1), ] ); } @@ -132,7 +129,7 @@ fn test_v_adjoint_synthesis() { assert_eq!( mock.commands(), - &vec![MockCommand::S(1), MockCommand::H(1), MockCommand::S(1),] + &vec![MockCommand::V(1), MockCommand::X(1)] ); } diff --git a/synir/tests/pauli_exponential.rs b/synir/tests/pauli_exponential.rs index 161b5f4f..9f72c082 100644 --- a/synir/tests/pauli_exponential.rs +++ b/synir/tests/pauli_exponential.rs @@ -3,12 +3,12 @@ mod common; use std::collections::VecDeque; use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use synir::data_structures::{CliffordTableau, PauliPolynomial}; -use synir::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; -use synir::ir::pauli_exponential::PauliExponential; -use synir::ir::pauli_exponential::PauliExponentialSynthesizer; -use synir::ir::pauli_polynomial::PauliPolynomialSynthStrategy; -use synir::ir::Synthesizer; +use syn::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; +use syn::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; +use syn::ir::pauli_exponential::PauliExponential; +use syn::ir::pauli_exponential::PauliExponentialSynthesizer; +use syn::ir::pauli_polynomial::PauliPolynomialSynthStrategy; +use syn::ir::Synthesizer; fn setup_simple_pe() -> PauliExponential { let ham = vec![("IZZZ", 0.3)]; @@ -79,29 +79,28 @@ fn test_naive_pauli_exponential_complex() { let mut cliff_synthesizer = NaiveCliffordSynthesizer::default(); - cliff_synthesizer.synthesize(ref_ct.clone(), &mut mock_ct); + cliff_synthesizer.synthesize(ref_ct.clone().adjoint(), &mut mock_ct); let mock_ct_ref_commands = [ - MockCommand::H(1), MockCommand::CX(0, 1), MockCommand::CX(0, 2), + MockCommand::CX(0, 3), MockCommand::H(1), MockCommand::H(2), + MockCommand::H(3), MockCommand::CX(1, 0), MockCommand::CX(2, 0), - MockCommand::H(1), - MockCommand::S(1), - MockCommand::CX(1, 2), + MockCommand::CX(3, 0), + MockCommand::V(1), + MockCommand::V(3), + MockCommand::CX(2, 1), + MockCommand::CX(3, 1), MockCommand::CX(3, 2), MockCommand::CX(2, 3), MockCommand::CX(3, 2), - MockCommand::H(2), MockCommand::CX(3, 2), - MockCommand::S(3), MockCommand::X(1), MockCommand::X(2), - MockCommand::X(3), - MockCommand::Z(1), ]; assert_eq!(mock_ct.commands(), &mock_ct_ref_commands); @@ -131,9 +130,7 @@ fn test_naive_pauli_exponential_complex() { MockCommand::CX(1, 0), MockCommand::CX(2, 0), MockCommand::CX(3, 0), - MockCommand::S(1), - MockCommand::H(1), - MockCommand::S(1), + MockCommand::V(1), MockCommand::V(3), MockCommand::CX(2, 1), MockCommand::CX(3, 1), @@ -141,6 +138,7 @@ fn test_naive_pauli_exponential_complex() { MockCommand::CX(2, 3), MockCommand::CX(3, 2), MockCommand::CX(3, 2), + MockCommand::X(1), MockCommand::X(2), ]; From dd429478e428feec3a279701461090059467d29c Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Mon, 6 Oct 2025 17:06:28 +0200 Subject: [PATCH 20/46] Add example from syn into synpy integration tests --- synpy/integration_tests/test_pauliopt.py | 33 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/synpy/integration_tests/test_pauliopt.py b/synpy/integration_tests/test_pauliopt.py index ed22b499..49686c47 100644 --- a/synpy/integration_tests/test_pauliopt.py +++ b/synpy/integration_tests/test_pauliopt.py @@ -5,6 +5,7 @@ from pauliopt.pauli.pauli_gadget import PauliGadget, PPhase from pauliopt.pauli.pauli_polynomial import PauliPolynomial from pauliopt.pauli_strings import I, X, Y, Z +# from pauliopt.pauli.utils import X, Y, Z, I from qiskit import QuantumCircuit from qiskit.quantum_info import Operator @@ -22,7 +23,8 @@ def create_random_phase_gadget(num_qubits: int, min_legs: int, max_legs: int, al """ angle = np.random.choice(allowed_angels) nr_legs = np.random.randint(min_legs, max_legs) - legs = np.random.choice([i for i in range(num_qubits)], size=nr_legs, replace=False) + legs = np.random.choice( + [i for i in range(num_qubits)], size=nr_legs, replace=False) phase_gadget = [I for _ in range(num_qubits)] for leg in legs: phase_gadget[leg] = np.random.choice([X, Y, Z]) @@ -46,11 +48,25 @@ def generate_random_pauli_polynomial( if max_legs is None: max_legs = num_qubits if allowed_angles is None: - allowed_angles = [2 * np.pi, np.pi, 0.5 * np.pi, 0.25 * np.pi, 0.125 * np.pi] + allowed_angles = [2 * np.pi, np.pi, 0.5 * + np.pi, 0.25 * np.pi, 0.125 * np.pi] pp = PauliPolynomial(num_qubits) for _ in range(num_gadgets): - pp >>= create_random_phase_gadget(num_qubits, min_legs, max_legs, allowed_angles) + pp >>= create_random_phase_gadget( + num_qubits, min_legs, max_legs, allowed_angles) + + return pp + + +def generate_syn_example() -> PauliPolynomial: + """ + Generate pauli polynomial use in syn tests. + """ + pp = PauliPolynomial(4) + pp >>= PPhase(0.3) @ [I, X, Y, Z] + pp >>= PPhase(0.7) @ [X, X, I, I] + pp >>= PPhase(0.12) @ [Y, Y, I, I] return pp @@ -69,7 +85,16 @@ class TestBasicSyn: @pytest.mark.parametrize("nr_qubits", [3, 6]) @pytest.mark.parametrize("nr_gadgets", [100, 200, 500, 1000]) def test_naive_synthesis(self, nr_qubits: int, nr_gadgets: int) -> None: - pauli_polynomial = generate_random_pauli_polynomial(nr_qubits, nr_gadgets) + pauli_polynomial = generate_random_pauli_polynomial( + nr_qubits, nr_gadgets) + qc = pauli_polynomial.copy().to_qiskit() + + qc_syn = PauliOptSynthesizer().synthesize(pauli_polynomial).to_qiskit() + + assert verify_equality(qc, qc_syn) + + def test_example_synthesis(self) -> None: + pauli_polynomial = generate_syn_example() qc = pauli_polynomial.copy().to_qiskit() qc_syn = PauliOptSynthesizer().synthesize(pauli_polynomial).to_qiskit() From d684b901ee98ece167db470edf57e3d4ed7b94de Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Mon, 6 Oct 2025 17:08:02 +0200 Subject: [PATCH 21/46] Add PermRowCol as strategy in pauli_exponential.rs --- synir/src/ir/clifford_tableau.rs | 1 + synir/src/ir/clifford_tableau/permrowcol.rs | 2 +- synir/src/ir/pauli_exponential.rs | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/synir/src/ir/clifford_tableau.rs b/synir/src/ir/clifford_tableau.rs index 9d26eef8..9de1dbb0 100644 --- a/synir/src/ir/clifford_tableau.rs +++ b/synir/src/ir/clifford_tableau.rs @@ -14,6 +14,7 @@ mod permrowcol; pub enum CliffordTableauSynthStrategy { #[default] Naive, + PermRowCol, Custom(Vec, Vec), } diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index d2946863..616d9cc3 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -34,7 +34,7 @@ impl PermRowColCliffordSynthesizer { impl AdjointSynthesizer for PermRowColCliffordSynthesizer where - G: CliffordGates + Debug, + G: CliffordGates, { fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { let num_qubits = clifford_tableau.size(); diff --git a/synir/src/ir/pauli_exponential.rs b/synir/src/ir/pauli_exponential.rs index ac3824e3..79be5518 100644 --- a/synir/src/ir/pauli_exponential.rs +++ b/synir/src/ir/pauli_exponential.rs @@ -1,11 +1,12 @@ use std::collections::VecDeque; +use crate::architecture::connectivity::Connectivity; use crate::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; use crate::ir::{CliffordGates, Gates, Synthesizer}; -use crate::ir::clifford_tableau::CallbackCliffordSynthesizer; use crate::ir::clifford_tableau::NaiveCliffordSynthesizer; +use crate::ir::clifford_tableau::{CallbackCliffordSynthesizer, PermRowColCliffordSynthesizer}; use crate::ir::{ clifford_tableau::CliffordTableauSynthStrategy, pauli_polynomial::{naive::NaivePauliPolynomialSynthesizer, PauliPolynomialSynthStrategy}, @@ -93,6 +94,12 @@ where ); clifford_synthesizer.synthesize(clifford_tableau.adjoint(), repr); } + CliffordTableauSynthStrategy::PermRowCol => { + let size = clifford_tableau.size(); + let mut clifford_synthesizer = + PermRowColCliffordSynthesizer::new(Connectivity::complete(size)); + clifford_synthesizer.synthesize(clifford_tableau.adjoint(), repr); + } }; } } From f0cfd7115fa3e56211ec0417a39f844d539fa0a6 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Mon, 6 Oct 2025 17:52:49 +0200 Subject: [PATCH 22/46] Add ability to modify row and column selection strategy for PermRowColCliffordSynthesizer --- synir/src/ir/clifford_tableau/permrowcol.rs | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index 616d9cc3..1cc6d8eb 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -11,10 +11,23 @@ use crate::{ use super::helper::clean_signs; -#[derive(Default)] +// #[derive(Default)] pub struct PermRowColCliffordSynthesizer { connectivity: Connectivity, permutation: Vec, + row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, + column_strategy: fn(&CliffordTableau, &Connectivity) -> usize, +} + +impl Default for PermRowColCliffordSynthesizer { + fn default() -> Self { + PermRowColCliffordSynthesizer { + connectivity: Connectivity::default(), + permutation: Vec::::default(), + row_strategy: pick_row, + column_strategy: pick_column, + } + } } impl PermRowColCliffordSynthesizer { @@ -24,12 +37,28 @@ impl PermRowColCliffordSynthesizer { Self { connectivity, permutation: (0..size).collect(), + row_strategy: pick_row, + column_strategy: pick_column, } } pub fn permutation(&self) -> &[usize] { &self.permutation } + + pub fn set_row_strategy( + &mut self, + row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, + ) { + (self.row_strategy) = row_strategy; + } + + pub fn set_column_strategy( + &mut self, + column_strategy: fn(&CliffordTableau, &Connectivity) -> usize, + ) { + (self.column_strategy) = column_strategy; + } } impl AdjointSynthesizer for PermRowColCliffordSynthesizer @@ -53,8 +82,9 @@ where let mut remaining_rows = (0..num_qubits).collect::>(); while !remaining_columns.is_empty() { - let pivot_row = pick_row(&clifford_tableau, &self.connectivity, &remaining_rows); - let pivot_column = pick_column(&clifford_tableau, &self.connectivity); + let pivot_row = + (self.row_strategy)(&clifford_tableau, &self.connectivity, &remaining_rows); + let pivot_column = (self.column_strategy)(&clifford_tableau, &self.connectivity); let column = clifford_tableau.column(pivot_column); let x_weight = column.x_weight(); let z_weight = column.z_weight(); From 99a5ac953de5b67c59e135276311395450488304 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Tue, 7 Oct 2025 17:12:45 +0200 Subject: [PATCH 23/46] Convert from syn to synir --- synir/benches/clifford_tableau.rs | 13 +++++++------ synir/benches/connectivity.rs | 4 ++-- synir/tests/clifford_tableau.rs | 20 ++++++-------------- synir/tests/pauli_exponential.rs | 12 ++++++------ 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/synir/benches/clifford_tableau.rs b/synir/benches/clifford_tableau.rs index fc33bfa2..7acb33e2 100644 --- a/synir/benches/clifford_tableau.rs +++ b/synir/benches/clifford_tableau.rs @@ -2,12 +2,13 @@ use crate::connectivity::connectivity_benchmark; use bitvec::bitvec; use bitvec::prelude::Lsb0; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use syn::data_structures::CliffordTableau; -use syn::data_structures::PauliString; -use syn::ir::clifford_tableau::CallbackCliffordSynthesizer; -use syn::ir::clifford_tableau::NaiveCliffordSynthesizer; -use syn::ir::CliffordGates; -use syn::ir::Synthesizer; +use synir::data_structures::CliffordTableau; +use synir::data_structures::PauliString; +use synir::ir::clifford_tableau::CallbackCliffordSynthesizer; +use synir::ir::clifford_tableau::NaiveCliffordSynthesizer; +use synir::ir::CliffordGates; +use synir::ir::Synthesizer; +use synir::IndexType; mod connectivity; diff --git a/synir/benches/connectivity.rs b/synir/benches/connectivity.rs index 7ca72338..08f6de24 100644 --- a/synir/benches/connectivity.rs +++ b/synir/benches/connectivity.rs @@ -2,8 +2,8 @@ use criterion::{black_box, criterion_group, Criterion}; use rand::prelude::IndexedRandom; use rand::seq::SliceRandom; use rand::{rng, Rng}; -use syn::architecture::connectivity::Connectivity; -use syn::architecture::Architecture; +use synir::architecture::connectivity::Connectivity; +use synir::architecture::Architecture; fn random_connected_connectivity( num_nodes: usize, diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index eacf8ec3..f7492d54 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -3,12 +3,12 @@ mod common; use bitvec::bitvec; use bitvec::prelude::Lsb0; use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use syn::architecture::connectivity::Connectivity; -use syn::data_structures::{CliffordTableau, PauliString, PropagateClifford}; -use syn::ir::clifford_tableau::{ +use synir::architecture::connectivity::Connectivity; +use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; +use synir::ir::clifford_tableau::{ CallbackCliffordSynthesizer, NaiveCliffordSynthesizer, PermRowColCliffordSynthesizer, }; -use syn::ir::{AdjointSynthesizer, Synthesizer}; +use synir::ir::{AdjointSynthesizer, Synthesizer}; fn setup_sample_ct() -> CliffordTableau { // Stab: ZZZ, -YIY, XIX @@ -108,12 +108,7 @@ fn test_v_synthesis() { let mut synthesizer = NaiveCliffordSynthesizer::default(); synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::V(1), - ] - ); + assert_eq!(mock.commands(), &vec![MockCommand::V(1),]); } #[test] @@ -127,10 +122,7 @@ fn test_v_adjoint_synthesis() { let ref_ct = parse_clifford_commands(2, mock.commands()); assert_eq!(clifford_tableau * ref_ct, CliffordTableau::new(2)); - assert_eq!( - mock.commands(), - &vec![MockCommand::V(1), MockCommand::X(1)] - ); + assert_eq!(mock.commands(), &vec![MockCommand::V(1), MockCommand::X(1)]); } #[test] diff --git a/synir/tests/pauli_exponential.rs b/synir/tests/pauli_exponential.rs index 9f72c082..ab309527 100644 --- a/synir/tests/pauli_exponential.rs +++ b/synir/tests/pauli_exponential.rs @@ -3,12 +3,12 @@ mod common; use std::collections::VecDeque; use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use syn::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; -use syn::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; -use syn::ir::pauli_exponential::PauliExponential; -use syn::ir::pauli_exponential::PauliExponentialSynthesizer; -use syn::ir::pauli_polynomial::PauliPolynomialSynthStrategy; -use syn::ir::Synthesizer; +use synir::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; +use synir::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; +use synir::ir::pauli_exponential::PauliExponential; +use synir::ir::pauli_exponential::PauliExponentialSynthesizer; +use synir::ir::pauli_polynomial::PauliPolynomialSynthStrategy; +use synir::ir::Synthesizer; fn setup_simple_pe() -> PauliExponential { let ham = vec![("IZZZ", 0.3)]; From b0f56e8dc357abbc8644cdcc7c0e313f56634a39 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Tue, 7 Oct 2025 17:17:05 +0200 Subject: [PATCH 24/46] Update formatting of clifford_tableau/naive.rs --- synir/src/ir/clifford_tableau/naive.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 142dc853..47b0078e 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -4,8 +4,7 @@ use crate::{ }; use super::helper::{ - clean_pivot, clean_naive_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, - swap, + clean_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, swap, }; use crate::data_structures::PauliLetter; @@ -37,7 +36,7 @@ where clean_x_observables(repr, &mut clifford_tableau, &checked_rows, row, row); clean_pivot(repr, &mut clifford_tableau, row, row, PauliLetter::Z); - + // Use the pivot to remove all other terms in the Z observable. clean_z_observables(repr, &mut clifford_tableau, &checked_rows, row, row); } From 13235f7b96116769ae1b38c005a09944c437e858 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Tue, 7 Oct 2025 18:07:09 +0200 Subject: [PATCH 25/46] Formatted test_pauliopt.py --- synpy/integration_tests/test_pauliopt.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/synpy/integration_tests/test_pauliopt.py b/synpy/integration_tests/test_pauliopt.py index 49686c47..f23fec63 100644 --- a/synpy/integration_tests/test_pauliopt.py +++ b/synpy/integration_tests/test_pauliopt.py @@ -5,6 +5,7 @@ from pauliopt.pauli.pauli_gadget import PauliGadget, PPhase from pauliopt.pauli.pauli_polynomial import PauliPolynomial from pauliopt.pauli_strings import I, X, Y, Z + # from pauliopt.pauli.utils import X, Y, Z, I from qiskit import QuantumCircuit from qiskit.quantum_info import Operator @@ -23,8 +24,7 @@ def create_random_phase_gadget(num_qubits: int, min_legs: int, max_legs: int, al """ angle = np.random.choice(allowed_angels) nr_legs = np.random.randint(min_legs, max_legs) - legs = np.random.choice( - [i for i in range(num_qubits)], size=nr_legs, replace=False) + legs = np.random.choice([i for i in range(num_qubits)], size=nr_legs, replace=False) phase_gadget = [I for _ in range(num_qubits)] for leg in legs: phase_gadget[leg] = np.random.choice([X, Y, Z]) @@ -48,13 +48,11 @@ def generate_random_pauli_polynomial( if max_legs is None: max_legs = num_qubits if allowed_angles is None: - allowed_angles = [2 * np.pi, np.pi, 0.5 * - np.pi, 0.25 * np.pi, 0.125 * np.pi] + allowed_angles = [2 * np.pi, np.pi, 0.5 * np.pi, 0.25 * np.pi, 0.125 * np.pi] pp = PauliPolynomial(num_qubits) for _ in range(num_gadgets): - pp >>= create_random_phase_gadget( - num_qubits, min_legs, max_legs, allowed_angles) + pp >>= create_random_phase_gadget(num_qubits, min_legs, max_legs, allowed_angles) return pp @@ -85,8 +83,7 @@ class TestBasicSyn: @pytest.mark.parametrize("nr_qubits", [3, 6]) @pytest.mark.parametrize("nr_gadgets", [100, 200, 500, 1000]) def test_naive_synthesis(self, nr_qubits: int, nr_gadgets: int) -> None: - pauli_polynomial = generate_random_pauli_polynomial( - nr_qubits, nr_gadgets) + pauli_polynomial = generate_random_pauli_polynomial(nr_qubits, nr_gadgets) qc = pauli_polynomial.copy().to_qiskit() qc_syn = PauliOptSynthesizer().synthesize(pauli_polynomial).to_qiskit() From fc94cd1f3fd0d4b3b689bb5c688f96ec7d7e5fa9 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 13:58:17 +0200 Subject: [PATCH 26/46] Remove derived Default for Connectivity and PermRowCol synthesizer --- synir/src/architecture/connectivity.rs | 2 +- synir/src/ir/clifford_tableau/permrowcol.rs | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/synir/src/architecture/connectivity.rs b/synir/src/architecture/connectivity.rs index 8095e3ae..a11cc5dc 100644 --- a/synir/src/architecture/connectivity.rs +++ b/synir/src/architecture/connectivity.rs @@ -28,7 +28,7 @@ fn get_non_cutting_vertices( .collect() } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Connectivity { graph: StableUnGraph, non_cutting: Vec, diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index 1cc6d8eb..9cb1b27b 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -19,17 +19,6 @@ pub struct PermRowColCliffordSynthesizer { column_strategy: fn(&CliffordTableau, &Connectivity) -> usize, } -impl Default for PermRowColCliffordSynthesizer { - fn default() -> Self { - PermRowColCliffordSynthesizer { - connectivity: Connectivity::default(), - permutation: Vec::::default(), - row_strategy: pick_row, - column_strategy: pick_column, - } - } -} - impl PermRowColCliffordSynthesizer { pub fn new(connectivity: Connectivity) -> Self { let size = connectivity.node_count(); From a0ff746e3acae78aebc070fecc385d0e985b2979 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 13:59:47 +0200 Subject: [PATCH 27/46] Remove unneeded dollar signs from assert message --- synir/src/data_structures/clifford_tableau.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index b2f44a1b..32be62f4 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -55,7 +55,10 @@ impl CliffordTableau { pub(crate) fn destabilizer(&self, qubit: usize, index: usize) -> PauliLetter { let n = self.size(); - assert!(index < n && qubit < n, "Given index: ${index} or qubit: ${qubit}$ out of bounds for clifford tableau of size ${n}$"); + assert!( + index < n && qubit < n, + "Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {n}" + ); PauliLetter::new( self.pauli_columns[qubit].x(index), @@ -65,7 +68,10 @@ impl CliffordTableau { pub(crate) fn stabilizer(&self, qubit: usize, index: usize) -> PauliLetter { let n = self.size(); - assert!(index < n && qubit < n, "Given index: ${index} or qubit: ${qubit}$ out of bounds for clifford tableau of size ${n}$"); + assert!( + index < n && qubit < n, + "Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {n}" + ); PauliLetter::new( self.pauli_columns[qubit].x(index + n), From eba83428aa4c728969f7ebc886d57e6369f3c3a2 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 14:00:57 +0200 Subject: [PATCH 28/46] Add comments on gate direction back to clean_z_pivot --- synir/src/ir/clifford_tableau/helper.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 3cfccd99..1420613a 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -145,6 +145,7 @@ pub(super) fn clean_z_pivot( { let num_qubits = clifford_tableau.size(); + // These are switched around because of implementation if check_pauli( &*clifford_tableau, pivot_column, @@ -155,6 +156,7 @@ pub(super) fn clean_z_pivot( repr.v(pivot_column); } + // These are switched around because of implementation if check_pauli( &*clifford_tableau, pivot_column, From 91f3c400386623e787dec05d3abc64512e9eb205 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 14:01:36 +0200 Subject: [PATCH 29/46] Remove unused import --- synir/src/ir/clifford_tableau/permrowcol.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index 9cb1b27b..a0ff4c18 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -1,5 +1,3 @@ -use std::fmt::Debug; - use crate::{ architecture::connectivity::Connectivity, data_structures::{CliffordTableau, PauliLetter}, From d2848df5aa0c727897f1612d473b2a31792e5570 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 14:07:54 +0200 Subject: [PATCH 30/46] Shift permrowcol tests to separate file, move common.rs to common/mod.rs per rust guidelines --- synir/tests/clifford_tableau.rs | 37 ------------- synir/tests/{common.rs => common/mod.rs} | 0 synir/tests/permrowcol.rs | 69 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 37 deletions(-) rename synir/tests/{common.rs => common/mod.rs} (100%) create mode 100644 synir/tests/permrowcol.rs diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index f7492d54..9ac25ac2 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -246,40 +246,3 @@ fn test_prc_clifford_synthesis() { assert_eq!(clifford_tableau, ref_ct); } - -#[test] -fn test_prc_clifford_synthesis_large() { - let mut clifford_tableau = setup_sample_inverse_ct(); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::grid(2, 2); - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(4, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_prc_clifford_synthesis_simple() { - let num_qubits = 3; - let mut clifford_tableau = CliffordTableau::new(num_qubits); - - clifford_tableau.cx(2, 1); - clifford_tableau.cx(1, 2); - clifford_tableau.cx(0, 2); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::line(num_qubits); - - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - - clifford_tableau.permute(synthesizer.permutation()); - assert_eq!(clifford_tableau, ref_ct); -} diff --git a/synir/tests/common.rs b/synir/tests/common/mod.rs similarity index 100% rename from synir/tests/common.rs rename to synir/tests/common/mod.rs diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs new file mode 100644 index 00000000..f4fcf4a3 --- /dev/null +++ b/synir/tests/permrowcol.rs @@ -0,0 +1,69 @@ +mod common; + +use bitvec::bitvec; +use bitvec::prelude::Lsb0; +use common::{parse_clifford_commands, MockCircuit}; +use synir::architecture::connectivity::Connectivity; +use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; +use synir::ir::clifford_tableau::PermRowColCliffordSynthesizer; +use synir::ir::Synthesizer; + +fn setup_sample_inverse_ct() -> CliffordTableau { + // Stab: -ZIYZ, -ZZYZ, -XZXI, IZXX + // Destab: -YYIZ, -YYXZ, ZIXX, -XZXZ + // qubit 1x: ZZXI + // qubit 1z: YYZX + let pauli_1 = PauliString::from_text("ZZXIYYZX"); + + // qubit 2x: IZZZ + // qubit 2z: YYIZ + let pauli_2 = PauliString::from_text("IZZZYYIZ"); + + // qubit 3x: YYXX + // qubit 3z: IXXX + let pauli_3 = PauliString::from_text("YYXXIXXX"); + + // qubit 3x: ZZIX + // qubit 3z: ZZXZ + let pauli_4 = PauliString::from_text("ZZIXZZXZ"); + + let signs = bitvec![1, 1, 1, 0, 1, 1, 0, 1]; + CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) +} + +#[test] +fn test_prc_clifford_synthesis_large() { + let mut clifford_tableau = setup_sample_inverse_ct(); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::grid(2, 2); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(4, mock.commands()); + clifford_tableau.permute(synthesizer.permutation()); + + assert_eq!(clifford_tableau, ref_ct); +} + +#[test] +fn test_prc_clifford_synthesis_simple() { + let num_qubits = 3; + let mut clifford_tableau = CliffordTableau::new(num_qubits); + + clifford_tableau.cx(2, 1); + clifford_tableau.cx(1, 2); + clifford_tableau.cx(0, 2); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::line(num_qubits); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + + clifford_tableau.permute(synthesizer.permutation()); + assert_eq!(clifford_tableau, ref_ct); +} From 3c59e5040266f14a9f6dbeedc8a63c868e15f6fd Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 15:09:31 +0200 Subject: [PATCH 31/46] Update permrowcol `pick_column` to not add weight of chosen pivot row --- synir/src/ir/clifford_tableau/helper.rs | 14 +++++++----- synir/src/ir/clifford_tableau/permrowcol.rs | 7 +++--- synir/tests/permrowcol.rs | 25 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 1420613a..70b37074 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -368,6 +368,7 @@ pub(super) fn pick_row( pub(super) fn pick_column( clifford_tableau: &CliffordTableau, connectivity: &Connectivity, + pivot_row: usize, ) -> usize { let mut column_weights = vec![usize::MAX; clifford_tableau.size()]; @@ -376,15 +377,16 @@ pub(super) fn pick_column( for qubit in non_cutting { column_weights[*qubit] = 0; for interaction in connectivity.nodes() { - let mult = (clifford_tableau.stabilizer(*qubit, interaction) != PauliLetter::I) - as usize - + (clifford_tableau.destabilizer(*qubit, interaction) != PauliLetter::I) as usize; - if mult > 0 { - column_weights[*qubit] += connectivity.distance(*qubit, interaction) * mult; + if interaction != pivot_row { + let mult_z = + (clifford_tableau.stabilizer(*qubit, interaction) != PauliLetter::I) as usize; + let mult_x = + (clifford_tableau.destabilizer(*qubit, interaction) != PauliLetter::I) as usize; + column_weights[*qubit] += + connectivity.distance(*qubit, interaction) * (mult_x + mult_z); } } } - column_weights .iter() .enumerate() diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index a0ff4c18..3f8ba76e 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -14,7 +14,7 @@ pub struct PermRowColCliffordSynthesizer { connectivity: Connectivity, permutation: Vec, row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, - column_strategy: fn(&CliffordTableau, &Connectivity) -> usize, + column_strategy: fn(&CliffordTableau, &Connectivity, usize) -> usize, } impl PermRowColCliffordSynthesizer { @@ -42,7 +42,7 @@ impl PermRowColCliffordSynthesizer { pub fn set_column_strategy( &mut self, - column_strategy: fn(&CliffordTableau, &Connectivity) -> usize, + column_strategy: fn(&CliffordTableau, &Connectivity, usize) -> usize, ) { (self.column_strategy) = column_strategy; } @@ -71,7 +71,8 @@ where while !remaining_columns.is_empty() { let pivot_row = (self.row_strategy)(&clifford_tableau, &self.connectivity, &remaining_rows); - let pivot_column = (self.column_strategy)(&clifford_tableau, &self.connectivity); + let pivot_column = + (self.column_strategy)(&clifford_tableau, &self.connectivity, pivot_row); let column = clifford_tableau.column(pivot_column); let x_weight = column.x_weight(); let z_weight = column.z_weight(); diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs index f4fcf4a3..c73232b9 100644 --- a/synir/tests/permrowcol.rs +++ b/synir/tests/permrowcol.rs @@ -67,3 +67,28 @@ fn test_prc_clifford_synthesis_simple() { clifford_tableau.permute(synthesizer.permutation()); assert_eq!(clifford_tableau, ref_ct); } + +#[test] +fn test_prc_swap_to_identity() { + let num_qubits = 2; + let mut clifford_tableau = CliffordTableau::new(num_qubits); + + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 0); + clifford_tableau.cx(0, 1); + println!("clifford_tableau: {}", clifford_tableau); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::line(num_qubits); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(2, mock.commands()); + + clifford_tableau.permute(synthesizer.permutation()); + // Check that the synthesized circuit and original are the same + assert_eq!(clifford_tableau, ref_ct); + // Check that the resulting circuit is empty + assert_eq!(mock.commands().len(), 0); +} From 7ebc5e3232b8680283597d8f882bafe1de86b2c9 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 15:28:24 +0200 Subject: [PATCH 32/46] Shift prc synthesis to permrowcol test file --- synir/tests/clifford_tableau.rs | 15 ------------- synir/tests/permrowcol.rs | 38 ++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index 9ac25ac2..51d67e93 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -231,18 +231,3 @@ fn test_custom_clifford_synthesis_simple() { let ref_ct = parse_clifford_commands(3, mock.commands()); assert_eq!(clifford_tableau, ref_ct); } - -#[test] -fn test_prc_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let num_qubits = clifford_tableau.size(); - let mut mock = MockCircuit::new(); - let connectivity = Connectivity::complete(num_qubits); - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let mut ref_ct = parse_clifford_commands(3, mock.commands()); - ref_ct.permute(synthesizer.permutation()); - - assert_eq!(clifford_tableau, ref_ct); -} diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs index c73232b9..8a9e2659 100644 --- a/synir/tests/permrowcol.rs +++ b/synir/tests/permrowcol.rs @@ -8,6 +8,25 @@ use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; use synir::ir::clifford_tableau::PermRowColCliffordSynthesizer; use synir::ir::Synthesizer; +fn setup_sample_ct() -> CliffordTableau { + // Stab: ZZZ, -YIY, XIX + // Destab: -IXI, XXI, IYY + // qubit 1x: ZYI + // qubit 1z: IZZ + let pauli_1 = PauliString::from_text("ZYIIZZ"); + + // qubit 2x: ZIX + // qubit 2z: XII + let pauli_2 = PauliString::from_text("ZIXXII"); + + // qubit 3x: ZYY + // qubit 3z: IIZ + let pauli_3 = PauliString::from_text("ZYYIIZ"); + + let signs = bitvec![0, 1, 0, 1, 0, 0]; + CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3], signs) +} + fn setup_sample_inverse_ct() -> CliffordTableau { // Stab: -ZIYZ, -ZZYZ, -XZXI, IZXX // Destab: -YYIZ, -YYXZ, ZIXX, -XZXZ @@ -68,6 +87,21 @@ fn test_prc_clifford_synthesis_simple() { assert_eq!(clifford_tableau, ref_ct); } +#[test] +fn test_prc_clifford_synthesis() { + let mut clifford_tableau = setup_sample_ct(); + let num_qubits = clifford_tableau.size(); + let mut mock = MockCircuit::new(); + let connectivity = Connectivity::complete(num_qubits); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + clifford_tableau.permute(synthesizer.permutation()); + + assert_eq!(clifford_tableau, ref_ct); +} + #[test] fn test_prc_swap_to_identity() { let num_qubits = 2; @@ -76,16 +110,14 @@ fn test_prc_swap_to_identity() { clifford_tableau.cx(0, 1); clifford_tableau.cx(1, 0); clifford_tableau.cx(0, 1); - println!("clifford_tableau: {}", clifford_tableau); - let mut mock = MockCircuit::new(); + let mut mock = MockCircuit::new(); let connectivity = Connectivity::line(num_qubits); let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); synthesizer.synthesize(clifford_tableau.clone(), &mut mock); let ref_ct = parse_clifford_commands(2, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); // Check that the synthesized circuit and original are the same assert_eq!(clifford_tableau, ref_ct); From 951bcfebe59ef8934fd485295422817f34095f02 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 15:31:00 +0200 Subject: [PATCH 33/46] Add more tests to ensure prc column selection is correct --- synir/tests/permrowcol.rs | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs index 8a9e2659..3f2e6280 100644 --- a/synir/tests/permrowcol.rs +++ b/synir/tests/permrowcol.rs @@ -124,3 +124,51 @@ fn test_prc_swap_to_identity() { // Check that the resulting circuit is empty assert_eq!(mock.commands().len(), 0); } + +#[test] +fn test_prc_2_cnot_reduction_v1() { + // Check that 2 CNOTs get reduced to 1 CNOT + permutation + let num_qubits = 2; + let mut clifford_tableau = CliffordTableau::new(num_qubits); + + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 0); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::line(num_qubits); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(2, mock.commands()); + + clifford_tableau.permute(synthesizer.permutation()); + // Check that the synthesized circuit and original are the same + assert_eq!(clifford_tableau, ref_ct); + // Check that the resulting circuit is empty + assert_eq!(mock.commands().len(), 1); +} + +#[test] +fn test_prc_2_cnot_reduction_v2() { + // Check that 2 CNOTs get reduced to 1 CNOT + permutation + let num_qubits = 2; + let mut clifford_tableau = CliffordTableau::new(num_qubits); + + clifford_tableau.cx(1, 0); + clifford_tableau.cx(0, 1); + let mut mock = MockCircuit::new(); + + let connectivity = Connectivity::line(num_qubits); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(2, mock.commands()); + + clifford_tableau.permute(synthesizer.permutation()); + // Check that the synthesized circuit and original are the same + assert_eq!(clifford_tableau, ref_ct); + // Check that the resulting circuit is empty + assert_eq!(mock.commands().len(), 1); +} From 4bae3834432f580453ac2dc370e8dd4b7d288cce Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 15:35:09 +0200 Subject: [PATCH 34/46] Remove unused imports --- synir/tests/clifford_tableau.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs index 51d67e93..2f06893b 100644 --- a/synir/tests/clifford_tableau.rs +++ b/synir/tests/clifford_tableau.rs @@ -3,11 +3,8 @@ mod common; use bitvec::bitvec; use bitvec::prelude::Lsb0; use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use synir::architecture::connectivity::Connectivity; use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; -use synir::ir::clifford_tableau::{ - CallbackCliffordSynthesizer, NaiveCliffordSynthesizer, PermRowColCliffordSynthesizer, -}; +use synir::ir::clifford_tableau::{CallbackCliffordSynthesizer, NaiveCliffordSynthesizer}; use synir::ir::{AdjointSynthesizer, Synthesizer}; fn setup_sample_ct() -> CliffordTableau { From 8db28a843b1560ea4d86ce83ff7784e59fa7b29b Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Sat, 11 Oct 2025 15:37:54 +0200 Subject: [PATCH 35/46] Reorder prc synthesis tests to be uniform --- synir/tests/permrowcol.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs index 3f2e6280..522d0545 100644 --- a/synir/tests/permrowcol.rs +++ b/synir/tests/permrowcol.rs @@ -50,6 +50,21 @@ fn setup_sample_inverse_ct() -> CliffordTableau { CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) } +#[test] +fn test_prc_clifford_synthesis() { + let mut clifford_tableau = setup_sample_ct(); + let num_qubits = clifford_tableau.size(); + let mut mock = MockCircuit::new(); + let connectivity = Connectivity::complete(num_qubits); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + clifford_tableau.permute(synthesizer.permutation()); + + assert_eq!(clifford_tableau, ref_ct); +} + #[test] fn test_prc_clifford_synthesis_large() { let mut clifford_tableau = setup_sample_inverse_ct(); @@ -87,21 +102,6 @@ fn test_prc_clifford_synthesis_simple() { assert_eq!(clifford_tableau, ref_ct); } -#[test] -fn test_prc_clifford_synthesis() { - let mut clifford_tableau = setup_sample_ct(); - let num_qubits = clifford_tableau.size(); - let mut mock = MockCircuit::new(); - let connectivity = Connectivity::complete(num_qubits); - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); - - assert_eq!(clifford_tableau, ref_ct); -} - #[test] fn test_prc_swap_to_identity() { let num_qubits = 2; @@ -119,6 +119,7 @@ fn test_prc_swap_to_identity() { let ref_ct = parse_clifford_commands(2, mock.commands()); clifford_tableau.permute(synthesizer.permutation()); + // Check that the synthesized circuit and original are the same assert_eq!(clifford_tableau, ref_ct); // Check that the resulting circuit is empty From 54f6dd0bb608e38fa97316d1e3276ec0fb2c664e Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 15 Oct 2025 14:04:42 +0300 Subject: [PATCH 36/46] Fix rwlock stuff --- synir/src/data_structures/pauli_string.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synir/src/data_structures/pauli_string.rs b/synir/src/data_structures/pauli_string.rs index 15f19978..d66a3f5a 100644 --- a/synir/src/data_structures/pauli_string.rs +++ b/synir/src/data_structures/pauli_string.rs @@ -54,11 +54,11 @@ impl PauliString { } pub fn x_weight(&self) -> usize { - self.x.read().unwrap().count_ones() + self.x.count_ones() } pub fn z_weight(&self) -> usize { - self.z.read().unwrap().count_ones() + self.z.count_ones() } pub fn z(&self, i: usize) -> bool { From a1b74e27e97205234827295d32a2640f559464be Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 15 Oct 2025 20:57:02 +0300 Subject: [PATCH 37/46] Test refactor and bugfixes --- synir/src/architecture/connectivity.rs | 29 ++- synir/src/data_structures/clifford_tableau.rs | 21 +- synir/src/ir/clifford_tableau.rs | 8 +- .../ir/clifford_tableau/custom_callback.rs | 11 +- synir/src/ir/clifford_tableau/helper.rs | 23 +- synir/src/ir/clifford_tableau/naive.rs | 15 +- synir/src/ir/clifford_tableau/permrowcol.rs | 23 +- synir/tests/clifford_tableau.rs | 230 ------------------ synir/tests/common/mock_circuit.rs | 146 +++++++++++ synir/tests/common/mod.rs | 127 +--------- .../tests/common/sample_clifford_tableaus.rs | 114 +++++++++ synir/tests/ct_synthesis/custom_callback.rs | 166 +++++++++++++ synir/tests/ct_synthesis/mod.rs | 5 + synir/tests/ct_synthesis/naive.rs | 144 +++++++++++ synir/tests/ct_synthesis/naive_adjoint.rs | 144 +++++++++++ .../tests/ct_synthesis/permrowcol_complete.rs | 125 ++++++++++ synir/tests/ct_synthesis/permrowcol_line.rs | 125 ++++++++++ synir/tests/ct_synthesis_tests.rs | 2 + synir/tests/pauli_exponential.rs | 2 +- synir/tests/pauli_polynomial.rs | 2 +- synir/tests/permrowcol.rs | 175 ------------- 21 files changed, 1052 insertions(+), 585 deletions(-) delete mode 100644 synir/tests/clifford_tableau.rs create mode 100644 synir/tests/common/mock_circuit.rs create mode 100644 synir/tests/common/sample_clifford_tableaus.rs create mode 100644 synir/tests/ct_synthesis/custom_callback.rs create mode 100644 synir/tests/ct_synthesis/mod.rs create mode 100644 synir/tests/ct_synthesis/naive.rs create mode 100644 synir/tests/ct_synthesis/naive_adjoint.rs create mode 100644 synir/tests/ct_synthesis/permrowcol_complete.rs create mode 100644 synir/tests/ct_synthesis/permrowcol_line.rs create mode 100644 synir/tests/ct_synthesis_tests.rs delete mode 100644 synir/tests/permrowcol.rs diff --git a/synir/src/architecture/connectivity.rs b/synir/src/architecture/connectivity.rs index a11cc5dc..96069e0a 100644 --- a/synir/src/architecture/connectivity.rs +++ b/synir/src/architecture/connectivity.rs @@ -38,14 +38,11 @@ pub struct Connectivity { impl Connectivity { pub fn new(num_qubits: usize) -> Self { - let graph = StableUnGraph::with_capacity(num_qubits, 0); - - Connectivity { - graph, - non_cutting: Default::default(), - prev: Default::default(), - distance: HashMap::new(), + let mut graph = StableUnGraph::with_capacity(num_qubits, 0); + for _ in 0..num_qubits { + graph.add_node(()); } + Connectivity::from_graph(graph) } pub fn line(num_qubits: usize) -> Self { @@ -80,14 +77,22 @@ impl Connectivity { } pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self { - let mut graph = StableUnGraph::from_edges(edges); - graph.edge_weights_mut().for_each(|weight| *weight = 1); // Default weight of 1 for unweighted edges - Connectivity::from_graph(graph) + if edges.len() > 0 { + let mut graph = StableUnGraph::from_edges(edges); + graph.edge_weights_mut().for_each(|weight| *weight = 1); // Default weight of 1 for unweighted edges + Connectivity::from_graph(graph) + } else { + Connectivity::new(1) + } } pub fn from_weighted_edges(edges: &[(GraphIndex, GraphIndex, EdgeWeight)]) -> Self { - let graph = StableUnGraph::from_edges(edges); - Connectivity::from_graph(graph) + if edges.len() > 0 { + let graph = StableUnGraph::from_edges(edges); + Connectivity::from_graph(graph) + } else { + Connectivity::new(1) + } } pub fn from_graph(graph: StableUnGraph) -> Self { diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index ea626b25..55bba1c7 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -1,4 +1,5 @@ use bitvec::prelude::BitVec; +use bitvec::vec; use itertools::{izip, Itertools}; use std::fmt; use std::iter::zip; @@ -190,7 +191,7 @@ impl CliffordTableau { } } - pub fn permute(&mut self, permutation_vector: &[usize]) { + pub fn permute(&mut self, permutation_vector: Vec) { assert_eq!( permutation_vector .iter() @@ -206,6 +207,24 @@ impl CliffordTableau { .collect::>(); self.pauli_columns = sorted_pauli_columns; } + + pub fn get_permutation(&self) -> Option> { + let mut row_permutation = Vec::new(); + //let mut col_permutation = Vec::new(); + for pauli_column in self.pauli_columns.iter() { + if pauli_column.x_weight() != 1 || pauli_column.z_weight() != 1 { + return None; + } + //col_permutation.push(pauli_column.z.first_one().unwrap() - self.size); + row_permutation.push(pauli_column.x.first_one().unwrap()); + } + let inverse_perm: Vec = (0..self.size) + .into_iter() + .map(|i| row_permutation.iter().find_position(|&&x| x == i)) + .map(|x| x.unwrap().0) + .collect_vec(); + return Some(row_permutation); + } } impl HasAdjoint for CliffordTableau { diff --git a/synir/src/ir/clifford_tableau.rs b/synir/src/ir/clifford_tableau.rs index 9de1dbb0..f843df98 100644 --- a/synir/src/ir/clifford_tableau.rs +++ b/synir/src/ir/clifford_tableau.rs @@ -18,9 +18,11 @@ pub enum CliffordTableauSynthStrategy { Custom(Vec, Vec), } -impl, To> Synthesizer for T { - fn synthesize(&mut self, ir: CliffordTableau, repr: &mut To) { +impl, To, Returns> + Synthesizer for T +{ + fn synthesize(&mut self, ir: CliffordTableau, repr: &mut To) -> Returns { let ir = ir.adjoint(); - self.synthesize_adjoint(ir, repr) + return self.synthesize_adjoint(ir, repr); } } diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index 4fabeb44..2c60490b 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -51,11 +51,11 @@ impl CallbackCliffordSynthesizer { } } -impl AdjointSynthesizer for CallbackCliffordSynthesizer +impl AdjointSynthesizer for CallbackCliffordSynthesizer where G: CliffordGates, { - fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { + fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) -> CliffordTableau { let num_qubits = clifford_tableau.size(); let mut remaining_columns = (0..num_qubits).collect::>(); @@ -96,11 +96,8 @@ where ); } } - let final_permutation = zip(custom_columns, custom_rows) - .sorted_by_key(|a| a.1) - .map(|a| a.0) - .collect::>(); - clean_signs(repr, &mut clifford_tableau, &final_permutation); + clean_signs(repr, &mut clifford_tableau); + return clifford_tableau; } } diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 70b37074..73bf7378 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -1,5 +1,7 @@ use std::iter::zip; +use itertools::Itertools; + use crate::{ architecture::{connectivity::Connectivity, Architecture}, data_structures::{CliffordTableau, PauliLetter, PauliString, PropagateClifford}, @@ -246,16 +248,21 @@ pub(super) fn clean_z_observables( } } -pub(super) fn clean_signs( - repr: &mut G, - clifford_tableau: &mut CliffordTableau, - row_permutation: &[usize], -) where +pub(super) fn clean_signs(repr: &mut G, clifford_tableau: &mut CliffordTableau) +where G: CliffordGates, { let z_signs = clifford_tableau.z_signs(); - - for (sign, row) in zip(z_signs, row_permutation) { + let inv_perm = match clifford_tableau.get_permutation() { + None => panic!("Cleaning signs but tableau is not a permutation matrix: \n{}", clifford_tableau), + Some(perm) => perm + }; + let row_permutation = (0..clifford_tableau.size()) + .into_iter() + .map(|i| inv_perm.iter().find_position(|&&x| x == i)) + .map(|x| x.unwrap().0) + .collect_vec(); + for (sign, row) in zip(z_signs, row_permutation.iter()) { if sign { repr.x(*row); clifford_tableau.x(*row); @@ -264,7 +271,7 @@ pub(super) fn clean_signs( let x_signs = clifford_tableau.x_signs(); - for (sign, row) in zip(x_signs, row_permutation) { + for (sign, row) in zip(x_signs, row_permutation.iter()) { if sign { repr.z(*row); clifford_tableau.z(*row); diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 47b0078e..fb7db43b 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -12,11 +12,15 @@ use crate::data_structures::PauliLetter; #[derive(Default)] pub struct NaiveCliffordSynthesizer {} -impl AdjointSynthesizer for NaiveCliffordSynthesizer +impl AdjointSynthesizer for NaiveCliffordSynthesizer where G: CliffordGates, { - fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { + fn synthesize_adjoint( + &mut self, + mut clifford_tableau: CliffordTableau, + repr: &mut G, + ) -> CliffordTableau { let num_qubits = clifford_tableau.size(); for row in 0..num_qubits { @@ -41,10 +45,7 @@ where clean_z_observables(repr, &mut clifford_tableau, &checked_rows, row, row); } - clean_signs( - repr, - &mut clifford_tableau, - &(0..num_qubits).collect::>(), - ); + clean_signs(repr, &mut clifford_tableau); + return clifford_tableau; } } diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index 3f8ba76e..121750c9 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -12,7 +12,6 @@ use super::helper::clean_signs; // #[derive(Default)] pub struct PermRowColCliffordSynthesizer { connectivity: Connectivity, - permutation: Vec, row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, column_strategy: fn(&CliffordTableau, &Connectivity, usize) -> usize, } @@ -23,16 +22,11 @@ impl PermRowColCliffordSynthesizer { Self { connectivity, - permutation: (0..size).collect(), row_strategy: pick_row, column_strategy: pick_column, } } - pub fn permutation(&self) -> &[usize] { - &self.permutation - } - pub fn set_row_strategy( &mut self, row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, @@ -48,11 +42,15 @@ impl PermRowColCliffordSynthesizer { } } -impl AdjointSynthesizer for PermRowColCliffordSynthesizer +impl AdjointSynthesizer for PermRowColCliffordSynthesizer where G: CliffordGates, { - fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) { + fn synthesize_adjoint( + &mut self, + mut clifford_tableau: CliffordTableau, + repr: &mut G, + ) -> CliffordTableau { let num_qubits = clifford_tableau.size(); let machine_size = self.connectivity.node_count(); assert!( @@ -61,8 +59,6 @@ where num_qubits, machine_size ); - // Mapping between logical qubit to physical qubit - let mut permutation = (0..num_qubits).collect::>(); // logical qubit remaining to be disconnected let mut remaining_columns = (0..num_qubits).collect::>(); // stabilizers / destabilizers that are not yet identity rows @@ -126,13 +122,10 @@ where } // If the pivot row is now an identity row, we can remove it from the tableau. - - permutation[pivot_row] = pivot_column; self.connectivity.remove_node(pivot_column); } - clean_signs(repr, &mut clifford_tableau, &permutation); - - self.permutation = permutation; + clean_signs(repr, &mut clifford_tableau); + return clifford_tableau; } } diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs deleted file mode 100644 index 2f06893b..00000000 --- a/synir/tests/clifford_tableau.rs +++ /dev/null @@ -1,230 +0,0 @@ -mod common; - -use bitvec::bitvec; -use bitvec::prelude::Lsb0; -use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; -use synir::ir::clifford_tableau::{CallbackCliffordSynthesizer, NaiveCliffordSynthesizer}; -use synir::ir::{AdjointSynthesizer, Synthesizer}; - -fn setup_sample_ct() -> CliffordTableau { - // Stab: ZZZ, -YIY, XIX - // Destab: -IXI, XXI, IYY - // qubit 1x: ZYI - // qubit 1z: IZZ - let pauli_1 = PauliString::from_text("ZYIIZZ"); - - // qubit 2x: ZIX - // qubit 2z: XII - let pauli_2 = PauliString::from_text("ZIXXII"); - - // qubit 3x: ZYY - // qubit 3z: IIZ - let pauli_3 = PauliString::from_text("ZYYIIZ"); - - let signs = bitvec![0, 1, 0, 1, 0, 0]; - CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3], signs) -} - -fn setup_sample_inverse_ct() -> CliffordTableau { - // Stab: -ZIYZ, -ZZYZ, -XZXI, IZXX - // Destab: -YYIZ, -YYXZ, ZIXX, -XZXZ - // qubit 1x: ZZXI - // qubit 1z: YYZX - let pauli_1 = PauliString::from_text("ZZXIYYZX"); - - // qubit 2x: IZZZ - // qubit 2z: YYIZ - let pauli_2 = PauliString::from_text("IZZZYYIZ"); - - // qubit 3x: YYXX - // qubit 3z: IXXX - let pauli_3 = PauliString::from_text("YYXXIXXX"); - - // qubit 3x: ZZIX - // qubit 3z: ZZXZ - let pauli_4 = PauliString::from_text("ZZIXZZXZ"); - - let signs = bitvec![1, 1, 1, 0, 1, 1, 0, 1]; - CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) -} - -fn setup_2_qubit_clifford() -> CliffordTableau { - // qubit 1x: ZZXI - // qubit 1z: YYZX - let pauli_1 = PauliString::from_text("XIZI"); - - // qubit 2x: IZZZ - // qubit 2z: YYIZ - let pauli_2 = PauliString::from_text("IXIZ"); - - let signs = bitvec![0, 0, 0, 0]; - CliffordTableau::from_parts(vec![pauli_1, pauli_2], signs) -} - -#[test] -fn test_id_synthesis() { - let clifford_tableau = setup_2_qubit_clifford(); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau, &mut mock); - assert_eq!(mock.commands(), &vec![]); -} - -#[test] -fn test_s_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.s(1); - let mut mock = MockCircuit::new(); - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - assert_eq!(mock.commands(), &vec![MockCommand::S(1)]); -} - -#[test] -fn test_s_adjoint_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.s(1); - let mut mock = MockCircuit::new(); - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize_adjoint(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(2, mock.commands()); - assert_eq!(clifford_tableau * ref_ct, CliffordTableau::new(2)); - - assert_eq!(mock.commands(), &vec![MockCommand::S(1), MockCommand::Z(1)]); -} - -#[test] -fn test_v_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.v(1); - let mut mock = MockCircuit::new(); - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - assert_eq!(mock.commands(), &vec![MockCommand::V(1),]); -} - -#[test] -fn test_v_adjoint_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.v(1); - let mut mock = MockCircuit::new(); - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize_adjoint(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(2, mock.commands()); - assert_eq!(clifford_tableau * ref_ct, CliffordTableau::new(2)); - - assert_eq!(mock.commands(), &vec![MockCommand::V(1), MockCommand::X(1)]); -} - -#[test] -fn test_cnot_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.cx(0, 1); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let mut clifford_tableau = setup_2_qubit_clifford(); - clifford_tableau.cx(1, 0); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); -} - -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(4, mock.commands()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_clifford_synthesis_simple() { - let mut clifford_tableau = CliffordTableau::new(3); - clifford_tableau.cx(0, 1); - clifford_tableau.cx(1, 2); - let mut mock = MockCircuit::new(); - - let mut synthesizer = NaiveCliffordSynthesizer::default(); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_custom_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let mut mock = MockCircuit::new(); - - let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2], vec![0, 1, 2]); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_custom_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let mut mock = MockCircuit::new(); - - let mut synthesizer = - CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2, 3], vec![0, 2, 1, 3]); - - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let mut ref_ct = parse_clifford_commands(4, mock.commands()); - ref_ct.permute(&[0, 2, 1, 3]); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_custom_clifford_synthesis_simple() { - let mut clifford_tableau = CliffordTableau::new(3); - clifford_tableau.cx(0, 1); - clifford_tableau.cx(1, 2); - let mut mock = MockCircuit::new(); - - let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2], vec![0, 1, 2]); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - assert_eq!(clifford_tableau, ref_ct); -} diff --git a/synir/tests/common/mock_circuit.rs b/synir/tests/common/mock_circuit.rs new file mode 100644 index 00000000..fd25ac9b --- /dev/null +++ b/synir/tests/common/mock_circuit.rs @@ -0,0 +1,146 @@ +use std::cell::RefMut; + +use synir::{ + data_structures::{CliffordTableau, PropagateClifford}, + ir::{CliffordGates, Gates}, + IndexType, +}; + +type Angle = f64; +#[derive(Debug, Default)] +pub struct MockCircuit { + commands: Vec, +} + +#[derive(Debug, PartialEq)] +pub enum MockCommand { + CX(usize, usize), + CZ(usize, usize), + X(usize), + Y(usize), + Z(usize), + H(usize), + S(usize), + V(usize), + SDgr(usize), + VDgr(usize), + Rx(usize, f64), + Ry(usize, f64), + Rz(usize, f64), +} + +impl MockCircuit { + pub fn new() -> Self { + Self { + commands: Vec::new(), + } + } + pub fn commands(&self) -> &Vec { + &self.commands + } +} + +impl CliffordGates for MockCircuit { + fn s(&mut self, target: IndexType) { + self.commands.push(MockCommand::S(target)); + } + + fn v(&mut self, target: IndexType) { + self.commands.push(MockCommand::V(target)); + } + + fn s_dgr(&mut self, target: IndexType) { + self.commands.push(MockCommand::SDgr(target)); + } + + fn v_dgr(&mut self, target: IndexType) { + self.commands.push(MockCommand::VDgr(target)); + } + + fn x(&mut self, target: IndexType) { + self.commands.push(MockCommand::X(target)); + } + + fn y(&mut self, target: IndexType) { + self.commands.push(MockCommand::Y(target)); + } + + fn z(&mut self, target: IndexType) { + self.commands.push(MockCommand::Z(target)); + } + + fn h(&mut self, target: IndexType) { + self.commands.push(MockCommand::H(target)); + } + + fn cx(&mut self, control: IndexType, target: IndexType) { + self.commands.push(MockCommand::CX(control, target)); + } + + fn cz(&mut self, control: IndexType, target: IndexType) { + self.commands.push(MockCommand::CZ(control, target)); + } +} + +impl Gates for MockCircuit { + fn rx(&mut self, target: IndexType, angle: Angle) { + self.commands.push(MockCommand::Rx(target, angle)); + } + + fn ry(&mut self, target: IndexType, angle: Angle) { + self.commands.push(MockCommand::Ry(target, angle)); + } + + fn rz(&mut self, target: IndexType, angle: Angle) { + self.commands.push(MockCommand::Rz(target, angle)); + } +} + +pub fn parse_clifford_commands(size: usize, commands: &[MockCommand]) -> CliffordTableau { + let mut tableau = CliffordTableau::new(size); + for command in commands.iter() { + match command { + MockCommand::H(target) => { + tableau.h(*target); + } + MockCommand::S(target) => { + tableau.s(*target); + } + MockCommand::V(target) => { + tableau.v(*target); + } + MockCommand::CX(control, target) => { + tableau.cx(*control, *target); + } + MockCommand::X(target) => { + tableau.x(*target); + } + MockCommand::Z(target) => { + tableau.z(*target); + } + _ => { + panic!("not found") + } + } + } + tableau +} + +pub fn check_mock_equals_clifford_tableau( + clifford_tableau: &CliffordTableau, + mock: &MockCircuit, + permutation: Option>, +) { + assert!( + permutation.is_some(), + "Tableau was not a permutation matrix" + ); + println!("perm: {:?}", permutation.as_ref().unwrap()); + let mut ref_ct = parse_clifford_commands(clifford_tableau.size(), mock.commands()); + ref_ct.permute(permutation.unwrap()); + print!( + "Original tableau:\n{}\nReconstructed tableau:\n{}", + *clifford_tableau, ref_ct + ); + assert_eq!(*clifford_tableau, ref_ct); +} diff --git a/synir/tests/common/mod.rs b/synir/tests/common/mod.rs index a98ff57e..17c2f05b 100644 --- a/synir/tests/common/mod.rs +++ b/synir/tests/common/mod.rs @@ -1,125 +1,2 @@ -use synir::{ - data_structures::{CliffordTableau, PropagateClifford}, - ir::{CliffordGates, Gates}, - IndexType, -}; - -type Angle = f64; -#[derive(Debug, Default)] -pub struct MockCircuit { - commands: Vec, -} - -#[derive(Debug, PartialEq)] -pub enum MockCommand { - CX(usize, usize), - CZ(usize, usize), - X(usize), - Y(usize), - Z(usize), - H(usize), - S(usize), - V(usize), - SDgr(usize), - VDgr(usize), - Rx(usize, f64), - Ry(usize, f64), - Rz(usize, f64), -} - -impl MockCircuit { - pub fn new() -> Self { - Self { - commands: Vec::new(), - } - } - pub fn commands(&self) -> &Vec { - &self.commands - } -} - -impl CliffordGates for MockCircuit { - fn s(&mut self, target: IndexType) { - self.commands.push(MockCommand::S(target)); - } - - fn v(&mut self, target: IndexType) { - self.commands.push(MockCommand::V(target)); - } - - fn s_dgr(&mut self, target: IndexType) { - self.commands.push(MockCommand::SDgr(target)); - } - - fn v_dgr(&mut self, target: IndexType) { - self.commands.push(MockCommand::VDgr(target)); - } - - fn x(&mut self, target: IndexType) { - self.commands.push(MockCommand::X(target)); - } - - fn y(&mut self, target: IndexType) { - self.commands.push(MockCommand::Y(target)); - } - - fn z(&mut self, target: IndexType) { - self.commands.push(MockCommand::Z(target)); - } - - fn h(&mut self, target: IndexType) { - self.commands.push(MockCommand::H(target)); - } - - fn cx(&mut self, control: IndexType, target: IndexType) { - self.commands.push(MockCommand::CX(control, target)); - } - - fn cz(&mut self, control: IndexType, target: IndexType) { - self.commands.push(MockCommand::CZ(control, target)); - } -} - -impl Gates for MockCircuit { - fn rx(&mut self, target: IndexType, angle: Angle) { - self.commands.push(MockCommand::Rx(target, angle)); - } - - fn ry(&mut self, target: IndexType, angle: Angle) { - self.commands.push(MockCommand::Ry(target, angle)); - } - - fn rz(&mut self, target: IndexType, angle: Angle) { - self.commands.push(MockCommand::Rz(target, angle)); - } -} - -pub fn parse_clifford_commands(size: usize, commands: &[MockCommand]) -> CliffordTableau { - let mut tableau = CliffordTableau::new(size); - for command in commands.iter() { - match command { - MockCommand::H(target) => { - tableau.h(*target); - } - MockCommand::S(target) => { - tableau.s(*target); - } - MockCommand::V(target) => { - tableau.v(*target); - } - MockCommand::CX(control, target) => { - tableau.cx(*control, *target); - } - MockCommand::X(target) => { - tableau.x(*target); - } - MockCommand::Z(target) => { - tableau.z(*target); - } - _ => { - panic!("not found") - } - } - } - tableau -} +pub mod mock_circuit; +pub mod sample_clifford_tableaus; diff --git a/synir/tests/common/sample_clifford_tableaus.rs b/synir/tests/common/sample_clifford_tableaus.rs new file mode 100644 index 00000000..e2b62825 --- /dev/null +++ b/synir/tests/common/sample_clifford_tableaus.rs @@ -0,0 +1,114 @@ +use bitvec::bitvec; +use bitvec::prelude::Lsb0; +use synir::{ + data_structures::{CliffordTableau, HasAdjoint, PauliString, PropagateClifford}, + ir::clifford_tableau, +}; + +pub fn setup_sample_ct() -> CliffordTableau { + // Stab: ZZZ, -YIY, XIX + // Destab: -IXI, XXI, IYY + // qubit 1x: ZYI + // qubit 1z: IZZ + let pauli_1 = PauliString::from_text("ZYIIZZ"); + + // qubit 2x: ZIX + // qubit 2z: XII + let pauli_2 = PauliString::from_text("ZIXXII"); + + // qubit 3x: ZYY + // qubit 3z: IIZ + let pauli_3 = PauliString::from_text("ZYYIIZ"); + + let signs = bitvec![0, 1, 0, 1, 0, 0]; + CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3], signs) +} + +pub fn setup_sample_inverse_ct() -> CliffordTableau { + // Stab: -ZIYZ, -ZZYZ, -XZXI, IZXX + // Destab: -YYIZ, -YYXZ, ZIXX, -XZXZ + // qubit 1x: ZZXI + // qubit 1z: YYZX + let pauli_1 = PauliString::from_text("ZZXIYYZX"); + + // qubit 2x: IZZZ + // qubit 2z: YYIZ + let pauli_2 = PauliString::from_text("IZZZYYIZ"); + + // qubit 3x: YYXX + // qubit 3z: IXXX + let pauli_3 = PauliString::from_text("YYXXIXXX"); + + // qubit 3x: ZZIX + // qubit 3z: ZZXZ + let pauli_4 = PauliString::from_text("ZZIXZZXZ"); + + let signs = bitvec![1, 1, 1, 0, 1, 1, 0, 1]; + CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) +} + +pub fn sample_swap_ct() -> CliffordTableau { + let mut clifford_tableau = CliffordTableau::new(2); + + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 0); + clifford_tableau.cx(0, 1); + return clifford_tableau; +} + +pub fn half_swap_0_1() -> CliffordTableau { + let mut clifford_tableau = CliffordTableau::new(2); + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 0); + return clifford_tableau; +} + +pub fn half_swap_1_0() -> CliffordTableau { + let mut clifford_tableau = CliffordTableau::new(2); + clifford_tableau.cx(1, 0); + clifford_tableau.cx(0, 1); + return clifford_tableau; +} + +pub fn sample_2cnot_ladder() -> CliffordTableau { + let mut clifford_tableau = CliffordTableau::new(3); + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 2); + return clifford_tableau; +} + +pub fn sample_s_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(1); + ct.s(0); + return ct; +} + +pub fn sample_s_dgr_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(1); + ct.s_dgr(0); + return ct; +} + +pub fn sample_v_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(1); + ct.v(0); + return ct; +} + +pub fn sample_v_dgr_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(1); + ct.v_dgr(0); + return ct; +} + +pub fn sample_cnot_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.cx(0, 1); + return ct; +} + +pub fn sample_cnot_reverse_gate() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.cx(1, 0); + return ct; +} diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs new file mode 100644 index 00000000..d07301c3 --- /dev/null +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -0,0 +1,166 @@ +extern crate rand; + +use rand::seq::SliceRandom; + +use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::sample_clifford_tableaus::{ + half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, + sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, + setup_sample_ct, setup_sample_inverse_ct, +}; +use itertools::Itertools; +use synir::data_structures::CliffordTableau; +use synir::ir::clifford_tableau::CallbackCliffordSynthesizer; +use synir::ir::Synthesizer; + +fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, CliffordTableau) { + let mut mock = MockCircuit::new(); + let mut rng = rand::rng(); //TODO make this from seed + let custom_columns = (0..clifford_tableau.size()).collect_vec(); + let mut custom_rows = (0..clifford_tableau.size()).collect_vec(); + custom_rows.shuffle(&mut rng); + println!("Shuffled rows: {:?}", custom_rows); + let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(custom_columns, custom_rows); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + return (mock, new_ct); +} + +#[test] +fn test_id_synthesis() { + let clifford_tableau = CliffordTableau::new(2); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_synthesis() { + let clifford_tableau = sample_s_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_dgr_synthesis() { + let clifford_tableau = sample_s_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_synthesis() { + let clifford_tableau = sample_v_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_dgr_synthesis() { + let clifford_tableau = sample_v_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_synthesis() { + let clifford_tableau = sample_cnot_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_reverse_synthesis() { + let clifford_tableau = sample_cnot_reverse_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_large() { + let clifford_tableau = setup_sample_inverse_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_simple() { + let clifford_tableau = sample_2cnot_ladder(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_swap() { + let clifford_tableau = sample_swap_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v1() { + let clifford_tableau = half_swap_0_1(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v2() { + let clifford_tableau = half_swap_1_0(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + + +#[test] +fn test_custom_clifford_synthesis_old() { + let clifford_tableau = setup_sample_ct(); + let mut mock = MockCircuit::new(); + + let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2], vec![0, 1, 2]); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + + assert_eq!(clifford_tableau, ref_ct); +} + +#[test] +fn test_custom_clifford_synthesis_large_old() { + let clifford_tableau = setup_sample_inverse_ct(); + let mut mock = MockCircuit::new(); + + let mut synthesizer = + CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2, 3], vec![0, 2, 1, 3]); + + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let mut ref_ct = parse_clifford_commands(4, mock.commands()); + ref_ct.permute(vec![0, 2, 1, 3]); + + assert_eq!(clifford_tableau, ref_ct); +} + +#[test] +fn test_custom_clifford_synthesis_simple_old() { + let mut clifford_tableau = CliffordTableau::new(3); + clifford_tableau.cx(0, 1); + clifford_tableau.cx(1, 2); + let mut mock = MockCircuit::new(); + + let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(vec![0, 1, 2], vec![0, 1, 2]); + synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + + let ref_ct = parse_clifford_commands(3, mock.commands()); + assert_eq!(clifford_tableau, ref_ct); +} \ No newline at end of file diff --git a/synir/tests/ct_synthesis/mod.rs b/synir/tests/ct_synthesis/mod.rs new file mode 100644 index 00000000..0eebbf9d --- /dev/null +++ b/synir/tests/ct_synthesis/mod.rs @@ -0,0 +1,5 @@ +pub mod naive; +pub mod naive_adjoint; +pub mod permrowcol_complete; +pub mod permrowcol_line; +pub mod custom_callback; diff --git a/synir/tests/ct_synthesis/naive.rs b/synir/tests/ct_synthesis/naive.rs new file mode 100644 index 00000000..2c80856d --- /dev/null +++ b/synir/tests/ct_synthesis/naive.rs @@ -0,0 +1,144 @@ +use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::sample_clifford_tableaus::{ + half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, + sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, + setup_sample_ct, setup_sample_inverse_ct, +}; +use synir::data_structures::CliffordTableau; +use synir::ir::clifford_tableau::NaiveCliffordSynthesizer; +use synir::ir::Synthesizer; + +fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, CliffordTableau) { + let mut mock = MockCircuit::new(); + let mut synthesizer = NaiveCliffordSynthesizer::default(); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + return (mock, new_ct); +} + +#[test] +fn test_id_synthesis() { + let clifford_tableau = CliffordTableau::new(2); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_synthesis() { + let clifford_tableau = sample_s_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_dgr_synthesis() { + let clifford_tableau = sample_s_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_synthesis() { + let clifford_tableau = sample_v_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_dgr_synthesis() { + let clifford_tableau = sample_v_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_synthesis() { + let clifford_tableau = sample_cnot_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_reverse_synthesis() { + let clifford_tableau = sample_cnot_reverse_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_large() { + let clifford_tableau = setup_sample_inverse_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_simple() { + let clifford_tableau = sample_2cnot_ladder(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_swap() { + let clifford_tableau = sample_swap_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v1() { + let clifford_tableau = half_swap_0_1(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(0, 1) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v2() { + let clifford_tableau = half_swap_1_0(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(1, 0) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} diff --git a/synir/tests/ct_synthesis/naive_adjoint.rs b/synir/tests/ct_synthesis/naive_adjoint.rs new file mode 100644 index 00000000..c1df92f4 --- /dev/null +++ b/synir/tests/ct_synthesis/naive_adjoint.rs @@ -0,0 +1,144 @@ +use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::sample_clifford_tableaus::{ + half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, + sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, + setup_sample_ct, setup_sample_inverse_ct, +}; +use synir::data_structures::{CliffordTableau, HasAdjoint}; +use synir::ir::clifford_tableau::NaiveCliffordSynthesizer; +use synir::ir::AdjointSynthesizer; + +fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, CliffordTableau) { + let mut mock = MockCircuit::new(); + let mut synthesizer = NaiveCliffordSynthesizer::default(); + let new_ct = synthesizer.synthesize_adjoint(clifford_tableau.adjoint(), &mut mock); + return (mock, new_ct); +} + +#[test] +fn test_id_synthesis() { + let clifford_tableau = CliffordTableau::new(2); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_synthesis() { + let clifford_tableau = sample_s_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_s_dgr_synthesis() { + let clifford_tableau = sample_s_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_synthesis() { + let clifford_tableau = sample_v_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_v_dgr_synthesis() { + let clifford_tableau = sample_v_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_synthesis() { + let clifford_tableau = sample_cnot_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_cnot_reverse_synthesis() { + let clifford_tableau = sample_cnot_reverse_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_large() { + let clifford_tableau = setup_sample_inverse_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_simple() { + let clifford_tableau = sample_2cnot_ladder(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_swap() { + let clifford_tableau = sample_swap_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v1() { + let clifford_tableau = half_swap_0_1(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(0, 1) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_half_swap_v2() { + let clifford_tableau = half_swap_1_0(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + assert_eq!( + mock.commands(), + &vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(1, 0) + ] + ); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} diff --git a/synir/tests/ct_synthesis/permrowcol_complete.rs b/synir/tests/ct_synthesis/permrowcol_complete.rs new file mode 100644 index 00000000..38809a4b --- /dev/null +++ b/synir/tests/ct_synthesis/permrowcol_complete.rs @@ -0,0 +1,125 @@ +use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::sample_clifford_tableaus::{ + half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, + sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, + setup_sample_ct, setup_sample_inverse_ct, +}; +use synir::architecture::connectivity::Connectivity; +use synir::data_structures::CliffordTableau; +use synir::ir::clifford_tableau::PermRowColCliffordSynthesizer; +use synir::ir::Synthesizer; + +fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, CliffordTableau) { + let num_qubits = clifford_tableau.size(); + let mut mock = MockCircuit::new(); + let connectivity = Connectivity::complete(num_qubits); + println!("size {}, qpu {}", num_qubits, connectivity.node_count()); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + return (mock, new_ct); +} + +#[test] +fn test_id_synthesis() { + let clifford_tableau = CliffordTableau::new(2); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![]); +} + +#[test] +fn test_s_synthesis() { + let clifford_tableau = sample_s_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); +} + +#[test] +fn test_s_dgr_synthesis() { + let clifford_tableau = sample_s_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); +} + +#[test] +fn test_v_synthesis() { + let clifford_tableau = sample_v_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); +} + +#[test] +fn test_v_dgr_synthesis() { + let clifford_tableau = sample_v_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); +} + +#[test] +fn test_cnot_synthesis() { + let clifford_tableau = sample_cnot_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); +} + +#[test] +fn test_cnot_reverse_synthesis() { + let clifford_tableau = sample_cnot_reverse_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); +} + +#[test] +fn test_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_large() { + let clifford_tableau = setup_sample_inverse_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_simple() { + let clifford_tableau = sample_2cnot_ladder(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!( + mock.commands(), + &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] + ); +} + +#[test] +fn test_prc_swap_to_identity() { + let clifford_tableau = sample_swap_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 0); +} + +#[test] +fn test_prc_2_cnot_reduction_v1() { + let clifford_tableau = half_swap_0_1(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 1); +} + +#[test] +fn test_prc_2_cnot_reduction_v2() { + let clifford_tableau = half_swap_1_0(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 1); +} diff --git a/synir/tests/ct_synthesis/permrowcol_line.rs b/synir/tests/ct_synthesis/permrowcol_line.rs new file mode 100644 index 00000000..c0cbce53 --- /dev/null +++ b/synir/tests/ct_synthesis/permrowcol_line.rs @@ -0,0 +1,125 @@ +use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::sample_clifford_tableaus::{ + half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, + sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, + setup_sample_ct, setup_sample_inverse_ct, +}; +use synir::architecture::connectivity::Connectivity; +use synir::data_structures::CliffordTableau; +use synir::ir::clifford_tableau::PermRowColCliffordSynthesizer; +use synir::ir::Synthesizer; + +fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, CliffordTableau) { + let num_qubits = clifford_tableau.size(); + let mut mock = MockCircuit::new(); + let connectivity = Connectivity::line(num_qubits); + println!("size {}, qpu {}", num_qubits, connectivity.node_count()); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + return (mock, new_ct); +} + +#[test] +fn test_id_synthesis() { + let clifford_tableau = CliffordTableau::new(2); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![]); +} + +#[test] +fn test_s_synthesis() { + let clifford_tableau = sample_s_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); +} + +#[test] +fn test_s_dgr_synthesis() { + let clifford_tableau = sample_s_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); +} + +#[test] +fn test_v_synthesis() { + let clifford_tableau = sample_v_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); +} + +#[test] +fn test_v_dgr_synthesis() { + let clifford_tableau = sample_v_dgr_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); +} + +#[test] +fn test_cnot_synthesis() { + let clifford_tableau = sample_cnot_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); +} + +#[test] +fn test_cnot_reverse_synthesis() { + let clifford_tableau = sample_cnot_reverse_gate(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); +} + +#[test] +fn test_clifford_synthesis() { + let clifford_tableau = setup_sample_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_large() { + let clifford_tableau = setup_sample_inverse_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +} + +#[test] +fn test_clifford_synthesis_simple() { + let clifford_tableau = sample_2cnot_ladder(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!( + mock.commands(), + &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] + ); +} + +#[test] +fn test_prc_swap_to_identity() { + let clifford_tableau = sample_swap_ct(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 0); +} + +#[test] +fn test_prc_2_cnot_reduction_v1() { + let clifford_tableau = half_swap_0_1(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 1); +} + +#[test] +fn test_prc_2_cnot_reduction_v2() { + let clifford_tableau = half_swap_1_0(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + assert_eq!(mock.commands().len(), 1); +} diff --git a/synir/tests/ct_synthesis_tests.rs b/synir/tests/ct_synthesis_tests.rs new file mode 100644 index 00000000..c061fba3 --- /dev/null +++ b/synir/tests/ct_synthesis_tests.rs @@ -0,0 +1,2 @@ +mod common; +mod ct_synthesis; diff --git a/synir/tests/pauli_exponential.rs b/synir/tests/pauli_exponential.rs index ab309527..324aa30d 100644 --- a/synir/tests/pauli_exponential.rs +++ b/synir/tests/pauli_exponential.rs @@ -2,7 +2,7 @@ mod common; use std::collections::VecDeque; -use common::{parse_clifford_commands, MockCircuit, MockCommand}; +use common::mock_circuit::{parse_clifford_commands, MockCircuit, MockCommand}; use synir::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; use synir::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; use synir::ir::pauli_exponential::PauliExponential; diff --git a/synir/tests/pauli_polynomial.rs b/synir/tests/pauli_polynomial.rs index e87e55cd..f4560e9b 100644 --- a/synir/tests/pauli_polynomial.rs +++ b/synir/tests/pauli_polynomial.rs @@ -2,7 +2,7 @@ mod common; use std::collections::VecDeque; -use common::{parse_clifford_commands, MockCircuit, MockCommand}; +use common::mock_circuit::{parse_clifford_commands, MockCircuit, MockCommand}; use synir::data_structures::{CliffordTableau, PauliPolynomial}; use synir::ir::pauli_polynomial::NaivePauliPolynomialSynthesizer; use synir::ir::Synthesizer; diff --git a/synir/tests/permrowcol.rs b/synir/tests/permrowcol.rs deleted file mode 100644 index 522d0545..00000000 --- a/synir/tests/permrowcol.rs +++ /dev/null @@ -1,175 +0,0 @@ -mod common; - -use bitvec::bitvec; -use bitvec::prelude::Lsb0; -use common::{parse_clifford_commands, MockCircuit}; -use synir::architecture::connectivity::Connectivity; -use synir::data_structures::{CliffordTableau, PauliString, PropagateClifford}; -use synir::ir::clifford_tableau::PermRowColCliffordSynthesizer; -use synir::ir::Synthesizer; - -fn setup_sample_ct() -> CliffordTableau { - // Stab: ZZZ, -YIY, XIX - // Destab: -IXI, XXI, IYY - // qubit 1x: ZYI - // qubit 1z: IZZ - let pauli_1 = PauliString::from_text("ZYIIZZ"); - - // qubit 2x: ZIX - // qubit 2z: XII - let pauli_2 = PauliString::from_text("ZIXXII"); - - // qubit 3x: ZYY - // qubit 3z: IIZ - let pauli_3 = PauliString::from_text("ZYYIIZ"); - - let signs = bitvec![0, 1, 0, 1, 0, 0]; - CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3], signs) -} - -fn setup_sample_inverse_ct() -> CliffordTableau { - // Stab: -ZIYZ, -ZZYZ, -XZXI, IZXX - // Destab: -YYIZ, -YYXZ, ZIXX, -XZXZ - // qubit 1x: ZZXI - // qubit 1z: YYZX - let pauli_1 = PauliString::from_text("ZZXIYYZX"); - - // qubit 2x: IZZZ - // qubit 2z: YYIZ - let pauli_2 = PauliString::from_text("IZZZYYIZ"); - - // qubit 3x: YYXX - // qubit 3z: IXXX - let pauli_3 = PauliString::from_text("YYXXIXXX"); - - // qubit 3x: ZZIX - // qubit 3z: ZZXZ - let pauli_4 = PauliString::from_text("ZZIXZZXZ"); - - let signs = bitvec![1, 1, 1, 0, 1, 1, 0, 1]; - CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) -} - -#[test] -fn test_prc_clifford_synthesis() { - let mut clifford_tableau = setup_sample_ct(); - let num_qubits = clifford_tableau.size(); - let mut mock = MockCircuit::new(); - let connectivity = Connectivity::complete(num_qubits); - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_prc_clifford_synthesis_large() { - let mut clifford_tableau = setup_sample_inverse_ct(); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::grid(2, 2); - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(4, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); - - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_prc_clifford_synthesis_simple() { - let num_qubits = 3; - let mut clifford_tableau = CliffordTableau::new(num_qubits); - - clifford_tableau.cx(2, 1); - clifford_tableau.cx(1, 2); - clifford_tableau.cx(0, 2); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::line(num_qubits); - - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(3, mock.commands()); - - clifford_tableau.permute(synthesizer.permutation()); - assert_eq!(clifford_tableau, ref_ct); -} - -#[test] -fn test_prc_swap_to_identity() { - let num_qubits = 2; - let mut clifford_tableau = CliffordTableau::new(num_qubits); - - clifford_tableau.cx(0, 1); - clifford_tableau.cx(1, 0); - clifford_tableau.cx(0, 1); - - let mut mock = MockCircuit::new(); - let connectivity = Connectivity::line(num_qubits); - - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(2, mock.commands()); - clifford_tableau.permute(synthesizer.permutation()); - - // Check that the synthesized circuit and original are the same - assert_eq!(clifford_tableau, ref_ct); - // Check that the resulting circuit is empty - assert_eq!(mock.commands().len(), 0); -} - -#[test] -fn test_prc_2_cnot_reduction_v1() { - // Check that 2 CNOTs get reduced to 1 CNOT + permutation - let num_qubits = 2; - let mut clifford_tableau = CliffordTableau::new(num_qubits); - - clifford_tableau.cx(0, 1); - clifford_tableau.cx(1, 0); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::line(num_qubits); - - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(2, mock.commands()); - - clifford_tableau.permute(synthesizer.permutation()); - // Check that the synthesized circuit and original are the same - assert_eq!(clifford_tableau, ref_ct); - // Check that the resulting circuit is empty - assert_eq!(mock.commands().len(), 1); -} - -#[test] -fn test_prc_2_cnot_reduction_v2() { - // Check that 2 CNOTs get reduced to 1 CNOT + permutation - let num_qubits = 2; - let mut clifford_tableau = CliffordTableau::new(num_qubits); - - clifford_tableau.cx(1, 0); - clifford_tableau.cx(0, 1); - let mut mock = MockCircuit::new(); - - let connectivity = Connectivity::line(num_qubits); - - let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); - synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - - let ref_ct = parse_clifford_commands(2, mock.commands()); - - clifford_tableau.permute(synthesizer.permutation()); - // Check that the synthesized circuit and original are the same - assert_eq!(clifford_tableau, ref_ct); - // Check that the resulting circuit is empty - assert_eq!(mock.commands().len(), 1); -} From 25c2c98cd9dfb9a3e9fa4f70b19bb6ee077ccd25 Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 15 Oct 2025 21:12:09 +0300 Subject: [PATCH 38/46] Custom Callback rewrite --- synir/src/ir/clifford_tableau/custom_callback.rs | 6 +++++- synir/src/ir/clifford_tableau/helper.rs | 7 +++++-- synir/tests/ct_synthesis/custom_callback.rs | 7 +++---- synir/tests/ct_synthesis/mod.rs | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index 2c60490b..0da378ae 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -55,7 +55,11 @@ impl AdjointSynthesizer for CallbackClif where G: CliffordGates, { - fn synthesize_adjoint(&mut self, mut clifford_tableau: CliffordTableau, repr: &mut G) -> CliffordTableau { + fn synthesize_adjoint( + &mut self, + mut clifford_tableau: CliffordTableau, + repr: &mut G, + ) -> CliffordTableau { let num_qubits = clifford_tableau.size(); let mut remaining_columns = (0..num_qubits).collect::>(); diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 73bf7378..56020def 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -254,8 +254,11 @@ where { let z_signs = clifford_tableau.z_signs(); let inv_perm = match clifford_tableau.get_permutation() { - None => panic!("Cleaning signs but tableau is not a permutation matrix: \n{}", clifford_tableau), - Some(perm) => perm + None => panic!( + "Cleaning signs but tableau is not a permutation matrix: \n{}", + clifford_tableau + ), + Some(perm) => perm, }; let row_permutation = (0..clifford_tableau.size()) .into_iter() diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs index d07301c3..6a601339 100644 --- a/synir/tests/ct_synthesis/custom_callback.rs +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -2,14 +2,14 @@ extern crate rand; use rand::seq::SliceRandom; -use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::mock_circuit::{parse_clifford_commands, check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; use crate::common::sample_clifford_tableaus::{ half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use itertools::Itertools; -use synir::data_structures::CliffordTableau; +use synir::data_structures::{CliffordTableau, PropagateClifford}; use synir::ir::clifford_tableau::CallbackCliffordSynthesizer; use synir::ir::Synthesizer; @@ -121,7 +121,6 @@ fn test_half_swap_v2() { check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); } - #[test] fn test_custom_clifford_synthesis_old() { let clifford_tableau = setup_sample_ct(); @@ -163,4 +162,4 @@ fn test_custom_clifford_synthesis_simple_old() { let ref_ct = parse_clifford_commands(3, mock.commands()); assert_eq!(clifford_tableau, ref_ct); -} \ No newline at end of file +} diff --git a/synir/tests/ct_synthesis/mod.rs b/synir/tests/ct_synthesis/mod.rs index 0eebbf9d..2fff57d6 100644 --- a/synir/tests/ct_synthesis/mod.rs +++ b/synir/tests/ct_synthesis/mod.rs @@ -1,5 +1,5 @@ +pub mod custom_callback; pub mod naive; pub mod naive_adjoint; pub mod permrowcol_complete; pub mod permrowcol_line; -pub mod custom_callback; From bee7694a1a75dd6de3e07521c3cb3ebf8b619582 Mon Sep 17 00:00:00 2001 From: Aerylia Date: Wed, 15 Oct 2025 21:12:17 +0300 Subject: [PATCH 39/46] Remove extra scope --- synir/src/ir/clifford_tableau/permrowcol.rs | 77 ++++++++++----------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index 121750c9..a4856883 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -81,45 +81,44 @@ where remaining_columns.retain(|&x| x != pivot_column); remaining_rows.retain(|&x| x != pivot_row); - { - clean_pivot( - repr, - &mut clifford_tableau, - pivot_column, - pivot_row, - first_letter, - ); - - // Use the pivot to remove all other terms in the X observable. - clean_prc( - repr, - &mut clifford_tableau, - &self.connectivity, - &remaining_columns, - pivot_column, - pivot_row, - first_letter, - ); - - clean_pivot( - repr, - &mut clifford_tableau, - pivot_column, - pivot_row, - second_letter, - ); - - // Use the pivot to remove all other terms in the Z observable. - clean_prc( - repr, - &mut clifford_tableau, - &self.connectivity, - &remaining_columns, - pivot_column, - pivot_row, - second_letter, - ); - } + + clean_pivot( + repr, + &mut clifford_tableau, + pivot_column, + pivot_row, + first_letter, + ); + + // Use the pivot to remove all other terms in the X observable. + clean_prc( + repr, + &mut clifford_tableau, + &self.connectivity, + &remaining_columns, + pivot_column, + pivot_row, + first_letter, + ); + + clean_pivot( + repr, + &mut clifford_tableau, + pivot_column, + pivot_row, + second_letter, + ); + + // Use the pivot to remove all other terms in the Z observable. + clean_prc( + repr, + &mut clifford_tableau, + &self.connectivity, + &remaining_columns, + pivot_column, + pivot_row, + second_letter, + ); // If the pivot row is now an identity row, we can remove it from the tableau. self.connectivity.remove_node(pivot_column); From ada915849d634e18b4fdd94a2e535b6ecc770344 Mon Sep 17 00:00:00 2001 From: Aerylia Date: Tue, 21 Oct 2025 15:23:12 +0300 Subject: [PATCH 40/46] Refactor integration tests to use macros --- Cargo.lock | 7 + synir/Cargo.toml | 1 + synir/src/ir/clifford_tableau/naive.rs | 8 +- .../tests/common/sample_clifford_tableaus.rs | 4 + synir/tests/ct_synthesis/custom_callback.rs | 140 +++++-------- synir/tests/ct_synthesis/naive.rs | 190 ++++++------------ synir/tests/ct_synthesis/naive_adjoint.rs | 190 ++++++------------ .../tests/ct_synthesis/permrowcol_complete.rs | 145 ++++--------- synir/tests/ct_synthesis/permrowcol_line.rs | 140 ++++--------- 9 files changed, 271 insertions(+), 554 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 10ff7ef2..6dfcaa21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,12 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "petgraph" version = "0.8.2" @@ -673,6 +679,7 @@ dependencies = [ "bitvec", "criterion", "itertools 0.13.0", + "paste", "petgraph", "rand", ] diff --git a/synir/Cargo.toml b/synir/Cargo.toml index 9712694b..3e5a5b0f 100644 --- a/synir/Cargo.toml +++ b/synir/Cargo.toml @@ -12,6 +12,7 @@ petgraph = { version = "0.8.2", features = ["stable_graph"], git = "https://gith [dev-dependencies] criterion = "0.5.1" rand = "0.9.0" +paste = "1.0.15" [[bench]] name = "clifford_tableau" diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index fb7db43b..3ad88bc4 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -9,9 +9,15 @@ use super::helper::{ use crate::data_structures::PauliLetter; -#[derive(Default)] +#[derive(Default, Debug)] pub struct NaiveCliffordSynthesizer {} +impl NaiveCliffordSynthesizer { + pub fn name(&self) -> &str { + return "naive"; + } +} + impl AdjointSynthesizer for NaiveCliffordSynthesizer where G: CliffordGates, diff --git a/synir/tests/common/sample_clifford_tableaus.rs b/synir/tests/common/sample_clifford_tableaus.rs index e2b62825..d1c81442 100644 --- a/synir/tests/common/sample_clifford_tableaus.rs +++ b/synir/tests/common/sample_clifford_tableaus.rs @@ -47,6 +47,10 @@ pub fn setup_sample_inverse_ct() -> CliffordTableau { CliffordTableau::from_parts(vec![pauli_1, pauli_2, pauli_3, pauli_4], signs) } +pub fn identity_2qb_ct() -> CliffordTableau { + return CliffordTableau::new(2); +} + pub fn sample_swap_ct() -> CliffordTableau { let mut clifford_tableau = CliffordTableau::new(2); diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs index 6a601339..82c6b163 100644 --- a/synir/tests/ct_synthesis/custom_callback.rs +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -2,11 +2,13 @@ extern crate rand; use rand::seq::SliceRandom; -use crate::common::mock_circuit::{parse_clifford_commands, check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; +use crate::common::mock_circuit::{ + check_mock_equals_clifford_tableau, parse_clifford_commands, MockCircuit, MockCommand, +}; use crate::common::sample_clifford_tableaus::{ half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, + setup_sample_ct, setup_sample_inverse_ct, identity_2qb_ct }; use itertools::Itertools; use synir::data_structures::{CliffordTableau, PropagateClifford}; @@ -25,101 +27,51 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } -#[test] -fn test_id_synthesis() { - let clifford_tableau = CliffordTableau::new(2); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_s_synthesis() { - let clifford_tableau = sample_s_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_s_dgr_synthesis() { - let clifford_tableau = sample_s_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_synthesis() { - let clifford_tableau = sample_v_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_dgr_synthesis() { - let clifford_tableau = sample_v_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_synthesis() { - let clifford_tableau = sample_cnot_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let clifford_tableau = sample_cnot_reverse_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} +macro_rules! test_clifford { + ($fun:ident, $expected:expr) => { + paste::item! { + #[test] + fn [< synthesize_ $fun>]() { + let clifford_tableau = $fun(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + if $expected.is_some() { + assert_eq!(mock.commands(), $expected.unwrap()); + } + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + } + } + }; +} + +// Single qubit gates +test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); +test_clifford!( + sample_s_dgr_gate, + Some(&vec![MockCommand::S(0), MockCommand::Z(0)]) +); +test_clifford!(sample_v_gate, Some(&vec![MockCommand::V(0)])); +test_clifford!( + sample_v_dgr_gate, + Some(&vec![MockCommand::V(0), MockCommand::X(0)]) +); + +// Advances Clifford Tableau +test_clifford!(setup_sample_ct, None::<&Vec>); +test_clifford!(setup_sample_inverse_ct, None::<&Vec>); + +// CNOT synthesis +test_clifford!(sample_cnot_gate, None::<&Vec>); +test_clifford!(sample_cnot_reverse_gate, None::<&Vec>); +test_clifford!( + sample_2cnot_ladder,None::<&Vec> +); +test_clifford!(sample_swap_ct, None::<&Vec>); +test_clifford!(half_swap_0_1, None::<&Vec>); +test_clifford!(half_swap_1_0, None::<&Vec>); -#[test] -fn test_clifford_synthesis_simple() { - let clifford_tableau = sample_2cnot_ladder(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_swap() { - let clifford_tableau = sample_swap_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v1() { - let clifford_tableau = half_swap_0_1(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v2() { - let clifford_tableau = half_swap_1_0(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} #[test] fn test_custom_clifford_synthesis_old() { diff --git a/synir/tests/ct_synthesis/naive.rs b/synir/tests/ct_synthesis/naive.rs index 2c80856d..216d8f04 100644 --- a/synir/tests/ct_synthesis/naive.rs +++ b/synir/tests/ct_synthesis/naive.rs @@ -1,8 +1,8 @@ use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; use crate::common::sample_clifford_tableaus::{ - half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, - sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, + half_swap_0_1, half_swap_1_0, identity_2qb_ct, sample_2cnot_ladder, sample_cnot_gate, + sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, + sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use synir::data_structures::CliffordTableau; use synir::ir::clifford_tableau::NaiveCliffordSynthesizer; @@ -15,130 +15,64 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } -#[test] -fn test_id_synthesis() { - let clifford_tableau = CliffordTableau::new(2); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +macro_rules! test_clifford { + ($fun:ident, $expected:expr) => { + paste::item! { + #[test] + fn [< synthesize_ $fun>]() { + let clifford_tableau = $fun(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + if $expected.is_some() { + assert_eq!(mock.commands(), $expected.unwrap()); + } + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + } + } + }; } -#[test] -fn test_s_synthesis() { - let clifford_tableau = sample_s_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_s_dgr_synthesis() { - let clifford_tableau = sample_s_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_synthesis() { - let clifford_tableau = sample_v_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_dgr_synthesis() { - let clifford_tableau = sample_v_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_synthesis() { - let clifford_tableau = sample_cnot_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let clifford_tableau = sample_cnot_reverse_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_simple() { - let clifford_tableau = sample_2cnot_ladder(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_swap() { - let clifford_tableau = sample_swap_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v1() { - let clifford_tableau = half_swap_0_1(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0), - MockCommand::CX(0, 1) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v2() { - let clifford_tableau = half_swap_1_0(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0), - MockCommand::CX(1, 0) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} +test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); +test_clifford!( + sample_s_dgr_gate, + Some(&vec![MockCommand::S(0), MockCommand::Z(0)]) +); +test_clifford!(sample_v_gate, Some(&vec![MockCommand::V(0)])); +test_clifford!( + sample_v_dgr_gate, + Some(&vec![MockCommand::V(0), MockCommand::X(0)]) +); +test_clifford!(sample_cnot_gate, Some(&vec![MockCommand::CX(0, 1)])); +test_clifford!(sample_cnot_reverse_gate, Some(&vec![MockCommand::CX(1, 0)])); +test_clifford!(setup_sample_ct, None::<&Vec>); +test_clifford!(setup_sample_inverse_ct, None::<&Vec>); +test_clifford!( + sample_2cnot_ladder, + Some(&vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)]) +); +test_clifford!( + sample_swap_ct, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0) + ]) +); +test_clifford!( + half_swap_0_1, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(0, 1) + ]) +); +test_clifford!( + half_swap_1_0, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(1, 0) + ]) +); diff --git a/synir/tests/ct_synthesis/naive_adjoint.rs b/synir/tests/ct_synthesis/naive_adjoint.rs index c1df92f4..73e9af68 100644 --- a/synir/tests/ct_synthesis/naive_adjoint.rs +++ b/synir/tests/ct_synthesis/naive_adjoint.rs @@ -1,8 +1,8 @@ use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; use crate::common::sample_clifford_tableaus::{ - half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, - sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, + half_swap_0_1, half_swap_1_0, identity_2qb_ct, sample_2cnot_ladder, sample_cnot_gate, + sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, + sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use synir::data_structures::{CliffordTableau, HasAdjoint}; use synir::ir::clifford_tableau::NaiveCliffordSynthesizer; @@ -15,130 +15,64 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } -#[test] -fn test_id_synthesis() { - let clifford_tableau = CliffordTableau::new(2); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +macro_rules! test_clifford { + ($fun:ident, $expected:expr) => { + paste::item! { + #[test] + fn [< synthesize_ $fun>]() { + let clifford_tableau = $fun(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + if $expected.is_some() { + assert_eq!(mock.commands(), $expected.unwrap()); + } + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + } + } + }; } -#[test] -fn test_s_synthesis() { - let clifford_tableau = sample_s_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_s_dgr_synthesis() { - let clifford_tableau = sample_s_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_synthesis() { - let clifford_tableau = sample_v_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_v_dgr_synthesis() { - let clifford_tableau = sample_v_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_synthesis() { - let clifford_tableau = sample_cnot_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let clifford_tableau = sample_cnot_reverse_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_simple() { - let clifford_tableau = sample_2cnot_ladder(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_swap() { - let clifford_tableau = sample_swap_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v1() { - let clifford_tableau = half_swap_0_1(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0), - MockCommand::CX(0, 1) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_half_swap_v2() { - let clifford_tableau = half_swap_1_0(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - assert_eq!( - mock.commands(), - &vec![ - MockCommand::CX(1, 0), - MockCommand::CX(0, 1), - MockCommand::CX(1, 0), - MockCommand::CX(1, 0) - ] - ); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} +test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); +test_clifford!( + sample_s_dgr_gate, + Some(&vec![MockCommand::S(0), MockCommand::Z(0)]) +); +test_clifford!(sample_v_gate, Some(&vec![MockCommand::V(0)])); +test_clifford!( + sample_v_dgr_gate, + Some(&vec![MockCommand::V(0), MockCommand::X(0)]) +); +test_clifford!(sample_cnot_gate, Some(&vec![MockCommand::CX(0, 1)])); +test_clifford!(sample_cnot_reverse_gate, Some(&vec![MockCommand::CX(1, 0)])); +test_clifford!(setup_sample_ct, None::<&Vec>); +test_clifford!(setup_sample_inverse_ct, None::<&Vec>); +test_clifford!( + sample_2cnot_ladder, + Some(&vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)]) +); +test_clifford!( + sample_swap_ct, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0) + ]) +); +test_clifford!( + half_swap_0_1, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(0, 1) + ]) +); +test_clifford!( + half_swap_1_0, + Some(&vec![ + MockCommand::CX(1, 0), + MockCommand::CX(0, 1), + MockCommand::CX(1, 0), + MockCommand::CX(1, 0) + ]) +); diff --git a/synir/tests/ct_synthesis/permrowcol_complete.rs b/synir/tests/ct_synthesis/permrowcol_complete.rs index 38809a4b..eafb8545 100644 --- a/synir/tests/ct_synthesis/permrowcol_complete.rs +++ b/synir/tests/ct_synthesis/permrowcol_complete.rs @@ -1,8 +1,8 @@ use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; use crate::common::sample_clifford_tableaus::{ - half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, - sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, + half_swap_0_1, half_swap_1_0, identity_2qb_ct, sample_2cnot_ladder, sample_cnot_gate, + sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, + sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use synir::architecture::connectivity::Connectivity; use synir::data_structures::CliffordTableau; @@ -19,107 +19,46 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } -#[test] -fn test_id_synthesis() { - let clifford_tableau = CliffordTableau::new(2); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![]); +macro_rules! test_clifford { + ($fun:ident, $expected:expr) => { + paste::item! { + #[test] + fn [< synthesize_ $fun>]() { + let clifford_tableau = $fun(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + if $expected.is_some() { + assert_eq!(mock.commands(), $expected.unwrap()); + } + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + } + } + }; } -#[test] -fn test_s_synthesis() { - let clifford_tableau = sample_s_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); -} - -#[test] -fn test_s_dgr_synthesis() { - let clifford_tableau = sample_s_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); -} +// Single qubit gates +test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); +test_clifford!( + sample_s_dgr_gate, + Some(&vec![MockCommand::S(0), MockCommand::Z(0)]) +); +test_clifford!(sample_v_gate, Some(&vec![MockCommand::V(0)])); +test_clifford!( + sample_v_dgr_gate, + Some(&vec![MockCommand::V(0), MockCommand::X(0)]) +); -#[test] -fn test_v_synthesis() { - let clifford_tableau = sample_v_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); -} - -#[test] -fn test_v_dgr_synthesis() { - let clifford_tableau = sample_v_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); -} +// Advances Clifford Tableau +test_clifford!(setup_sample_ct, None::<&Vec>); +test_clifford!(setup_sample_inverse_ct, None::<&Vec>); -#[test] -fn test_cnot_synthesis() { - let clifford_tableau = sample_cnot_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let clifford_tableau = sample_cnot_reverse_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); -} - -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} - -#[test] -fn test_clifford_synthesis_simple() { - let clifford_tableau = sample_2cnot_ladder(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!( - mock.commands(), - &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] - ); -} - -#[test] -fn test_prc_swap_to_identity() { - let clifford_tableau = sample_swap_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 0); -} - -#[test] -fn test_prc_2_cnot_reduction_v1() { - let clifford_tableau = half_swap_0_1(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 1); -} - -#[test] -fn test_prc_2_cnot_reduction_v2() { - let clifford_tableau = half_swap_1_0(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 1); -} +// CNOT synthesis +test_clifford!(sample_cnot_gate, Some(&vec![MockCommand::CX(0, 1)])); +test_clifford!(sample_cnot_reverse_gate, Some(&vec![MockCommand::CX(1, 0)])); +test_clifford!( + sample_2cnot_ladder, + Some(&vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)]) +); +test_clifford!(sample_swap_ct, Some::<&Vec>(&vec![])); +test_clifford!(half_swap_0_1, Some(&vec![MockCommand::CX(1, 0),])); +test_clifford!(half_swap_1_0, Some(&vec![MockCommand::CX(0, 1),])); diff --git a/synir/tests/ct_synthesis/permrowcol_line.rs b/synir/tests/ct_synthesis/permrowcol_line.rs index c0cbce53..6d741afd 100644 --- a/synir/tests/ct_synthesis/permrowcol_line.rs +++ b/synir/tests/ct_synthesis/permrowcol_line.rs @@ -2,7 +2,7 @@ use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircui use crate::common::sample_clifford_tableaus::{ half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, + setup_sample_ct, setup_sample_inverse_ct, identity_2qb_ct }; use synir::architecture::connectivity::Connectivity; use synir::data_structures::CliffordTableau; @@ -19,107 +19,47 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } -#[test] -fn test_id_synthesis() { - let clifford_tableau = CliffordTableau::new(2); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![]); -} - -#[test] -fn test_s_synthesis() { - let clifford_tableau = sample_s_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::S(0)]); -} - -#[test] -fn test_s_dgr_synthesis() { - let clifford_tableau = sample_s_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::S(0), MockCommand::Z(0)]); -} - -#[test] -fn test_v_synthesis() { - let clifford_tableau = sample_v_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::V(0),]); -} - -#[test] -fn test_v_dgr_synthesis() { - let clifford_tableau = sample_v_dgr_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::V(0), MockCommand::X(0)]); -} - -#[test] -fn test_cnot_synthesis() { - let clifford_tableau = sample_cnot_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::CX(0, 1)]); -} - -#[test] -fn test_cnot_reverse_synthesis() { - let clifford_tableau = sample_cnot_reverse_gate(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands(), &vec![MockCommand::CX(1, 0)]); -} - -#[test] -fn test_clifford_synthesis() { - let clifford_tableau = setup_sample_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); -} -#[test] -fn test_clifford_synthesis_large() { - let clifford_tableau = setup_sample_inverse_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); +macro_rules! test_clifford { + ($fun:ident, $expected:expr) => { + paste::item! { + #[test] + fn [< synthesize_ $fun>]() { + let clifford_tableau = $fun(); + let (mock, new_ct) = run_synthesizer(&clifford_tableau); + if $expected.is_some() { + assert_eq!(mock.commands(), $expected.unwrap()); + } + check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); + } + } + }; } -#[test] -fn test_clifford_synthesis_simple() { - let clifford_tableau = sample_2cnot_ladder(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!( - mock.commands(), - &vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)] - ); -} +// Single qubit gates +test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); +test_clifford!( + sample_s_dgr_gate, + Some(&vec![MockCommand::S(0), MockCommand::Z(0)]) +); +test_clifford!(sample_v_gate, Some(&vec![MockCommand::V(0)])); +test_clifford!( + sample_v_dgr_gate, + Some(&vec![MockCommand::V(0), MockCommand::X(0)]) +); -#[test] -fn test_prc_swap_to_identity() { - let clifford_tableau = sample_swap_ct(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 0); -} +// Advances Clifford Tableau +test_clifford!(setup_sample_ct, None::<&Vec>); +test_clifford!(setup_sample_inverse_ct, None::<&Vec>); -#[test] -fn test_prc_2_cnot_reduction_v1() { - let clifford_tableau = half_swap_0_1(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 1); -} - -#[test] -fn test_prc_2_cnot_reduction_v2() { - let clifford_tableau = half_swap_1_0(); - let (mock, new_ct) = run_synthesizer(&clifford_tableau); - check_mock_equals_clifford_tableau(&clifford_tableau, &mock, new_ct.get_permutation()); - assert_eq!(mock.commands().len(), 1); -} +// CNOT synthesis +test_clifford!(sample_cnot_gate, Some(&vec![MockCommand::CX(0, 1)])); +test_clifford!(sample_cnot_reverse_gate, Some(&vec![MockCommand::CX(1, 0)])); +test_clifford!( + sample_2cnot_ladder, + Some(&vec![MockCommand::CX(0, 1), MockCommand::CX(1, 2)]) +); +test_clifford!(sample_swap_ct, Some::<&Vec>(&vec![])); +test_clifford!(half_swap_0_1, Some(&vec![MockCommand::CX(1, 0),])); +test_clifford!(half_swap_1_0, Some(&vec![MockCommand::CX(0, 1),])); From 5047359f72bba1bc14dcf8234f7e0a46d83b52b1 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 11:33:31 +0200 Subject: [PATCH 41/46] Add check to ensure pivot qubit is non-identity Update clean_observable functions to apply an additional CNOT to set identity pivots to Z or X respectively. This removes onus of custom callback to always provide valid ordering. Cleaned up code slightly to remove unused variables and imports. Removed unneeded return functions, into_iter calls. Added documentation on updated functions. --- .../ir/clifford_tableau/custom_callback.rs | 15 +++---------- synir/src/ir/clifford_tableau/helper.rs | 22 ++++++++++++++++++- synir/tests/ct_synthesis/custom_callback.rs | 15 +++++-------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/synir/src/ir/clifford_tableau/custom_callback.rs b/synir/src/ir/clifford_tableau/custom_callback.rs index 0da378ae..402409dc 100644 --- a/synir/src/ir/clifford_tableau/custom_callback.rs +++ b/synir/src/ir/clifford_tableau/custom_callback.rs @@ -1,7 +1,3 @@ -use std::iter::zip; - -use itertools::Itertools; - use crate::{ data_structures::CliffordTableau, ir::{ @@ -65,16 +61,12 @@ where let mut remaining_columns = (0..num_qubits).collect::>(); let mut remaining_rows = (0..num_qubits).collect::>(); - let mut custom_columns = vec![]; - let mut custom_rows = vec![]; + // let mut custom_columns = vec![]; + // let mut custom_rows = vec![]; - // for (&pivot_column, &pivot_row) in zip(custom_columns, custom_rows) { while !remaining_columns.is_empty() { let (pivot_column, pivot_row) = (self.custom_callback)(&remaining_columns, &remaining_rows, &clifford_tableau); - // Cleanup pivot column - custom_columns.push(pivot_column); - custom_rows.push(pivot_row); remaining_columns.retain(|&x| x != pivot_column); remaining_rows.retain(|&x| x != pivot_row); @@ -100,8 +92,7 @@ where ); } } - clean_signs(repr, &mut clifford_tableau); - return clifford_tableau; + clifford_tableau } } diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 56020def..9431c0fa 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -116,6 +116,7 @@ pub(super) fn clean_observables( } } +/// Sets destabilizer entry at (pivot_column, pivot_row) to X if it is not I, leaves I terms unchanged. pub(super) fn clean_x_pivot( repr: &mut G, clifford_tableau: &mut CliffordTableau, @@ -137,6 +138,7 @@ pub(super) fn clean_x_pivot( } } +/// Sets destabilizer entry at (pivot_column, pivot_row) to Z if it is not I, leaves I terms unchanged. pub(super) fn clean_z_pivot( repr: &mut G, clifford_tableau: &mut CliffordTableau, @@ -170,6 +172,10 @@ pub(super) fn clean_z_pivot( } } +/// Cleans the destabilizer observables for `pivot_row` in the Clifford tableau using (pivot_column, pivot_row) as the entry for elimination.. +/// Assumes that (pivot_column, pivot_row) is either an I term or a X term. +/// Only removes entries from columns in `remaining_columns` and assumes `pivot_column` has already been removed from `remaining_columns`. +/// If (pivot_column, pivot_row) is an I term, set it to X first using a non-I term in (pivot_row, remaining_columns). pub(super) fn clean_x_observables( repr: &mut G, clifford_tableau: &mut CliffordTableau, @@ -198,12 +204,21 @@ pub(super) fn clean_x_observables( let affected_cols = check_across_columns(&*clifford_tableau, remaining_columns, pivot_row, is_not_i); + if check_pauli(clifford_tableau, pivot_column, pivot_row, is_i) { + repr.cx(affected_cols[0], pivot_column); + clifford_tableau.cx(affected_cols[0], pivot_column); + } + for col in affected_cols { repr.cx(pivot_column, col); clifford_tableau.cx(pivot_column, col); } } +/// Cleans the destabilizer observables for `pivot_row` in the Clifford tableau using (pivot_column, pivot_row) as the entry for elimination.. +/// Assumes that (pivot_column, pivot_row) is either an I term or a X term. +/// Only removes entries from columns in `remaining_columns` and assumes `pivot_column` has already been removed from `remaining_columns`. +/// If (pivot_column, pivot_row) is an I term, set it to X first using a non-I term in (pivot_row, remaining_columns). pub(super) fn clean_z_observables( repr: &mut G, clifford_tableau: &mut CliffordTableau, @@ -242,6 +257,12 @@ pub(super) fn clean_z_observables( pivot_row + num_qubits, is_not_i, ); + + if check_pauli(clifford_tableau, pivot_column, pivot_row + num_qubits, is_i) { + repr.cx(pivot_column, affected_cols[0]); + clifford_tableau.cx(pivot_column, affected_cols[0]); + } + for col in affected_cols { repr.cx(col, pivot_column); clifford_tableau.cx(col, pivot_column); @@ -261,7 +282,6 @@ where Some(perm) => perm, }; let row_permutation = (0..clifford_tableau.size()) - .into_iter() .map(|i| inv_perm.iter().find_position(|&&x| x == i)) .map(|x| x.unwrap().0) .collect_vec(); diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs index 82c6b163..9c0640ea 100644 --- a/synir/tests/ct_synthesis/custom_callback.rs +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -6,9 +6,9 @@ use crate::common::mock_circuit::{ check_mock_equals_clifford_tableau, parse_clifford_commands, MockCircuit, MockCommand, }; use crate::common::sample_clifford_tableaus::{ - half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, - sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, identity_2qb_ct + half_swap_0_1, half_swap_1_0, identity_2qb_ct, sample_2cnot_ladder, sample_cnot_gate, + sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, + sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use itertools::Itertools; use synir::data_structures::{CliffordTableau, PropagateClifford}; @@ -24,11 +24,9 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford println!("Shuffled rows: {:?}", custom_rows); let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(custom_columns, custom_rows); let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - return (mock, new_ct); + (mock, new_ct) } - - macro_rules! test_clifford { ($fun:ident, $expected:expr) => { paste::item! { @@ -65,14 +63,11 @@ test_clifford!(setup_sample_inverse_ct, None::<&Vec>); // CNOT synthesis test_clifford!(sample_cnot_gate, None::<&Vec>); test_clifford!(sample_cnot_reverse_gate, None::<&Vec>); -test_clifford!( - sample_2cnot_ladder,None::<&Vec> -); +test_clifford!(sample_2cnot_ladder, None::<&Vec>); test_clifford!(sample_swap_ct, None::<&Vec>); test_clifford!(half_swap_0_1, None::<&Vec>); test_clifford!(half_swap_1_0, None::<&Vec>); - #[test] fn test_custom_clifford_synthesis_old() { let clifford_tableau = setup_sample_ct(); From 08f9939556c022b56e5d467fb07a093ce969d2b9 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 11:33:57 +0200 Subject: [PATCH 42/46] Format file --- synir/tests/ct_synthesis/permrowcol_line.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/synir/tests/ct_synthesis/permrowcol_line.rs b/synir/tests/ct_synthesis/permrowcol_line.rs index 6d741afd..388222ef 100644 --- a/synir/tests/ct_synthesis/permrowcol_line.rs +++ b/synir/tests/ct_synthesis/permrowcol_line.rs @@ -1,8 +1,8 @@ use crate::common::mock_circuit::{check_mock_equals_clifford_tableau, MockCircuit, MockCommand}; use crate::common::sample_clifford_tableaus::{ - half_swap_0_1, half_swap_1_0, sample_2cnot_ladder, sample_cnot_gate, sample_cnot_reverse_gate, - sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, sample_v_gate, - setup_sample_ct, setup_sample_inverse_ct, identity_2qb_ct + half_swap_0_1, half_swap_1_0, identity_2qb_ct, sample_2cnot_ladder, sample_cnot_gate, + sample_cnot_reverse_gate, sample_s_dgr_gate, sample_s_gate, sample_swap_ct, sample_v_dgr_gate, + sample_v_gate, setup_sample_ct, setup_sample_inverse_ct, }; use synir::architecture::connectivity::Connectivity; use synir::data_structures::CliffordTableau; @@ -19,7 +19,6 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford return (mock, new_ct); } - macro_rules! test_clifford { ($fun:ident, $expected:expr) => { paste::item! { From 01049446ba4522afff91023d0bc57afa664239af Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 13:46:29 +0200 Subject: [PATCH 43/46] Modify pick_column to penalize identity on row The additional CNOTs seen are included because the chosen pivot is the identity. To avoid this, we add the cost of the additional CNOT to the pick_column heuristic. This breaks ties in our favor for symmetric setups. --- synir/src/ir/clifford_tableau/helper.rs | 5 +++++ synir/src/ir/clifford_tableau/naive.rs | 2 +- synir/src/ir/clifford_tableau/permrowcol.rs | 2 +- synir/tests/ct_synthesis/permrowcol_line.rs | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index 9431c0fa..28dcbd6e 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -414,6 +414,11 @@ pub(super) fn pick_column( (clifford_tableau.destabilizer(*qubit, interaction) != PauliLetter::I) as usize; column_weights[*qubit] += connectivity.distance(*qubit, interaction) * (mult_x + mult_z); + } else { + column_weights[*qubit] += + (clifford_tableau.stabilizer(*qubit, interaction) == PauliLetter::I) as usize; + column_weights[*qubit] += + (clifford_tableau.destabilizer(*qubit, interaction) == PauliLetter::I) as usize; } } } diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 3ad88bc4..5f50a6a2 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -52,6 +52,6 @@ where } clean_signs(repr, &mut clifford_tableau); - return clifford_tableau; + clifford_tableau } } diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs index a4856883..2ede25c7 100644 --- a/synir/src/ir/clifford_tableau/permrowcol.rs +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -125,6 +125,6 @@ where } clean_signs(repr, &mut clifford_tableau); - return clifford_tableau; + clifford_tableau } } diff --git a/synir/tests/ct_synthesis/permrowcol_line.rs b/synir/tests/ct_synthesis/permrowcol_line.rs index 388222ef..476e8438 100644 --- a/synir/tests/ct_synthesis/permrowcol_line.rs +++ b/synir/tests/ct_synthesis/permrowcol_line.rs @@ -13,10 +13,10 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford let num_qubits = clifford_tableau.size(); let mut mock = MockCircuit::new(); let connectivity = Connectivity::line(num_qubits); - println!("size {}, qpu {}", num_qubits, connectivity.node_count()); + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - return (mock, new_ct); + (mock, new_ct) } macro_rules! test_clifford { From 701951ff3904e3686bfa87a15ea80b78b18b71ed Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 13:52:53 +0200 Subject: [PATCH 44/46] Allow identity_2qb_ct test for custom callback to return a permuted CT The custom callback sets the chosen pivot_row and pivot_column to be the pivot qubit and thus determines the non-identity term in the horizontal and vertical identity rows. Thus, we should allow synthesis up to permutation if we have off-diagonal pivot_row and pivot_column possibilities. --- synir/tests/ct_synthesis/custom_callback.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs index 9c0640ea..37fa5649 100644 --- a/synir/tests/ct_synthesis/custom_callback.rs +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -44,7 +44,7 @@ macro_rules! test_clifford { } // Single qubit gates -test_clifford!(identity_2qb_ct, Some::<&Vec>(&vec![])); +test_clifford!(identity_2qb_ct, None::<&Vec>); test_clifford!(sample_s_gate, Some(&vec![MockCommand::S(0)])); test_clifford!( sample_s_dgr_gate, From c32da81a7c4ce70a282af72446694f30c5390a40 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 13:56:29 +0200 Subject: [PATCH 45/46] Remove print statement Remove unneeded return --- synir/tests/ct_synthesis/custom_callback.rs | 2 +- synir/tests/ct_synthesis/naive.rs | 2 +- synir/tests/ct_synthesis/naive_adjoint.rs | 2 +- synir/tests/ct_synthesis/permrowcol_complete.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/synir/tests/ct_synthesis/custom_callback.rs b/synir/tests/ct_synthesis/custom_callback.rs index 37fa5649..05a923d2 100644 --- a/synir/tests/ct_synthesis/custom_callback.rs +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -21,7 +21,7 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford let custom_columns = (0..clifford_tableau.size()).collect_vec(); let mut custom_rows = (0..clifford_tableau.size()).collect_vec(); custom_rows.shuffle(&mut rng); - println!("Shuffled rows: {:?}", custom_rows); + let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(custom_columns, custom_rows); let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); (mock, new_ct) diff --git a/synir/tests/ct_synthesis/naive.rs b/synir/tests/ct_synthesis/naive.rs index 216d8f04..bd20795b 100644 --- a/synir/tests/ct_synthesis/naive.rs +++ b/synir/tests/ct_synthesis/naive.rs @@ -12,7 +12,7 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford let mut mock = MockCircuit::new(); let mut synthesizer = NaiveCliffordSynthesizer::default(); let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - return (mock, new_ct); + (mock, new_ct) } macro_rules! test_clifford { diff --git a/synir/tests/ct_synthesis/naive_adjoint.rs b/synir/tests/ct_synthesis/naive_adjoint.rs index 73e9af68..26aa9a2c 100644 --- a/synir/tests/ct_synthesis/naive_adjoint.rs +++ b/synir/tests/ct_synthesis/naive_adjoint.rs @@ -12,7 +12,7 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford let mut mock = MockCircuit::new(); let mut synthesizer = NaiveCliffordSynthesizer::default(); let new_ct = synthesizer.synthesize_adjoint(clifford_tableau.adjoint(), &mut mock); - return (mock, new_ct); + (mock, new_ct) } macro_rules! test_clifford { diff --git a/synir/tests/ct_synthesis/permrowcol_complete.rs b/synir/tests/ct_synthesis/permrowcol_complete.rs index eafb8545..dabd9dc9 100644 --- a/synir/tests/ct_synthesis/permrowcol_complete.rs +++ b/synir/tests/ct_synthesis/permrowcol_complete.rs @@ -16,7 +16,7 @@ fn run_synthesizer(clifford_tableau: &CliffordTableau) -> (MockCircuit, Clifford println!("size {}, qpu {}", num_qubits, connectivity.node_count()); let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); - return (mock, new_ct); + (mock, new_ct) } macro_rules! test_clifford { From 3f16f1a735f6240623d1928154db54105f073d61 Mon Sep 17 00:00:00 2001 From: Keefe Huang Date: Fri, 24 Oct 2025 14:10:19 +0200 Subject: [PATCH 46/46] Add comments for permutation direction --- synir/src/data_structures/clifford_tableau.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index 55bba1c7..a5d34cc5 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -208,6 +208,8 @@ impl CliffordTableau { self.pauli_columns = sorted_pauli_columns; } + /// Calculates the row permutation of a Clifford tableau if it is a permutation of identity. + /// The permutation indicates perm[physical] = logical, such that the for [1, 0, 2], physical qubit 0 stores logical qubit 1. pub fn get_permutation(&self) -> Option> { let mut row_permutation = Vec::new(); //let mut col_permutation = Vec::new(); @@ -218,12 +220,11 @@ impl CliffordTableau { //col_permutation.push(pauli_column.z.first_one().unwrap() - self.size); row_permutation.push(pauli_column.x.first_one().unwrap()); } - let inverse_perm: Vec = (0..self.size) - .into_iter() + let _ = (0..self.size) .map(|i| row_permutation.iter().find_position(|&&x| x == i)) .map(|x| x.unwrap().0) .collect_vec(); - return Some(row_permutation); + Some(row_permutation) } }