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/benches/clifford_tableau.rs b/synir/benches/clifford_tableau.rs index dc53d556..7acb33e2 100644 --- a/synir/benches/clifford_tableau.rs +++ b/synir/benches/clifford_tableau.rs @@ -4,8 +4,8 @@ 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::clifford_tableau::NaiveCliffordSynthesizer; use synir::ir::CliffordGates; use synir::ir::Synthesizer; use synir::IndexType; diff --git a/synir/benches/connectivity.rs b/synir/benches/connectivity.rs index b2a9903a..08f6de24 100644 --- a/synir/benches/connectivity.rs +++ b/synir/benches/connectivity.rs @@ -1,8 +1,7 @@ use criterion::{black_box, criterion_group, Criterion}; -use petgraph::visit::Walker; use rand::prelude::IndexedRandom; use rand::seq::SliceRandom; -use rand::Rng; +use rand::{rng, Rng}; use synir::architecture::connectivity::Connectivity; use synir::architecture::Architecture; @@ -11,7 +10,7 @@ fn random_connected_connectivity( 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) { 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..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,13 +77,22 @@ impl Connectivity { } pub fn from_edges(edges: &[(GraphIndex, GraphIndex)]) -> Self { - let graph = StableUnGraph::from_edges(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 { @@ -109,6 +115,14 @@ impl Connectivity { .collect() } + pub fn node_count(&self) -> usize { + self.graph.node_count() + } + + pub fn edge_count(&self) -> usize { + self.graph.edge_count() + } + pub fn edges(&self) -> Vec<(GraphIndex, GraphIndex)> { self.graph .edge_references() @@ -158,41 +172,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 } @@ -429,7 +423,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] @@ -440,7 +434,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 +449,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 +469,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 +477,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 +498,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]); } diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index 3c7ac932..a5d34cc5 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; @@ -9,10 +10,12 @@ use super::{ pauli_string::{cx, PauliString}, IndexType, PropagateClifford, }; +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 @@ -51,10 +54,40 @@ 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, + "Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {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, + "Given index: {index} or qubit: {qubit} out of bounds for clifford tableau of size {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(crate) fn columns(&self) -> &Vec { + &self.pauli_columns + } + pub fn compose(&self, rhs: &Self) -> Self { rhs.prepend(self) } @@ -158,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() @@ -174,6 +207,25 @@ impl CliffordTableau { .collect::>(); 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(); + 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 _ = (0..self.size) + .map(|i| row_permutation.iter().find_position(|&&x| x == i)) + .map(|x| x.unwrap().0) + .collect_vec(); + Some(row_permutation) + } } impl HasAdjoint for CliffordTableau { diff --git a/synir/src/data_structures/pauli_string.rs b/synir/src/data_structures/pauli_string.rs index 6b1fd7d8..d66a3f5a 100644 --- a/synir/src/data_structures/pauli_string.rs +++ b/synir/src/data_structures/pauli_string.rs @@ -53,6 +53,14 @@ impl PauliString { self.x[i] } + pub fn x_weight(&self) -> usize { + self.x.count_ones() + } + + pub fn z_weight(&self) -> usize { + self.z.count_ones() + } + pub fn z(&self, i: usize) -> bool { self.z[i] } diff --git a/synir/src/ir/clifford_tableau.rs b/synir/src/ir/clifford_tableau.rs index 228cfb60..f843df98 100644 --- a/synir/src/ir/clifford_tableau.rs +++ b/synir/src/ir/clifford_tableau.rs @@ -3,21 +3,26 @@ 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 { #[default] Naive, + PermRowCol, 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 8e6a3a50..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::{ @@ -12,14 +8,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,35 +41,32 @@ 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 } } -impl<'a, G> 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::>(); 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); @@ -84,7 +76,7 @@ where clean_x_observables( repr, &mut clifford_tableau, - &remaining_rows, + &remaining_columns, pivot_column, pivot_row, ); @@ -94,17 +86,13 @@ where clean_z_observables( repr, &mut clifford_tableau, - &remaining_rows, + &remaining_columns, pivot_column, pivot_row, ); } } - 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); + clifford_tableau } } diff --git a/synir/src/ir/clifford_tableau/helper.rs b/synir/src/ir/clifford_tableau/helper.rs index c2c8f657..28dcbd6e 100644 --- a/synir/src/ir/clifford_tableau/helper.rs +++ b/synir/src/ir/clifford_tableau/helper.rs @@ -1,6 +1,9 @@ use std::iter::zip; +use itertools::Itertools; + use crate::{ + architecture::{connectivity::Connectivity, Architecture}, data_structures::{CliffordTableau, PauliLetter, PauliString, PropagateClifford}, ir::CliffordGates, }; @@ -43,7 +46,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 +71,52 @@ 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"), + } +} + +/// 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, @@ -77,18 +126,19 @@ 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); } } +/// 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, @@ -98,40 +148,45 @@ 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); } } +/// 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, - 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 +194,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 +202,27 @@ 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); + + 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_row, col); - clifford_tableau.cx(pivot_row, col); + 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, - remaining_rows: &[usize], + remaining_columns: &[usize], pivot_column: usize, pivot_row: usize, ) where @@ -167,8 +231,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 +242,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,26 +253,39 @@ 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, ); + + 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_row); - clifford_tableau.cx(col, pivot_row); + repr.cx(col, pivot_column); + clifford_tableau.cx(col, pivot_column); } } -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()) + .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); @@ -217,7 +294,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); @@ -258,7 +335,6 @@ pub(super) fn naive_pivot_search( break; } } - pivot_col } @@ -290,3 +366,211 @@ 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, + pivot_row: usize, +) -> 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() { + 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); + } 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; + } + } + } + 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); + 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); + } +} diff --git a/synir/src/ir/clifford_tableau/naive.rs b/synir/src/ir/clifford_tableau/naive.rs index 9309c525..5f50a6a2 100644 --- a/synir/src/ir/clifford_tableau/naive.rs +++ b/synir/src/ir/clifford_tableau/naive.rs @@ -7,14 +7,26 @@ use super::helper::{ clean_pivot, clean_signs, clean_x_observables, clean_z_observables, naive_pivot_search, swap, }; -#[derive(Default)] +use crate::data_structures::PauliLetter; + +#[derive(Default, Debug)] pub struct NaiveCliffordSynthesizer {} -impl AdjointSynthesizer for NaiveCliffordSynthesizer +impl NaiveCliffordSynthesizer { + pub fn name(&self) -> &str { + return "naive"; + } +} + +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 { @@ -25,21 +37,21 @@ where } // Cleanup pivot column - clean_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); } - clean_signs( - repr, - &mut clifford_tableau, - &(0..num_qubits).collect::>(), - ); + clean_signs(repr, &mut clifford_tableau); + clifford_tableau } } diff --git a/synir/src/ir/clifford_tableau/permrowcol.rs b/synir/src/ir/clifford_tableau/permrowcol.rs new file mode 100644 index 00000000..2ede25c7 --- /dev/null +++ b/synir/src/ir/clifford_tableau/permrowcol.rs @@ -0,0 +1,130 @@ +use crate::{ + architecture::connectivity::Connectivity, + data_structures::{CliffordTableau, PauliLetter}, + ir::{ + clifford_tableau::helper::{clean_pivot, clean_prc, pick_column, pick_row}, + AdjointSynthesizer, CliffordGates, + }, +}; + +use super::helper::clean_signs; + +// #[derive(Default)] +pub struct PermRowColCliffordSynthesizer { + connectivity: Connectivity, + row_strategy: fn(&CliffordTableau, &Connectivity, &[usize]) -> usize, + column_strategy: fn(&CliffordTableau, &Connectivity, usize) -> usize, +} + +impl PermRowColCliffordSynthesizer { + pub fn new(connectivity: Connectivity) -> Self { + let size = connectivity.node_count(); + + Self { + connectivity, + row_strategy: pick_row, + column_strategy: pick_column, + } + } + + 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) -> usize, + ) { + (self.column_strategy) = column_strategy; + } +} + +impl AdjointSynthesizer for PermRowColCliffordSynthesizer +where + G: CliffordGates, +{ + 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!( + num_qubits <= machine_size, + "Number of qubits {} exceeds machine size {}", + num_qubits, + machine_size + ); + // 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 = + (self.row_strategy)(&clifford_tableau, &self.connectivity, &remaining_rows); + 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(); + + 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, + ); + + // 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); + } + + clean_signs(repr, &mut clifford_tableau); + clifford_tableau + } +} 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); + } }; } } diff --git a/synir/tests/clifford_tableau.rs b/synir/tests/clifford_tableau.rs deleted file mode 100644 index 11d54d04..00000000 --- a/synir/tests/clifford_tableau.rs +++ /dev/null @@ -1,241 +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::S(1), - MockCommand::H(1), - MockCommand::S(1), - MockCommand::X(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::S(1), MockCommand::H(1), MockCommand::S(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.rs b/synir/tests/common/mock_circuit.rs similarity index 83% rename from synir/tests/common.rs rename to synir/tests/common/mock_circuit.rs index a98ff57e..fd25ac9b 100644 --- a/synir/tests/common.rs +++ b/synir/tests/common/mock_circuit.rs @@ -1,3 +1,5 @@ +use std::cell::RefMut; + use synir::{ data_structures::{CliffordTableau, PropagateClifford}, ir::{CliffordGates, Gates}, @@ -123,3 +125,22 @@ pub fn parse_clifford_commands(size: usize, commands: &[MockCommand]) -> Cliffor } 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 new file mode 100644 index 00000000..17c2f05b --- /dev/null +++ b/synir/tests/common/mod.rs @@ -0,0 +1,2 @@ +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..d1c81442 --- /dev/null +++ b/synir/tests/common/sample_clifford_tableaus.rs @@ -0,0 +1,118 @@ +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 identity_2qb_ct() -> CliffordTableau { + return CliffordTableau::new(2); +} + +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..05a923d2 --- /dev/null +++ b/synir/tests/ct_synthesis/custom_callback.rs @@ -0,0 +1,112 @@ +extern crate rand; + +use rand::seq::SliceRandom; + +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, 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}; +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); + + let mut synthesizer = CallbackCliffordSynthesizer::custom_pivot(custom_columns, custom_rows); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + (mock, new_ct) +} + +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, None::<&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_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); +} diff --git a/synir/tests/ct_synthesis/mod.rs b/synir/tests/ct_synthesis/mod.rs new file mode 100644 index 00000000..2fff57d6 --- /dev/null +++ b/synir/tests/ct_synthesis/mod.rs @@ -0,0 +1,5 @@ +pub mod custom_callback; +pub mod naive; +pub mod naive_adjoint; +pub mod permrowcol_complete; +pub mod permrowcol_line; diff --git a/synir/tests/ct_synthesis/naive.rs b/synir/tests/ct_synthesis/naive.rs new file mode 100644 index 00000000..bd20795b --- /dev/null +++ b/synir/tests/ct_synthesis/naive.rs @@ -0,0 +1,78 @@ +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, 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; +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); + (mock, new_ct) +} + +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_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 new file mode 100644 index 00000000..26aa9a2c --- /dev/null +++ b/synir/tests/ct_synthesis/naive_adjoint.rs @@ -0,0 +1,78 @@ +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, 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; +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); + (mock, new_ct) +} + +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_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 new file mode 100644 index 00000000..dabd9dc9 --- /dev/null +++ b/synir/tests/ct_synthesis/permrowcol_complete.rs @@ -0,0 +1,64 @@ +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, 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; +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); + (mock, new_ct) +} + +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, 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 new file mode 100644 index 00000000..476e8438 --- /dev/null +++ b/synir/tests/ct_synthesis/permrowcol_line.rs @@ -0,0 +1,64 @@ +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, 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; +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); + + let mut synthesizer = PermRowColCliffordSynthesizer::new(connectivity); + let new_ct = synthesizer.synthesize(clifford_tableau.clone(), &mut mock); + (mock, new_ct) +} + +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, 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_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 161b5f4f..324aa30d 100644 --- a/synir/tests/pauli_exponential.rs +++ b/synir/tests/pauli_exponential.rs @@ -2,8 +2,8 @@ mod common; use std::collections::VecDeque; -use common::{parse_clifford_commands, MockCircuit, MockCommand}; -use synir::data_structures::{CliffordTableau, PauliPolynomial}; +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; use synir::ir::pauli_exponential::PauliExponentialSynthesizer; @@ -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), ]; 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/synpy/integration_tests/test_pauliopt.py b/synpy/integration_tests/test_pauliopt.py index ed22b499..f23fec63 100644 --- a/synpy/integration_tests/test_pauliopt.py +++ b/synpy/integration_tests/test_pauliopt.py @@ -5,6 +5,8 @@ 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 @@ -55,6 +57,18 @@ def generate_random_pauli_polynomial( 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 + + def verify_equality(qc_in: QuantumCircuit, qc_out: QuantumCircuit) -> bool: """ Verify the equality up to a global phase @@ -75,3 +89,11 @@ def test_naive_synthesis(self, nr_qubits: int, nr_gadgets: int) -> None: 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() + + assert verify_equality(qc, qc_syn)