Repository: qir-alliance/qir-runner (language: python)

Issue:
Fix accumulated phase for rotations by 2 Pi
This fix was missing from #163, which only addressed the special case for rotations by Pi and missing the corresponding special case for rotations by 2 Pi. This makes sure the phase of -1 is present for Rx/Ry by 2 Pi, and adds tests to confirm them.


Discussion / hints:

Fix patch:
diff --git a/sparsesim/src/lib.rs b/sparsesim/src/lib.rs
index c6421d8c..c2b8ec3b 100644
--- a/sparsesim/src/lib.rs
+++ b/sparsesim/src/lib.rs
@@ -974,29 +974,40 @@ impl QuantumSim {
             } else {
                 self.mcx(ctls, target);
             }
-            // Rx/Ry are different from X/Y by a global phase of -i, so apply that here for mathematical correctness.
+            // Rx/Ry are different from X/Y by a global phase of -i, so apply that here when indicated by m01,
+            // for mathematical correctness.
             let (_, ctls) = self.resolve_and_check_qubits(target, ctls);
-            self.state =
-                self.state
-                    .drain()
-                    .fold(SparseState::default(), |mut accum, (index, value)| {
-                        if ctls.iter().all(|c| index.bit(*c)) {
-                            accum.insert(index, value * -Complex64::i());
-                        }
-                        accum
-                    });
+            let factor = m01
+                * if sign_flip {
+                    Complex64::i()
+                } else {
+                    Complex64::one()
+                };
+            if factor != Complex64::one() {
+                self.state =
+                    self.state
+                        .drain()
+                        .fold(SparseState::default(), |mut accum, (index, value)| {
+                            if ctls.iter().all(|c| index.bit(*c)) {
+                                accum.insert(index, value * factor);
+                            }
+                            accum
+                        });
+            }
         } else if m01.is_nearly_zero() {
-            // This is just identity, so we can effectively no-op and just add a phase of -1.
-            let (_, ctls) = self.resolve_and_check_qubits(target, ctls);
-            self.state =
-                self.state
-                    .drain()
-                    .fold(SparseState::default(), |mut accum, (index, value)| {
-                        if ctls.iter().all(|c| index.bit(*c)) {
-                            accum.insert(index, value * -Complex64::one());
-                        }
-                        accum
-                    });
+            // This is just identity, so we can effectively no-op, and just add a phase of -1 as indicated by m00.
+            if m00 == -Complex64::one() {
+                let (_, ctls) = self.resolve_and_check_qubits(target, ctls);
+                self.state =
+                    self.state
+                        .drain()
+                        .fold(SparseState::default(), |mut accum, (index, value)| {
+                            if ctls.iter().all(|c| index.bit(*c)) {
+                                accum.insert(index, value * -Complex64::one());
+                            }
+                            accum
+                        });
+            }
         } else {
             let (target, ctls) = self.resolve_and_check_qubits(target, ctls);
             let mut new_state = SparseState::default();


Test patch:
diff --git a/sparsesim/src/matrix_testing.rs b/sparsesim/src/matrix_testing.rs
index 2828817f..626ab731 100644
--- a/sparsesim/src/matrix_testing.rs
+++ b/sparsesim/src/matrix_testing.rs
@@ -603,6 +603,45 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_rx_zero() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.rx(0.0, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&rx(0.0)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
+    #[test]
+    fn test_rx_3pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.rx(3.0 * PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&rx(3.0 * PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
+    #[test]
+    fn test_rx_4pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.rx(4.0 * PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&rx(4.0 * PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
     #[test]
     fn test_ry() {
         assert_operation_equal_referenced(
@@ -642,6 +681,45 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_ry_zero() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.ry(0.0, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&ry(0.0)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
+    #[test]
+    fn test_ry_3pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.ry(3.0 * PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&ry(3.0 * PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
+    #[test]
+    fn test_ry_4pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.ry(4.0 * PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&ry(4.0 * PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
     #[test]
     fn test_mcri() {
         assert_operation_equal_referenced(