diff --git a/crates/transpiler/src/passes/commutation_cancellation.rs b/crates/transpiler/src/passes/commutation_cancellation.rs index 99a4b3b10ba8..fd7673e02a73 100644 --- a/crates/transpiler/src/passes/commutation_cancellation.rs +++ b/crates/transpiler/src/passes/commutation_cancellation.rs @@ -338,7 +338,7 @@ pub fn cancel_commutations( } else if sx_supported && is_multiple_of_pi(total_angle, 0.5) { let num_sx = (total_angle / FRAC_PI_2).round(); total_phase -= FRAC_PI_4 * num_sx; - for _ in 0..(num_sx as i64) % 4 { + for _ in 0..(num_sx as i64).rem_euclid(4) { dag.insert_1q_on_incoming_qubit((StandardGate::SX, &[]), cancel_set[0]); } } else { diff --git a/releasenotes/notes/fix-commutative-cancellation-sxdg-12c52787870c18fb.yaml b/releasenotes/notes/fix-commutative-cancellation-sxdg-12c52787870c18fb.yaml new file mode 100644 index 000000000000..66cc4c0429b2 --- /dev/null +++ b/releasenotes/notes/fix-commutative-cancellation-sxdg-12c52787870c18fb.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed a miscompilation in :class:`.CommutativeCancellation` when a run of single-qubit :math:`X` + rotations coalesced to a negative angle and :math:`\sqrt X` was available as a target gate. See + `#16594 `__. diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 8750d352a95d..fd6fcd8e518e 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -981,6 +981,19 @@ def test_determinism(self): # The actual asseertion. self.assertTrue(left.structurally_equal(right)) + @ddt.data(2, 3, 4) + def test_negative_x_rotations(self, num_sxdg): + qc = QuantumCircuit(1) + for _ in range(num_sxdg): + qc.sxdg(0) + + expected = qc.copy_empty_like() + for _ in range(4 - num_sxdg): + expected.sx(0) + + pass_ = CommutativeCancellation(["sx", "rz"]) + self.assertEqual(pass_(qc), expected) + if __name__ == "__main__": unittest.main()