1use crate::sum::PauliSum;
5use ppvm_traits::char::Pauli;
6use ppvm_traits::config::Config;
7use ppvm_traits::traits::*;
8
9pub(crate) fn rotate_1_map_insert_closure<T: Config>(
10 k: &T::PauliWordType,
11 v: &mut T::Coeff,
12 axis: Pauli,
13 addr0: usize,
14 sin: &T::Coeff,
15 cos: &T::Coeff,
16) -> Option<(T::PauliWordType, T::Coeff)> {
17 if axis == Pauli::L {
18 panic!("Rotation axis cannot be L");
19 }
20 if k.get_lbit(addr0) {
22 return None;
23 }
24 let xbit = k.get_xbit(addr0);
25 let zbit = k.get_zbit(addr0);
26 let p_g = (zbit as u8) << 1 | (xbit as u8);
28 let (eps, p_q) = levi_civita(p_g, axis as u8);
29 if eps == 0 {
30 None
31 } else {
32 let mut coeff = v.clone();
33 *v *= cos.clone();
34 let mut new_word = k.clone();
35 new_word.set_xbit(addr0, p_q & 0b01 != 0);
36 new_word.set_zbit(addr0, p_q & 0b10 != 0);
37 new_word.rehash();
38
39 coeff *= sin.mul_sign(eps);
40 Some((new_word, coeff))
41 }
42}
43
44impl<T: Config> RotationOne<T> for PauliSum<T>
45where
46 T::Coeff: std::ops::MulAssign,
47 T::Map: ACMapInsert<T::Storage, T::Coeff, T::BuildHasher, T::PauliWordType> + ACMapConsume,
48{
49 fn rotate_1(&mut self, axis: Pauli, addr0: usize, theta: <T as Config>::Coeff) {
50 let (sin, cos) = theta.sin_cos();
51 self.map_insert(|k, v| rotate_1_map_insert_closure::<T>(k, v, axis, addr0, &sin, &cos));
52 }
53
54 #[inline]
55 fn rx(&mut self, addr0: usize, theta: impl Into<T::Coeff>) {
56 let (sin, cos) = theta.into().sin_cos();
59 self.map_insert(|k, v| {
60 if k.get_lbit(addr0) {
61 return None;
62 }
63 let zbit = k.get_zbit(addr0);
64 if !zbit {
65 return None;
66 }
67 let xbit = k.get_xbit(addr0);
68 let mut coeff = v.clone();
69 *v *= cos.clone();
70 let mut new_word = k.clone();
71 new_word.set_xbit(addr0, !xbit);
72 new_word.rehash();
73 let eps: i8 = if xbit { -1 } else { 1 };
74 coeff *= sin.mul_sign(eps);
75 Some((new_word, coeff))
76 });
77 }
78
79 #[inline]
80 fn ry(&mut self, addr0: usize, theta: impl Into<T::Coeff>) {
81 let (sin, cos) = theta.into().sin_cos();
84 self.map_insert(|k, v| {
85 if k.get_lbit(addr0) {
86 return None;
87 }
88 let xbit = k.get_xbit(addr0);
89 let zbit = k.get_zbit(addr0);
90 if xbit == zbit {
91 return None;
92 }
93 let mut coeff = v.clone();
94 *v *= cos.clone();
95 let mut new_word = k.clone();
96 new_word.set_xbit(addr0, !xbit);
97 new_word.set_zbit(addr0, !zbit);
98 new_word.rehash();
99 let eps: i8 = if zbit { -1 } else { 1 };
100 coeff *= sin.mul_sign(eps);
101 Some((new_word, coeff))
102 });
103 }
104
105 #[inline]
106 fn rz(&mut self, addr0: usize, theta: impl Into<T::Coeff>) {
107 let (sin, cos) = theta.into().sin_cos();
110 self.map_insert(|k, v| {
111 if k.get_lbit(addr0) {
112 return None;
113 }
114 let xbit = k.get_xbit(addr0);
115 if !xbit {
116 return None;
117 }
118 let zbit = k.get_zbit(addr0);
119 let mut coeff = v.clone();
120 *v *= cos.clone();
121 let mut new_word = k.clone();
122 new_word.set_zbit(addr0, !zbit);
123 new_word.rehash();
124 let eps: i8 = if zbit { 1 } else { -1 };
125 coeff *= sin.mul_sign(eps);
126 Some((new_word, coeff))
127 });
128 }
129}
130
131impl<T: Config> RotXY<T> for PauliSum<T>
132where
133 PauliSum<T>: RotationOne<T>,
134{
135 fn r(&mut self, addr0: usize, axis_angle: <T as Config>::Coeff, theta: <T as Config>::Coeff) {
136 self.rz(addr0, axis_angle.clone());
140 self.rx(addr0, theta);
141 self.rz(addr0, -axis_angle);
142 }
143}
144
145#[inline]
149pub fn levi_civita(i: u8, j: u8) -> (i8, u8) {
150 let k = i ^ j; let commute = ((i == 0) | (j == 0) | (k == 0)) as u8; #[inline]
158 fn rank(p: u8) -> u8 {
159 let b1 = p >> 1; (b1 << 1).wrapping_sub(b1 & (p & 1)) }
162
163 let ri = rank(i);
164 let rj = rank(j);
165
166 let mut diff = rj.wrapping_sub(ri).wrapping_add(3); diff -= 3 & (0u8.wrapping_sub(diff >> 2)); let eps_raw = 1i8 - 2 * ((diff >> 1) as i8);
172
173 let eps = eps_raw * (1 - commute as i8); let k = k * (1 - commute); (eps, k)
178}
179
180#[cfg(test)]
181mod tests {
182 use crate::config::fxhash::ByteF64;
183
184 use super::*;
185
186 #[test]
187 fn test_rx() {
188 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
189 answer += ("X", 1.0);
190 answer.rx(0, 2.1);
191 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
192 expect += ("X", 1.0);
193 assert_eq!(answer, expect);
194
195 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
196 answer += ("Y", 1.0);
197 answer.rx(0, 2.1);
198 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
199 expect += ("Y", 2.1_f64.cos());
200 expect += ("Z", -2.1_f64.sin());
201 assert_eq!(answer, expect);
202
203 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
204 answer += ("Z", 1.0);
205 answer.rx(0, 2.1);
206 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
207 expect += ("Z", 2.1_f64.cos());
208 expect += ("Y", 2.1_f64.sin());
209 assert_eq!(answer, expect);
210
211 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
212 answer += ("I", 1.0);
213 answer.rx(0, 2.1);
214 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
215 expect += ("I", 1.0);
216 assert_eq!(answer, expect);
217 }
218
219 #[test]
220 fn test_ry() {
221 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
222 answer += ("X", 1.0);
223 answer.ry(0, 2.1);
224
225 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
226 expect += ("X", 2.1_f64.cos());
227 expect += ("Z", 2.1_f64.sin());
228 assert_eq!(answer, expect);
229
230 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
231 answer += ("Y", 1.0);
232 answer.ry(0, 2.1);
233 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
234 expect += ("Y", 1.0);
235 assert_eq!(answer, expect);
236
237 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
238 answer += ("Z", 1.0);
239 answer.ry(0, 2.1);
240 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
241 expect += ("Z", 2.1_f64.cos());
242 expect += ("X", -2.1_f64.sin());
243 assert_eq!(answer, expect);
244
245 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
246 answer += ("I", 1.0);
247 answer.ry(0, 2.1);
248 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
249 expect += ("I", 1.0);
250 assert_eq!(answer, expect);
251 }
252
253 #[test]
254 fn test_r() {
255 use std::f64::consts::FRAC_PI_2;
256 let theta = 2.1;
257
258 let mut via_r: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
260 via_r += ("Z", 1.0);
261 via_r.r(0, 0.0, theta);
262 let mut via_rx: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
263 via_rx += ("Z", 1.0);
264 via_rx.rx(0, theta);
265 assert!((via_r.overlap(&via_rx) - 1.0).abs() < 1e-9);
266
267 let mut via_r: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
271 via_r += ("Z", 1.0);
272 via_r.r(0, FRAC_PI_2, theta);
273 let mut via_ry: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
274 via_ry += ("Z", 1.0);
275 via_ry.ry(0, theta);
276 assert!((via_r.overlap(&via_ry) - 1.0).abs() < 1e-9);
277 }
278
279 #[test]
280 fn test_rz() {
281 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
282 answer += ("X", 1.0);
283 answer.rz(0, 2.1);
284 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
285 expect += ("X", 2.1_f64.cos());
286 expect += ("Y", -2.1_f64.sin());
287 assert_eq!(answer, expect);
288
289 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
290 answer += ("Y", 1.0);
291 answer.rz(0, 2.1);
292 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
293 expect += ("Y", 2.1_f64.cos());
294 expect += ("X", 2.1_f64.sin());
295 assert_eq!(answer, expect);
296
297 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
298 answer += ("Z", 1.0);
299 answer.rz(0, 2.1);
300 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
301 expect += ("Z", 1.0);
302 assert_eq!(answer, expect);
303
304 let mut answer: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
305 answer += ("I", 1.0);
306 answer.rz(0, 2.1);
307 let mut expect: PauliSum<ByteF64<2>> = PauliSum::builder().n_qubits(1).build();
308 expect += ("I", 1.0);
309 assert_eq!(answer, expect);
310 }
311}