Skip to content

Commit

Permalink
Fix InverseCancellation to run in classical blocks (#13454)
Browse files Browse the repository at this point in the history
* Fix an issue that `InverseCancellation` does not run in classical blocks (#13437)

* Add test functions for  pass running in classical blocks.

* Reformat test function file

* Add a release note

(cherry picked from commit 6025b7c)
  • Loading branch information
haimeng-zhang authored and mtreinish committed Nov 27, 2024
1 parent e86d9b6 commit 4349397
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/optimization/inverse_cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes.utils import control_flow

from qiskit._accelerate.inverse_cancellation import inverse_cancellation

Expand Down Expand Up @@ -74,6 +75,7 @@ def __init__(self, gates_to_cancel: List[Union[Gate, Tuple[Gate, Gate]]]):

super().__init__()

@control_flow.trivial_recurse
def run(self, dag: DAGCircuit):
"""Run the InverseCancellation pass on `dag`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fixes:
- |
The transpilation pass :class`.InverseCancellation` now runs inside of flow controlled blocks. Previously, it ignores the pairs of gates in classical blocks that can be cancelled. Refer to `#13437 <https://github.com/Qiskit/qiskit/issues/13437>` for more details.
54 changes: 54 additions & 0 deletions test/python/transpiler/test_inverse_cancellation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes import InverseCancellation
from qiskit.transpiler import PassManager
from qiskit.circuit import Clbit, Qubit
from qiskit.circuit.library import (
RXGate,
HGate,
Expand Down Expand Up @@ -398,6 +399,59 @@ def test_backwards_pair(self):
new_circ = inverse_pass(qc)
self.assertEqual(new_circ, QuantumCircuit(1))

def test_if_else(self):
"""Test that the pass recurses in a simple if-else."""
pass_ = InverseCancellation([CXGate()])

inner_test = QuantumCircuit(4, 1)
inner_test.cx(0, 1)
inner_test.cx(0, 1)
inner_test.cx(2, 3)

inner_expected = QuantumCircuit(4, 1)
inner_expected.cx(2, 3)

test = QuantumCircuit(4, 1)
test.h(0)
test.measure(0, 0)
test.if_else((0, True), inner_test.copy(), inner_test.copy(), range(4), [0])

expected = QuantumCircuit(4, 1)
expected.h(0)
expected.measure(0, 0)
expected.if_else((0, True), inner_expected, inner_expected, range(4), [0])

self.assertEqual(pass_(test), expected)

def test_nested_control_flow(self):
"""Test that collection recurses into nested control flow."""
pass_ = InverseCancellation([CXGate()])
qubits = [Qubit() for _ in [None] * 4]
clbit = Clbit()

inner_test = QuantumCircuit(qubits, [clbit])
inner_test.cx(0, 1)
inner_test.cx(0, 1)
inner_test.cx(2, 3)

inner_expected = QuantumCircuit(qubits, [clbit])
inner_expected.cx(2, 3)

true_body = QuantumCircuit(qubits, [clbit])
true_body.while_loop((clbit, True), inner_test.copy(), [0, 1, 2, 3], [0])

test = QuantumCircuit(qubits, [clbit])
test.for_loop(range(2), None, inner_test.copy(), [0, 1, 2, 3], [0])
test.if_else((clbit, True), true_body, None, [0, 1, 2, 3], [0])

expected_if_body = QuantumCircuit(qubits, [clbit])
expected_if_body.while_loop((clbit, True), inner_expected, [0, 1, 2, 3], [0])
expected = QuantumCircuit(qubits, [clbit])
expected.for_loop(range(2), None, inner_expected, [0, 1, 2, 3], [0])
expected.if_else((clbit, True), expected_if_body, None, [0, 1, 2, 3], [0])

self.assertEqual(pass_(test), expected)


if __name__ == "__main__":
unittest.main()

0 comments on commit 4349397

Please sign in to comment.