diff --git a/synir/src/data_structures.rs b/synir/src/data_structures.rs index 98aade8b..8ede657d 100644 --- a/synir/src/data_structures.rs +++ b/synir/src/data_structures.rs @@ -1,10 +1,12 @@ use crate::IndexType; +use bitvec::vec::BitVec; +pub mod angle; mod clifford_tableau; mod pauli_polynomial; mod pauli_string; -use bitvec::vec::BitVec; +pub use angle::Angle; pub use clifford_tableau::CliffordTableau; pub use pauli_polynomial::PauliPolynomial; pub use pauli_string::PauliString; diff --git a/synir/src/data_structures/angle.rs b/synir/src/data_structures/angle.rs new file mode 100644 index 00000000..703e1875 --- /dev/null +++ b/synir/src/data_structures/angle.rs @@ -0,0 +1,292 @@ +use std::{ + f64::consts::PI, + ops::{Add, AddAssign, Sub, SubAssign}, +}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Angle { + Arbitrary(f64), + Pi4Rotations(u8), +} + +impl Angle { + pub fn from_angle(rad: f64) -> Self { + Angle::Arbitrary(rad) + } + + pub fn from_angles(angles: &[f64]) -> Vec { + angles + .into_iter() + .map(|rad| Angle::from_angle(*rad)) + .collect() + } + + pub fn from_pi4_rotation(n: u8) -> Self { + Angle::Pi4Rotations(n % 8) + } + + pub fn from_pi4_rotations(ns: &[u8]) -> Vec { + ns.into_iter() + .map(|n| Angle::from_pi4_rotation(*n)) + .collect() + } + + pub fn to_radians(&self) -> f64 { + match self { + Angle::Arbitrary(rad) => *rad, + Angle::Pi4Rotations(n) => (*n as f64) * (std::f64::consts::FRAC_PI_4), + } + } + + pub fn flip(&mut self) { + match self { + Angle::Arbitrary(rad) => *rad = -*rad, + Angle::Pi4Rotations(n) => *n = (8 - *n) % 8, + } + } +} + +impl AddAssign for Angle { + fn add_assign(&mut self, other: Self) { + match (self, other) { + (Angle::Arbitrary(rad1), Angle::Arbitrary(rad2)) => { + *rad1 += rad2; + } + (Angle::Pi4Rotations(n1), Angle::Pi4Rotations(n2)) => { + *n1 = (*n1 + n2) % 8; + } + (Angle::Arbitrary(rad1), Angle::Pi4Rotations(n2)) => { + *rad1 += n2 as f64 * PI / 4.0; + } + _ => panic!("Cannot add Arbitrary Angle to Pi4 rotation"), + } + } +} + +impl SubAssign for Angle { + fn sub_assign(&mut self, other: Self) { + match (self, other) { + (Angle::Arbitrary(rad1), Angle::Arbitrary(rad2)) => { + *rad1 -= rad2; + } + (Angle::Pi4Rotations(n1), Angle::Pi4Rotations(n2)) => { + *n1 = (*n1 + (8 - n2)) % 8; + } + (Angle::Arbitrary(rad1), Angle::Pi4Rotations(n2)) => { + *rad1 -= n2 as f64 * PI / 4.0; + } + _ => panic!("Cannot subtract different types of Angles"), + } + } +} + +impl Add for Angle { + type Output = Angle; + + fn add(self, other: Angle) -> Angle { + match (self, other) { + (Angle::Arbitrary(rad1), Angle::Arbitrary(rad2)) => Angle::Arbitrary(rad1 + rad2), + (Angle::Pi4Rotations(n1), Angle::Pi4Rotations(n2)) => { + Angle::Pi4Rotations((n1 + n2) % 8) + } + (Angle::Arbitrary(rad1), Angle::Pi4Rotations(n2)) => { + Angle::Arbitrary(rad1 + n2 as f64 * PI / 4.0) + } + (Angle::Pi4Rotations(n1), Angle::Arbitrary(rad2)) => { + Angle::Arbitrary(rad2 + n1 as f64 * PI / 4.0) + } + } + } +} + +impl Sub for Angle { + type Output = Angle; + + fn sub(self, other: Angle) -> Angle { + match (self, other) { + (Angle::Arbitrary(rad1), Angle::Arbitrary(rad2)) => Angle::Arbitrary(rad1 - rad2), + (Angle::Pi4Rotations(n1), Angle::Pi4Rotations(n2)) => { + Angle::Pi4Rotations((n1 + 8 - n2) % 8) + } + (Angle::Arbitrary(rad1), Angle::Pi4Rotations(n2)) => { + Angle::Arbitrary(rad1 - n2 as f64 * PI / 4.0) + } + (Angle::Pi4Rotations(n1), Angle::Arbitrary(rad2)) => { + Angle::Arbitrary(n1 as f64 * PI / 4.0 - rad2) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn check_angle_approx(angle1: Angle, angle2: Angle) -> bool { + match (angle1, angle2) { + (Angle::Arbitrary(a1), Angle::Arbitrary(a2)) => (a1 - a2).abs() < 1e-9, + (Angle::Pi4Rotations(a1), Angle::Pi4Rotations(a2)) => a1 == a2, + _ => panic!("Not defined for Arbitrary Angles and Pi4 rotations"), + } + } + + #[test] + fn test_angle_simple_add() { + let n1 = 1; + let n2 = 2; + + let a1 = Angle::from_pi4_rotation(n1); + let a2 = Angle::from_pi4_rotation(n2); + + assert_eq!(a1 + a2, Angle::from_pi4_rotation(3)); + + let mut a3 = Angle::from_pi4_rotation(n1); + a3 += a2; + + assert_eq!(a3, Angle::from_pi4_rotation(3)); + } + + #[test] + fn test_angle_overflow_add() { + let n1 = 5; + let n2 = 6; + + let a1 = Angle::from_pi4_rotation(n1); + let a2 = Angle::from_pi4_rotation(n2); + + assert_eq!(a1 + a2, Angle::from_pi4_rotation(3)); + + let mut a3 = Angle::from_pi4_rotation(n1); + a3 += a2; + + assert_eq!(a3, Angle::from_pi4_rotation(3)); + } + + #[test] + fn test_angle_simple_sub() { + let n1 = 4; + let n2 = 2; + + let a1 = Angle::from_pi4_rotation(n1); + let a2 = Angle::from_pi4_rotation(n2); + + assert_eq!(a1 - a2, Angle::from_pi4_rotation(2)); + + let mut a3 = Angle::from_pi4_rotation(n1); + a3 -= a2; + + assert_eq!(a3, Angle::from_pi4_rotation(2)); + } + + #[test] + fn test_angle_overflow_sub() { + let n1 = 2; + let n2 = 6; + + let a1 = Angle::from_pi4_rotation(n1); + let a2 = Angle::from_pi4_rotation(n2); + + assert_eq!(a1 - a2, Angle::from_pi4_rotation(4)); + + let mut a3 = Angle::from_pi4_rotation(n1); + a3 -= a2; + + assert_eq!(a3, Angle::from_pi4_rotation(4)); + } + + #[test] + fn test_angle_float_simple_add() { + let n1 = 0.32; + let n2 = 0.64; + + let a1 = Angle::from_angle(n1); + let a2 = Angle::from_angle(n2); + + let ref_a = Angle::from_angle(0.96); + + assert!(check_angle_approx(a1 + a2, ref_a)); + + let mut a3 = Angle::from_angle(n1); + a3 += a2; + + assert!(check_angle_approx(a3, ref_a)); + } + + #[test] + fn test_angle_float_simple_sub() { + let n1 = 0.32; + let n2 = 0.64; + + let a1 = Angle::from_angle(n1); + let a2 = Angle::from_angle(n2); + + let ref_a = Angle::from_angle(-0.32); + + assert!(check_angle_approx(a1 - a2, ref_a)); + + let mut a3 = Angle::from_angle(n1); + a3 -= a2; + + assert!(check_angle_approx(a3, ref_a)); + } + + #[test] + fn test_angle_mixed_simple_add() { + let n1 = 0.32; + let n2 = 2; + + let a1 = Angle::from_angle(n1); + let a2 = Angle::from_pi4_rotation(n2); + + let ref_a = Angle::from_angle(1.8907963268); + assert!(check_angle_approx(a1 + a2, ref_a)); + assert!(check_angle_approx(a2 + a1, ref_a)); + + let mut a3 = Angle::from_angle(n1); + a3 += a2; + + assert!(check_angle_approx(a3, ref_a)); + } + + #[test] + #[should_panic] + fn test_angle_bad_mixed_simple_add() { + let n1 = 0.32; + let n2 = 2; + + let mut a2 = Angle::from_pi4_rotation(n2); + let a3 = Angle::from_angle(n1); + a2 += a3 + } + + #[test] + fn test_angle_mixed_simple_sub() { + let n1 = 0.32; + let n2 = 2; + + let a1 = Angle::from_angle(n1); + let a2 = Angle::from_pi4_rotation(n2); + + let ref_a1 = Angle::from_angle(-1.2507963268); + let ref_a2 = Angle::from_angle(1.2507963268); + + assert!(check_angle_approx(a1 - a2, ref_a1)); + assert!(check_angle_approx(a2 - a1, ref_a2)); + + let mut a3 = Angle::from_angle(n1); + a3 -= a2; + + assert!(check_angle_approx(a3, ref_a1)); + } + + #[test] + #[should_panic] + fn test_angle_bad_mixed_simple_sub() { + let n1 = 0.32; + let n2 = 2; + + let mut a2 = Angle::from_pi4_rotation(n2); + let a3 = Angle::from_angle(n1); + a2 -= a3 + } +} diff --git a/synir/src/data_structures/clifford_tableau.rs b/synir/src/data_structures/clifford_tableau.rs index a5d34cc5..94c0e99f 100644 --- a/synir/src/data_structures/clifford_tableau.rs +++ b/synir/src/data_structures/clifford_tableau.rs @@ -10,7 +10,7 @@ use super::{ pauli_string::{cx, PauliString}, IndexType, PropagateClifford, }; -use crate::data_structures::PauliLetter; +use crate::data_structures::{Angle, PauliLetter}; #[derive(PartialEq, Eq, Debug, Clone, Default)] pub struct CliffordTableau { @@ -191,6 +191,89 @@ impl CliffordTableau { } } + /// Composes a gadget onto a Clifford tableau if the angle is Clifford + /// Decomposes the Pauli gadget by performing naive decomposition into mapping to Z legs, CNOT walls and Z-rotations + pub fn compose_gadget(&mut self, rhs: (PauliString, Angle)) { + let (pauli_string, angle) = rhs; + let size = self.size(); + assert_eq!( + size, + pauli_string.len(), + "Cannot compose Clifford tableau with PauliPolynomial of different size" + ); + let pi2rotations = match angle { + Angle::Arbitrary(angle) => panic!( + "Cannot compose Clifford tableau with non-Clifford angle: {}", + angle + ), + Angle::Pi4Rotations(rotations) => { + if rotations % 2 == 1 { + panic!("Cannot compose Clifford tableau with non-Clifford angle: {} pi/4 rotations", rotations); + } + (rotations >> 1) % 4 + } + }; + let mut leg_numbers = Vec::with_capacity(size); + for i in 0..size { + match pauli_string.pauli(i) { + PauliLetter::I => {} + PauliLetter::X => { + self.h(i); + leg_numbers.push(i); + } + PauliLetter::Y => { + self.v(i); + leg_numbers.push(i); + } + PauliLetter::Z => { + leg_numbers.push(i); + } + } + } + + for (control, target) in leg_numbers.iter().tuple_windows() { + self.cx(*control, *target); + } + match pi2rotations { + 0 => {} + 1 => { + let target = *leg_numbers.last().unwrap(); + self.s(target); + } + 2 => { + let target = *leg_numbers.last().unwrap(); + self.z(target); + } + 3 => { + let target = *leg_numbers.last().unwrap(); + self.s_dgr(target); + } + _ => unreachable!(), + } + + for (control, target) in leg_numbers + .iter() + .tuple_windows() + .collect_vec() + .iter() + .rev() + { + self.cx(**control, **target); + } + for i in 0..size { + match pauli_string.pauli(i) { + PauliLetter::I => {} + PauliLetter::X => { + self.h(i); + } + PauliLetter::Y => { + self.v_dgr(i); + } + PauliLetter::Z => {} + } + } + } + pub fn permute(&mut self, permutation_vector: Vec) { assert_eq!( permutation_vector diff --git a/synir/src/data_structures/pauli_polynomial.rs b/synir/src/data_structures/pauli_polynomial.rs index ff38897b..02bc1357 100644 --- a/synir/src/data_structures/pauli_polynomial.rs +++ b/synir/src/data_structures/pauli_polynomial.rs @@ -1,12 +1,11 @@ use std::iter::zip; +use super::{pauli_string::PauliString, IndexType, MaskedPropagateClifford, PropagateClifford}; +use crate::data_structures::{Angle, PauliLetter}; use bitvec::vec::BitVec; use itertools::zip_eq; -use super::{pauli_string::PauliString, IndexType, MaskedPropagateClifford, PropagateClifford}; - -// todo: Make this into a union / type Angle -type Angle = f64; +mod simplify; #[derive(Debug, Clone, Default)] pub struct PauliPolynomial { @@ -62,6 +61,35 @@ impl PauliPolynomial { pub fn angle(&self, i: usize) -> Angle { self.angles[i] } + + pub fn commutes_with(&self, other: &PauliPolynomial) -> bool { + let size = self.size(); + assert_eq!(size, other.size()); + + let self_length = self.length(); + let other_length = other.length(); + + for index_1 in 0..self_length { + let mut pauli_string = Vec::with_capacity(size); + for q1 in 0..size { + pauli_string.push(self.chain(q1).pauli(index_1)); + } + for index_2 in 0..other_length { + let other_pauli_string = (0..size).map(|q2| other.chain(q2).pauli(index_2)); + let mut commutes = true; + for (p1, p2) in zip(&pauli_string, other_pauli_string) { + if *p1 == PauliLetter::I || p2 == PauliLetter::I || p1 == &p2 { + continue; + } + commutes = !commutes; + } + if !commutes { + return false; + } + } + } + true + } } impl PropagateClifford for PauliPolynomial { @@ -78,7 +106,7 @@ impl PropagateClifford for PauliPolynomial { super::pauli_string::cx(control, target); for (angle, flip) in zip(self.angles.iter_mut(), bit_mask.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } @@ -91,7 +119,7 @@ impl PropagateClifford for PauliPolynomial { let y_vec = chains_target.y_bitmask(); for (angle, flip) in zip(self.angles.iter_mut(), y_vec.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } chains_target.s(); @@ -105,7 +133,7 @@ impl PropagateClifford for PauliPolynomial { let y_vec = chains_target.y_bitmask(); for (angle, flip) in zip(self.angles.iter_mut(), y_vec.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } self @@ -126,7 +154,7 @@ impl MaskedPropagateClifford for PauliPolynomial { super::pauli_string::masked_cx(control, target, mask); for (angle, flip) in zip(self.angles.iter_mut(), bit_mask.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } @@ -140,7 +168,7 @@ impl MaskedPropagateClifford for PauliPolynomial { let y_vec = chains_target.masked_y_bitmask(mask); for (angle, flip) in zip(self.angles.iter_mut(), y_vec.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } chains_target.masked_s(mask); @@ -154,15 +182,17 @@ impl MaskedPropagateClifford for PauliPolynomial { let y_vec = chains_target.masked_y_bitmask(mask); for (angle, flip) in zip(self.angles.iter_mut(), y_vec.iter()) { if *flip { - *angle *= -1.0; + angle.flip(); } } self } } + #[cfg(test)] mod tests { use super::*; + use itertools::Itertools; impl PartialEq for PauliPolynomial { fn eq(&self, other: &Self) -> bool { @@ -173,7 +203,11 @@ mod tests { #[test] fn test_pauli_polynomial_constructor() { let size = 3; - let ham = vec![("IXYZ", 0.3), ("XXII", 0.7), ("YYII", 0.12)]; + let ham = vec![ + ("IXYZ", Angle::from_angle(0.3)), + ("XXII", Angle::from_angle(0.7)), + ("YYII", Angle::from_angle(0.12)), + ]; let pp = PauliPolynomial::from_hamiltonian(ham); let pg1_ref = PauliString::from_text("IXY"); @@ -181,7 +215,7 @@ mod tests { let pg3_ref = PauliString::from_text("YII"); let pg4_ref = PauliString::from_text("ZII"); - let angles_ref = vec![0.3, 0.7, 0.12]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref, pg4_ref], @@ -201,7 +235,11 @@ mod tests { #[test] #[should_panic] fn test_pauli_polynomial_constructor_unequal_strings() { - let ham = vec![("IXYZ", 0.3), ("XXI", 0.7), ("YYII", 0.12)]; + let ham = vec![ + ("IXYZ", Angle::from_angle(0.3)), + ("XXI", Angle::from_angle(0.7)), + ("YYII", Angle::from_angle(0.12)), + ]; let _ = PauliPolynomial::from_hamiltonian(ham); } @@ -210,7 +248,7 @@ mod tests { let pg1_ref = PauliString::from_text("IXY"); let pg2_ref = PauliString::from_text("ZYX"); let pg3_ref = PauliString::from_text("YIX"); - let angles_ref = vec![0.3, 0.7, 0.12]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12]); PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -236,7 +274,7 @@ mod tests { let pg2_ref = PauliString::from_text("ZXY"); // YIX let pg3_ref = PauliString::from_text("YIX"); - let angles_ref = vec![0.3, -0.7, -0.12]; + let angles_ref = Angle::from_angles(&[0.3, -0.7, -0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -261,7 +299,7 @@ mod tests { let pg2_ref = PauliString::from_text("YZX"); // YIX -> ZIX let pg3_ref = PauliString::from_text("ZIX"); - let angles_ref = vec![-0.3, 0.7, 0.12]; + let angles_ref = Angle::from_angles(&[-0.3, 0.7, 0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -286,7 +324,7 @@ mod tests { let pg2_ref = PauliString::from_text("ZXY"); // YIX -> XI(-Y) let pg3_ref = PauliString::from_text("XIY"); - let angles_ref = vec![0.3, 0.7, 0.12]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -311,7 +349,7 @@ mod tests { let pg2_ref = PauliString::from_text("YZX"); // YIX -> (-Z)IX let pg3_ref = PauliString::from_text("ZIX"); - let angles_ref = vec![-0.3, -0.7, 0.12]; + let angles_ref = Angle::from_angles(&[-0.3, -0.7, 0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -336,7 +374,7 @@ mod tests { let pg2_ref = PauliString::from_text("XYZ"); // YIX - let pg3_ref = PauliString::from_text("YIX"); - let angles_ref = vec![0.3, -0.7, -0.12]; + let angles_ref = Angle::from_angles(&[0.3, -0.7, -0.12]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -358,7 +396,7 @@ mod tests { let pg2_ref = PauliString::from_text("IXYZ"); let pg3_ref = PauliString::from_text("YIXZ"); - let angles_ref = vec![0.3, 0.7, 0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, 0.15]); PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], @@ -385,7 +423,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, 1, 1] - let angles_ref = vec![0.3, 0.7, 0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -411,7 +449,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, 1, -1] - let angles_ref = vec![0.3, 0.7, 0.12, -0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, -0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -437,7 +475,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, -1, 1] - let angles_ref = vec![0.3, 0.7, -0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, -0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -463,7 +501,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, 1, 1] - let angles_ref = vec![0.3, 0.7, 0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -490,7 +528,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, 1, 1] - let angles_ref = vec![0.3, 0.7, 0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -516,7 +554,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, 1, -1, 1] - let angles_ref = vec![0.3, 0.7, -0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, 0.7, -0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -542,7 +580,7 @@ mod tests { // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); // [1, -1, 1, 1] - let angles_ref = vec![0.3, -0.7, 0.12, 0.15]; + let angles_ref = Angle::from_angles(&[0.3, -0.7, 0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -567,8 +605,8 @@ mod tests { let pg2_ref = PauliString::from_text("IXYZ"); // YIXZ let pg3_ref = PauliString::from_text("YIXZ"); - // [1, 1, -1, 1] - let angles_ref = vec![0.3, 0.7, 0.12, 0.15]; + // [1, 1, 1, 1] + let angles_ref = Angle::from_angles(&[0.3, 0.7, 0.12, 0.15]); let pp_ref = PauliPolynomial { chains: vec![pg1_ref, pg2_ref, pg3_ref], angles: angles_ref, @@ -576,4 +614,57 @@ mod tests { }; assert_eq!(pp, pp_ref); } + + #[test] + fn test_commutes_with_simple() { + let pp1s = vec![ + vec![("I", Angle::from_angle(0.3))], + vec![("X", Angle::from_angle(0.5))], + vec![("Y", Angle::from_angle(0.7))], + vec![("Z", Angle::from_angle(0.9))], + ] + .into_iter() + .map(|ham| PauliPolynomial::from_hamiltonian(ham)) + .collect::>(); + + let pp2s = pp1s.clone(); + + for (i, (pp1, pp2)) in pp1s.iter().cartesian_product(pp2s.iter()).enumerate() { + if i <= 5 || i == 8 || i == 10 || i == 12 || i == 15 { + assert!(pp1.commutes_with(pp2)); + } else { + assert!(!pp1.commutes_with(pp2)); + } + } + } + + #[test] + fn test_commutes_with() { + let pp1 = PauliPolynomial::from_hamiltonian(vec![ + ("IYYX", Angle::from_angle(0.3)), + ("XXXI", Angle::from_angle(0.5)), + ]); + + let pp2 = PauliPolynomial::from_hamiltonian(vec![ + ("IYZZ", Angle::from_angle(0.7)), + ("ZZXI", Angle::from_angle(0.9)), + ]); + + assert!(pp1.commutes_with(&pp2)); + } + + #[test] + fn test_not_commutes_with() { + let pp1 = PauliPolynomial::from_hamiltonian(vec![ + ("IYYX", Angle::from_angle(0.3)), + ("XXXI", Angle::from_angle(0.5)), + ]); + + let pp2 = PauliPolynomial::from_hamiltonian(vec![ + ("IYZZ", Angle::from_angle(0.7)), + ("ZZXY", Angle::from_angle(0.9)), + ]); + + assert!(!pp1.commutes_with(&pp2)); + } } diff --git a/synir/src/data_structures/pauli_polynomial/simplify.rs b/synir/src/data_structures/pauli_polynomial/simplify.rs new file mode 100644 index 00000000..65e009e5 --- /dev/null +++ b/synir/src/data_structures/pauli_polynomial/simplify.rs @@ -0,0 +1,173 @@ +use crate::data_structures::PauliPolynomial; +use itertools::Itertools; +use std::collections::HashMap; + +/// Check for items in PauliPolynomial that has the same Pauli string +/// Returns a vector of a tuple of Pauli string identifier and the locations of this repeated string +pub(crate) fn check_repeats(pp: &PauliPolynomial) -> Vec<(usize, Vec)> { + let size = pp.size(); + let length = pp.length(); + let mut repeats = HashMap::>::new(); + for index in 0..length { + let mut num = 0; + for letter in 0..size { + num += (pp.chain(letter).x(index) as usize) << 2 * letter; + num += (pp.chain(letter).z(index) as usize) << 2 * letter + 1; + } + repeats + .entry(num) + .and_modify(|e: &mut Vec| e.push(index)) + .or_insert(vec![index]); + } + repeats + .into_iter() + .filter(|(_, v)| v.len() > 1) + .sorted() + .collect_vec() +} + +/// Takes output of check_repeats and merges items with the same Pauli string +/// Assumes that all angles are of the same type +pub(crate) fn _merge_repeats( + mut pp: PauliPolynomial, + merge_list: Vec<(usize, Vec)>, +) -> PauliPolynomial { + let mut pp_merge_list = Vec::::new(); + // merge all the angles first + for (_, angle_merge_list) in merge_list { + let merge_index = angle_merge_list[0]; + let mut angle = pp.angle(merge_index); + for angle_index in angle_merge_list.iter().skip(1) { + angle += pp.angle(*angle_index); + } + pp.angles[merge_index] = angle; + pp_merge_list.extend_from_slice(&angle_merge_list[1..]); + } + // remove duplicate entries + pp_merge_list.sort_by(|a, b| b.cmp(a)); + for remove_index in pp_merge_list { + pp.angles.remove(remove_index); + for chain_index in 0..pp.chains().len() { + pp.chains[chain_index].x.remove(remove_index); + pp.chains[chain_index].z.remove(remove_index); + } + } + pp +} + +/// Merges all items in PauliPolynomial that share the same Pauli string +pub fn merge_repeats(mut pp: PauliPolynomial) -> PauliPolynomial { + let repeats = check_repeats(&pp); + _merge_repeats(pp, repeats) +} + +#[cfg(test)] +mod tests { + use crate::data_structures::Angle; + use crate::data_structures::PauliString; + + use super::*; + + #[test] + fn test_simple_check_repeats() { + // Combined reading from back -> 01 = 1 + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("I", Angle::from_angle(1.0)), + ("X", Angle::from_angle(2.0)), + ("Z", Angle::from_angle(3.0)), + ("X", Angle::from_angle(4.0)), + ("X", Angle::from_angle(5.0)), + ]); + let repeats = check_repeats(&pp); + assert!(repeats.len() == 1); + assert_eq!(repeats, vec![(1, vec![1, 3, 4])]); + } + + #[test] + fn test_check_repeats() { + // XIZY appears twice + // Z string -> 0011 + // X string -> 1001 + // Combined reading from back -> 11 10 00 01 = 225 + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("XIZY", Angle::from_angle(1.0)), + ("XIZY", Angle::from_angle(2.0)), + ("YZZI", Angle::from_angle(3.0)), + ]); + let repeats = check_repeats(&pp); + assert!(repeats.len() == 1); + assert_eq!(repeats, vec![(225, vec![0, 1])]); + } + + #[test] + fn test_multiple_repeats() { + // Combined reading from back -> 01 = 1 + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("II", Angle::from_angle(1.0)), + ("IX", Angle::from_angle(2.0)), + ("ZZ", Angle::from_angle(3.0)), + ("IX", Angle::from_angle(4.0)), + ("ZZ", Angle::from_angle(5.0)), + ]); + let repeats = check_repeats(&pp); + assert!(repeats.len() == 2); + assert_eq!(repeats, vec![(4, vec![1, 3]), (10, vec![2, 4])]); + } + + #[test] + fn test_simple_merge_repeats() { + // Combined reading from back -> 01 = 1 + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("I", Angle::from_angle(1.0)), + ("X", Angle::from_angle(2.0)), + ("Z", Angle::from_angle(3.0)), + ("X", Angle::from_angle(4.0)), + ("X", Angle::from_angle(5.0)), + ]); + let repeats = check_repeats(&pp); + + let pp = _merge_repeats(pp, repeats); + + assert!(pp.chain(0).len() == 3); + assert_eq!(pp.chain(0), &PauliString::from_text("IXZ")); + assert_eq!(pp.angles, Angle::from_angles(&[1.0, 11.0, 3.0])); + } + + #[test] + fn test_merge_repeats() { + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("XIZY", Angle::from_angle(1.0)), + ("XIZY", Angle::from_angle(2.0)), + ("YZZI", Angle::from_angle(3.0)), + ]); + let repeats = check_repeats(&pp); + let pp = _merge_repeats(pp, repeats); + + assert!(pp.chain(0).len() == 2); + assert_eq!(pp.chain(0), &PauliString::from_text("XY")); + assert_eq!(pp.chain(1), &PauliString::from_text("IZ")); + assert_eq!(pp.chain(2), &PauliString::from_text("ZZ")); + assert_eq!(pp.chain(3), &PauliString::from_text("YI")); + assert_eq!(pp.angles, Angle::from_angles(&[3.0, 3.0])); + } + + #[test] + fn test_multiple_merge_repeats() { + // Combined reading from back -> 01 = 1 + let pp = PauliPolynomial::from_hamiltonian(vec![ + ("II", Angle::from_angle(1.0)), + ("IX", Angle::from_angle(2.0)), + ("ZZ", Angle::from_angle(3.0)), + ("IX", Angle::from_angle(4.0)), + ("ZZ", Angle::from_angle(5.0)), + ]); + + let repeats = check_repeats(&pp); + let pp = _merge_repeats(pp, repeats); + + assert!(pp.chain(0).len() == 3); + assert_eq!(pp.chain(0), &PauliString::from_text("IIZ")); + assert_eq!(pp.chain(1), &PauliString::from_text("IXZ")); + assert_eq!(pp.angles, Angle::from_angles(&[1.0, 6.0, 8.0])); + } +} diff --git a/synir/src/data_structures/pauli_string.rs b/synir/src/data_structures/pauli_string.rs index d66a3f5a..590b4df4 100644 --- a/synir/src/data_structures/pauli_string.rs +++ b/synir/src/data_structures/pauli_string.rs @@ -123,6 +123,21 @@ impl PauliString { mask &= &self.z; mask } + + pub(crate) fn commutes_with(&self, other: &PauliString) -> bool { + assert_eq!(self.len(), other.len()); + let length = self.len(); + let mut commutes = true; + for index in 0..length { + let p1 = self.pauli(index); + let p2 = other.pauli(index); + if p1 == PauliLetter::I || p2 == PauliLetter::I || p1 == p2 { + continue; + } + commutes = !commutes; + } + commutes + } } pub(crate) fn cx(control: &mut PauliString, target: &mut PauliString) { @@ -164,6 +179,7 @@ mod tests { use super::*; use bitvec::prelude::Lsb0; use bitvec::{bits, bitvec}; + use itertools::Itertools; #[test] fn test_from_basis_int() { @@ -300,4 +316,49 @@ mod tests { let pauli_string = PauliString::from_text("IXYZI"); assert_eq!(pauli_string.to_string(), String::from("I X Y Z I")); } + + #[test] + fn test_check_commute_simple() { + let ps1 = vec![ + PauliString::from_text("I"), + PauliString::from_text("X"), + PauliString::from_text("Y"), + PauliString::from_text("Z"), + ]; + + let ps2 = vec![ + PauliString::from_text("I"), + PauliString::from_text("X"), + PauliString::from_text("Y"), + PauliString::from_text("Z"), + ]; + + for (p1, p2) in ps1.iter().cartesian_product(ps2.iter()) { + if p1 == &PauliString::from_text("I") || p2 == &PauliString::from_text("I") || p1 == p2 + { + assert!(p1.commutes_with(p2)); + continue; + } + assert!(!p1.commutes_with(p2)); + } + } + + #[test] + #[should_panic] + fn test_bad_commute() { + let ps1 = PauliString::from_text("IXXYZ"); + let ps2 = PauliString::from_text("IYZX"); + ps1.commutes_with(&ps2); + } + + #[test] + fn test_check_commute() { + let ps1 = PauliString::from_text("IXYZ"); + let ps2 = PauliString::from_text("IYZX"); + assert!(!ps1.commutes_with(&ps2)); + + let ps3 = PauliString::from_text("IXYZ"); + let ps4 = PauliString::from_text("IZXI"); + assert!(ps3.commutes_with(&ps4)); + } } diff --git a/synir/src/ir/pauli_polynomial/helper.rs b/synir/src/ir/pauli_polynomial/helper.rs index 127f5ad1..536a747b 100644 --- a/synir/src/ir/pauli_polynomial/helper.rs +++ b/synir/src/ir/pauli_polynomial/helper.rs @@ -108,7 +108,7 @@ pub(super) fn push_down_pauli_polynomial_update( } } let last_qubit = *affected_qubits.last().unwrap(); - repr.rz(last_qubit, pauli_polynomial.angle(col)); + repr.rz(last_qubit, pauli_polynomial.angle(col).to_radians()); mask.replace(col, false); } } diff --git a/synir/tests/ct_synthesis/ct_compose.rs b/synir/tests/ct_synthesis/ct_compose.rs new file mode 100644 index 00000000..0d08c3ea --- /dev/null +++ b/synir/tests/ct_synthesis/ct_compose.rs @@ -0,0 +1,193 @@ +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::{Angle, 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()); + } + } + }; +} + +fn compose_x_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("XI"), + Angle::from_pi4_rotation(2), + )); + ct +} + +fn compose_z_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("ZI"), + Angle::from_pi4_rotation(4), + )); + ct +} + +fn compose_y_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("YI"), + Angle::from_pi4_rotation(6), + )); + ct +} + +test_clifford!(compose_x_gadget, Some(&vec![MockCommand::V(0),])); +test_clifford!(compose_z_gadget, Some(&vec![MockCommand::Z(0),])); + +test_clifford!( + compose_y_gadget, + Some(&vec![MockCommand::H(0), MockCommand::Z(0)]) +); + +fn compose_xx_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("XX"), + Angle::from_pi4_rotation(2), + )); + ct +} + +fn compose_zz_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("ZZ"), + Angle::from_pi4_rotation(2), + )); + ct +} + +fn compose_yy_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(2); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("YY"), + Angle::from_pi4_rotation(2), + )); + ct +} + +test_clifford!( + compose_xx_gadget, + Some(&vec![ + MockCommand::V(0), + MockCommand::H(1), + MockCommand::CX(1, 0), + MockCommand::H(1), + MockCommand::V(1) + ]) +); + +test_clifford!( + compose_zz_gadget, + Some(&vec![ + MockCommand::S(0), + MockCommand::H(1), + MockCommand::CX(0, 1), + MockCommand::S(1), + MockCommand::V(1), + MockCommand::Z(1) + ]) +); + +test_clifford!( + compose_yy_gadget, + Some(&vec![ + MockCommand::H(0), + MockCommand::S(1), + MockCommand::CX(0, 1), + MockCommand::H(1), + MockCommand::CX(1, 0), + MockCommand::S(1), + MockCommand::V(1), + MockCommand::X(1), + MockCommand::Z(0), + MockCommand::Z(1) + ]) +); + +fn compose_complex_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(3); + ct.compose_gadget(( + synir::data_structures::PauliString::from_text("XYZ"), + Angle::from_pi4_rotation(2), + )); + ct +} + +fn manual_compose_complex_gadget() -> CliffordTableau { + let mut ct = CliffordTableau::new(3); + ct.h(0); + ct.v(1); + ct.cx(0, 1); + ct.cx(1, 2); + ct.s(2); + ct.cx(1, 2); + ct.cx(0, 1); + ct.h(0); + ct.v_dgr(1); + ct +} + +test_clifford!( + compose_complex_gadget, + Some(&vec![ + MockCommand::V(0), + MockCommand::V(1), + MockCommand::CX(1, 0), + MockCommand::CX(2, 0), + MockCommand::S(1), + MockCommand::H(2), + MockCommand::CX(1, 2), + MockCommand::V(1), + MockCommand::S(2), + MockCommand::V(2), + MockCommand::X(1), + MockCommand::Z(2) + ]) +); + +#[test] +fn test_correctness() { + let ct1 = compose_complex_gadget(); + let ct2 = manual_compose_complex_gadget(); + assert_eq!(ct1, ct2); +} diff --git a/synir/tests/ct_synthesis/mod.rs b/synir/tests/ct_synthesis/mod.rs index 2fff57d6..3d2bebc6 100644 --- a/synir/tests/ct_synthesis/mod.rs +++ b/synir/tests/ct_synthesis/mod.rs @@ -1,3 +1,4 @@ +pub mod ct_compose; pub mod custom_callback; pub mod naive; pub mod naive_adjoint; diff --git a/synir/tests/pauli_exponential.rs b/synir/tests/pauli_exponential.rs index 324aa30d..c19afa71 100644 --- a/synir/tests/pauli_exponential.rs +++ b/synir/tests/pauli_exponential.rs @@ -3,6 +3,7 @@ mod common; use std::collections::VecDeque; use common::mock_circuit::{parse_clifford_commands, MockCircuit, MockCommand}; +use synir::data_structures::Angle; use synir::data_structures::{CliffordTableau, HasAdjoint, PauliPolynomial}; use synir::ir::clifford_tableau::{CliffordTableauSynthStrategy, NaiveCliffordSynthesizer}; use synir::ir::pauli_exponential::PauliExponential; @@ -11,7 +12,7 @@ use synir::ir::pauli_polynomial::PauliPolynomialSynthStrategy; use synir::ir::Synthesizer; fn setup_simple_pe() -> PauliExponential { - let ham = vec![("IZZZ", 0.3)]; + let ham = vec![("IZZZ", Angle::from_angle(0.3))]; let pauli_polynomial = PauliPolynomial::from_hamiltonian(ham); let clifford_tableau = CliffordTableau::new(4); @@ -19,7 +20,11 @@ fn setup_simple_pe() -> PauliExponential { } fn setup_complex_pe() -> PauliExponential { - let ham = vec![("IXYZ", 0.3), ("XXII", 0.7), ("YYII", 0.12)]; + let ham = vec![ + ("IXYZ", Angle::from_angle(0.3)), + ("XXII", Angle::from_angle(0.7)), + ("YYII", Angle::from_angle(0.12)), + ]; let pauli_polynomial = PauliPolynomial::from_hamiltonian(ham); let clifford_tableau = CliffordTableau::new(4); diff --git a/synir/tests/pauli_polynomial.rs b/synir/tests/pauli_polynomial.rs index f4560e9b..7d1d8103 100644 --- a/synir/tests/pauli_polynomial.rs +++ b/synir/tests/pauli_polynomial.rs @@ -3,13 +3,14 @@ mod common; use std::collections::VecDeque; use common::mock_circuit::{parse_clifford_commands, MockCircuit, MockCommand}; +use synir::data_structures::Angle; use synir::data_structures::{CliffordTableau, PauliPolynomial}; use synir::ir::pauli_polynomial::NaivePauliPolynomialSynthesizer; use synir::ir::Synthesizer; fn setup_complex_pp() -> VecDeque { - let ham_1 = vec![("IZZZ", 0.3)]; - let ham_2 = vec![("XXII", 0.7)]; + let ham_1 = vec![("IZZZ", Angle::from_angle(0.3))]; + let ham_2 = vec![("XXII", Angle::from_angle(0.7))]; let pp_1 = PauliPolynomial::from_hamiltonian(ham_1); let pp_2 = PauliPolynomial::from_hamiltonian(ham_2); @@ -17,7 +18,7 @@ fn setup_complex_pp() -> VecDeque { } fn setup_simple_pp() -> VecDeque { - let ham = vec![("IXYZ", 0.3)]; + let ham = vec![("IXYZ", Angle::from_angle(0.3))]; let pauli_polynomial = PauliPolynomial::from_hamiltonian(ham); diff --git a/synpy/integration_tests/test_qiskit.py b/synpy/integration_tests/test_qiskit.py index 3df06263..d34bb66b 100644 --- a/synpy/integration_tests/test_qiskit.py +++ b/synpy/integration_tests/test_qiskit.py @@ -12,6 +12,21 @@ def test_qiskit_bell() -> None: plugin = SynPyCliffordPlugin() circ = plugin.run(cliff, None, None, []) - # circ.draw() + + assert circ == qc + +def test_qiskit_circuit() -> None: + qc = QuantumCircuit(3) + qc.h(0) + qc.cx(0, 1) + qc.t(1) + qc.cx(1, 2) + qc.h(2) + qc.cz(0, 2) + + cliff = Clifford(qc) + + plugin = SynPyCliffordPlugin() + circ = plugin.run(cliff, None, None, []) assert circ == qc diff --git a/synpy/src/synthesis.rs b/synpy/src/synthesis.rs index 6e905add..b7bb2cfa 100644 --- a/synpy/src/synthesis.rs +++ b/synpy/src/synthesis.rs @@ -1,8 +1,10 @@ use pyo3::exceptions::PyException; use pyo3::{pyclass, pymethods, PyErr}; +use synir::data_structures::Angle; use std::ops::Deref; use pyo3::{pyfunction, PyRef, PyResult}; +use synir::data_structures::Angle; use synir::data_structures::PropagateClifford; use synir::data_structures::{CliffordTableau, PauliPolynomial}; use synir::ir::clifford_tableau::CliffordTableauSynthStrategy; @@ -210,8 +212,14 @@ pub fn synthesize_pauli_exponential( ) -> PyResult> { let converted_hamiltonian = hamiltonian .iter() - .map(|inner| inner.iter().map(PyPauliString::as_tuple).collect()) - .map(|inner: Vec<(&str, f64)>| PauliPolynomial::from_hamiltonian(inner)) + .map(|inner| { + inner + .iter() + .map(PyPauliString::as_tuple) + .map(|(pauli_str, phase)| (pauli_str, Angle::from_angle(phase))) + .collect::>() + }) + .map(|inner: Vec<(&str, Angle)>| PauliPolynomial::from_hamiltonian(inner)) .collect(); let clifford_gates: Vec = clifford_gates.iter().map(|cmd| *(cmd.deref())).collect(); diff --git a/synpy/src/wrapper/qiskit.rs b/synpy/src/wrapper/qiskit.rs new file mode 100644 index 00000000..997d20cf --- /dev/null +++ b/synpy/src/wrapper/qiskit.rs @@ -0,0 +1,188 @@ +extern crate pyo3; +extern crate pyo3_ffi; + +use pyo3::prelude::*; +use synir::ir::{CliffordGates, Gates}; + +#[pyclass] +pub struct QiskitSynIR { + circuit: Py, +} + +#[pymethods] +impl QiskitSynIR { + #[new] + pub fn new(qiskit_circuit: Py) -> Self { + QiskitSynIR { + circuit: qiskit_circuit, + } + } + + pub fn get_commands(&self, tgt: usize) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "h", (tgt,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn s(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "s", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn v(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "sx", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn s_dgr(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "sdg", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn v_dgr(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "sxdg", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn x(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "x", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn y(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "y", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn z(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "z", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn h(&mut self, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "h", (target,))?; + Ok(()) + }) + .unwrap(); + } + + pub fn cx(&mut self, control: synir::IndexType, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "cx", (control, target))?; + Ok(()) + }) + .unwrap(); + } + + pub fn cz(&mut self, control: synir::IndexType, target: synir::IndexType) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "cz", (control, target))?; + Ok(()) + }) + .unwrap(); + } + + pub fn rx(&mut self, target: synir::IndexType, angle: f64) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "rx", (angle, target))?; + Ok(()) + }) + .unwrap(); + } + + pub fn ry(&mut self, target: synir::IndexType, angle: f64) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "ry", (angle, target))?; + Ok(()) + }) + .unwrap(); + } + + pub fn rz(&mut self, target: synir::IndexType, angle: f64) { + Python::attach(|py| -> PyResult<()> { + self.circuit.call_method1(py, "rz", (angle, target))?; + Ok(()) + }) + .unwrap(); + } +} + +impl CliffordGates for QiskitSynIR { + fn s(&mut self, target: synir::IndexType) { + self.s(target); + } + + fn v(&mut self, target: synir::IndexType) { + self.v(target); + } + + fn s_dgr(&mut self, target: synir::IndexType) { + self.s_dgr(target); + } + + fn v_dgr(&mut self, target: synir::IndexType) { + self.v_dgr(target); + } + + fn x(&mut self, target: synir::IndexType) { + self.x(target); + } + + fn y(&mut self, target: synir::IndexType) { + self.y(target); + } + + fn z(&mut self, target: synir::IndexType) { + self.z(target); + } + + fn h(&mut self, target: synir::IndexType) { + self.h(target); + } + + fn cx(&mut self, control: synir::IndexType, target: synir::IndexType) { + self.cx(control, target); + } + + fn cz(&mut self, control: synir::IndexType, target: synir::IndexType) { + self.cz(control, target); + } +} + +impl Gates for QiskitSynIR { + fn rx(&mut self, target: synir::IndexType, angle: f64) { + self.rx(target, angle); + } + + fn ry(&mut self, target: synir::IndexType, angle: f64) { + self.ry(target, angle); + } + + fn rz(&mut self, target: synir::IndexType, angle: f64) { + self.rz(target, angle); + } +}