1use bitvec::view::BitView;
5use num::PrimInt;
6use num::{
7 Complex, One, Zero,
8 complex::{Complex64, ComplexFloat},
9};
10
11use crate::prelude::*;
12
13impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> RotationOne<T>
14 for GeneralizedTableau<T, I, C>
15where
16 <<T as Config>::Storage as BitView>::Store: PrimInt,
17 T::Coeff: Zero + One + Send + Sync + num::Num + PartialOrd,
18 I: TableauIndex + Send + Sync,
19 Complex<T::Coeff>: std::ops::Mul<Output = Complex<T::Coeff>>
20 + std::ops::AddAssign
21 + From<Complex64>
22 + ComplexFloat
23 + Copy,
24{
25 fn rotate_1(&mut self, axis: Pauli, addr0: usize, theta: <T as Config>::Coeff) {
26 if self.is_inactive(addr0) {
27 return;
28 }
29 let (sin, cos) = (theta * 0.5.into()).sin_cos();
30
31 let complex_cos: Complex<T::Coeff> = Complex {
32 re: cos,
33 im: T::Coeff::zero(),
34 };
35
36 let i_complex_sin: Complex<T::Coeff> = Complex {
37 re: T::Coeff::zero(),
38 im: -sin,
39 };
40
41 self.branch_with_coefficients(addr0, axis, complex_cos, i_complex_sin);
42 }
43}
44
45impl<T: Config, I, C: SparseVector<Complex<T::Coeff>, I>> RotXY<T> for GeneralizedTableau<T, I, C>
46where
47 GeneralizedTableau<T, I, C>: RotationOne<T>,
48{
49 fn r(&mut self, addr0: usize, axis_angle: T::Coeff, theta: T::Coeff) {
50 self.rz(addr0, -axis_angle.clone());
54 self.rx(addr0, theta);
55 self.rz(addr0, axis_angle);
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use ppvm_pauli_sum::config::fxhash::ByteF64;
63 use std::f64::consts::{FRAC_PI_2, PI};
64
65 type TestConfig = ByteF64<1>;
66 type TestTableau = GeneralizedTableau<TestConfig>;
67
68 #[test]
70 fn test_rx_pi_flips_zero_to_one() {
71 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
72 tab.rx(0, PI);
73
74 assert_eq!(tab.coefficients.len(), 1);
76
77 let result = tab.measure(0);
78 assert!(result.unwrap(), "Expected to measure 1, got {:?}", result);
79 }
80
81 #[test]
82 fn test_ry_pi_flips_zero_to_one() {
83 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
84 tab.ry(0, PI);
85 assert_eq!(tab.coefficients.len(), 1);
87 let result = tab.measure(0);
88 assert!(result.unwrap(), "Expected to measure 1, got {:?}", result);
89 }
90
91 #[test]
92 fn test_rz_leaves_zero_invariant() {
93 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
94 tab.rz(0, 0.123);
95 assert_eq!(tab.coefficients.len(), 1);
97 let result = tab.measure(0);
98 assert!(!result.unwrap(), "Expected to measure 0, got {:?}", result);
99 }
100
101 #[test]
102 fn test_rx_pi_2_statistics() {
103 let mut tab: TestTableau = GeneralizedTableau::new_with_seed(1, 1e-12, 0);
104 tab.rx(0, FRAC_PI_2);
105 assert_eq!(tab.coefficients.len(), 2);
107
108 let trials = 100;
109 let mut count_one = 0;
110 for i in 0..trials {
111 let mut tmp_tab = tab.fork(Some(i));
112 let result = tmp_tab.measure(0);
113 count_one += result.unwrap() as u8;
114 }
115 println!("{}", count_one);
116 assert!(35 < count_one && count_one < 65);
117 }
118
119 #[test]
120 fn test_ry_round_trip() {
121 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-10);
122 tab.ry(0, 2.0 * PI);
123
124 assert_eq!(tab.coefficients.len(), 1);
125
126 assert!(!tab.measure(0).unwrap());
127 }
128
129 #[test]
131 fn test_r_identity() {
132 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
133 tab.r(0, 0.0, 0.0);
134 assert_eq!(tab.coefficients.len(), 1);
135 assert!(!tab.measure(0).unwrap());
136 }
137
138 #[test]
140 fn test_r_axis_zero_is_rx() {
141 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
142 tab.r(0, 0.0, PI);
143 assert_eq!(tab.coefficients.len(), 1);
144 assert!(tab.measure(0).unwrap());
145 }
146
147 #[test]
149 fn test_r_axis_half_pi_is_ry() {
150 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
151 tab.r(0, FRAC_PI_2, PI);
152 assert_eq!(tab.coefficients.len(), 1);
153 assert!(tab.measure(0).unwrap());
154 }
155
156 #[test]
158 fn test_r_branches() {
159 let mut tab: TestTableau = GeneralizedTableau::new(1, 1e-12);
160 tab.r(0, 0.37 * PI, FRAC_PI_2);
161 assert_eq!(tab.coefficients.len(), 2);
162 }
163
164 #[test]
167 fn test_r_matches_rz_rx_rz() {
168 let (axis_angle, theta) = (0.21 * PI, 0.34 * PI);
169
170 let mut tab_r: TestTableau = GeneralizedTableau::new_with_seed(1, 1e-12, 0);
171 tab_r.r(0, axis_angle, theta);
172
173 let mut tab_manual: TestTableau = GeneralizedTableau::new_with_seed(1, 1e-12, 0);
174 tab_manual.rz(0, -axis_angle);
175 tab_manual.rx(0, theta);
176 tab_manual.rz(0, axis_angle);
177
178 for seed in 0..200 {
179 let result_r = tab_r.fork(Some(seed)).measure(0).unwrap();
180 let result_manual = tab_manual.fork(Some(seed)).measure(0).unwrap();
181 assert_eq!(result_r, result_manual, "mismatch at seed {}", seed);
182 }
183 }
184
185 #[test]
186 fn test_two_qubit_case() {
187 let mut tab: TestTableau = GeneralizedTableau::new(2, 1e-10);
188 tab.rx(0, PI);
189
190 assert_eq!(tab.coefficients.len(), 1);
191
192 assert!(!tab.measure(1).unwrap());
193 assert!(tab.measure(0).unwrap());
194
195 tab.rx(1, PI);
196
197 assert_eq!(tab.coefficients.len(), 1);
198
199 assert!(tab.measure(1).unwrap());
200 assert!(tab.measure(0).unwrap());
201
202 tab.rx(0, PI);
203
204 assert_eq!(tab.coefficients.len(), 1);
205
206 assert!(tab.measure(1).unwrap());
207 assert!(!tab.measure(0).unwrap());
208
209 tab.rx(1, PI);
210
211 assert_eq!(tab.coefficients.len(), 1);
212
213 assert!(!tab.measure(1).unwrap());
214 assert!(!tab.measure(0).unwrap());
215 }
216}