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

Issue:
Rx/Ry use shortcuts to X/Y for performance that lose expected global phase
Mathematically, Rx(π) = -iX and Ry(π) = -iY. However, the internals of the sparse simulator detect when the rotation is effectively just a Pauli rotation and call X or Y directly, meaning that Rx(π) = X without the phase of -i. While this doesn't effect measurement probability (the Born rule), it can be confusing for experimentation on the simulator since it means the gates don't behave the way the matrix math would indicate. To better match the math, the short-cut should induce the proper global phase.


Discussion / hints:

Fix patch:
diff --git a/sparsesim/src/lib.rs b/sparsesim/src/lib.rs
index 6deee3f9..233cf9d1 100644
--- a/sparsesim/src/lib.rs
+++ b/sparsesim/src/lib.rs
@@ -955,6 +955,17 @@ 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.
+            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
+                    });
         } else if m01.is_nearly_zero() {
             // This is just identity, so we can no-op.
         } else {


Test patch:
diff --git a/sparsesim/src/matrix_testing.rs b/sparsesim/src/matrix_testing.rs
index 28450605..e92b5aa0 100644
--- a/sparsesim/src/matrix_testing.rs
+++ b/sparsesim/src/matrix_testing.rs
@@ -417,6 +417,8 @@ mod tests {
 
         // Sparse state vector should have one entry for |0⟩.
         assert_eq!(sim.state.len(), 1);
+        // If the operations are equal including the phase, the entry should be 1.
+        assert!((sim.state.values().next().unwrap() - Complex64::one()).is_nearly_zero());
     }
 
     #[test]
@@ -549,6 +551,19 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_rz_pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.rz(PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&rz(PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
     #[test]
     fn test_rx() {
         assert_operation_equal_referenced(
@@ -562,6 +577,19 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_rx_pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.rx(PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&rx(PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
     #[test]
     fn test_ry() {
         assert_operation_equal_referenced(
@@ -575,6 +603,19 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_ry_pi() {
+        assert_operation_equal_referenced(
+            |sim, qs| {
+                sim.ry(PI, qs[0]);
+            },
+            |sim, qs| {
+                sim.apply(&adjoint(&ry(PI)), &[qs[0]], None);
+            },
+            1,
+        );
+    }
+
     #[test]
     fn test_mcri() {
         assert_operation_equal_referenced(