From 8428277c55abccfd01c9de832a1533e87ca5f07e Mon Sep 17 00:00:00 2001 From: Seemanta Bhattacharjee Date: Thu, 14 Dec 2023 19:15:56 +0600 Subject: [PATCH] Fixed bug in OptimizeSwapBeforeMeasure (#11413) * fixed bug in OptimizeSwapBeforeMeasure #11195 * fix lint issue (cherry picked from commit 3551c7ca9d870eee34f83fd10a3e181560e5a232) --- .../optimize_swap_before_measure.py | 4 +- ...-swap-before-measure-67e8896da2215d49.yaml | 44 ++++++++++++ .../test_optimize_swap_before_measure.py | 71 +++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/fix-optimize-swap-before-measure-67e8896da2215d49.yaml diff --git a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py index 54ebe4f41d0f..6682e7ebbdba 100644 --- a/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py +++ b/qiskit/transpiler/passes/optimization/optimize_swap_before_measure.py @@ -43,7 +43,7 @@ def run(self, dag): if getattr(swap.op, "condition", None) is not None: continue final_successor = [] - for successor in dag.successors(swap): + for successor in dag.descendants(swap): final_successor.append( isinstance(successor, DAGOutNode) or (isinstance(successor, DAGOpNode) and isinstance(successor.op, Measure)) @@ -56,7 +56,7 @@ def run(self, dag): measure_layer.add_qreg(qreg) for creg in dag.cregs.values(): measure_layer.add_creg(creg) - for successor in list(dag.successors(swap)): + for successor in list(dag.descendants(swap)): if isinstance(successor, DAGOpNode) and isinstance(successor.op, Measure): # replace measure node with a new one, where qargs is set with the "other" # swap qarg. diff --git a/releasenotes/notes/fix-optimize-swap-before-measure-67e8896da2215d49.yaml b/releasenotes/notes/fix-optimize-swap-before-measure-67e8896da2215d49.yaml new file mode 100644 index 000000000000..9c743062c6d3 --- /dev/null +++ b/releasenotes/notes/fix-optimize-swap-before-measure-67e8896da2215d49.yaml @@ -0,0 +1,44 @@ +--- +fixes: + - | + Fixed an issue with the :class:`~.OptimizeSwapBeforeMeasure` pass where + it would incorrectly optimize circuits involving swap and measure + instructions. This commit fixes the bug by changing `DAGCircuit.successors` + to `DAGCircuit.descendants`. Also, added a couple of extra tests to ensure + that the bug is fixed. For example:: + + from qiskit import QuantumCircuit + from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure + pass_ = OptimizeSwapBeforeMeasure() + qc = QuantumCircuit(2, 1) + qc.swap(0, 1) + qc.measure(0, 0) + qc.measure(0, 0) + print(qc.draw()) + print(pass_(qc).draw()) + + would previously print:: + + ┌─┐┌─┐ + q_0: ─X─┤M├┤M├ + │ └╥┘└╥┘ + q_1: ─X──╫──╫─ + ║ ║ + c: 1/════╩══╩═ + 0 0 + ┌─┐ + q_0: ┤M├─── + └╥┘┌─┐ + q_1: ─╫─┤M├ + ║ └╥┘ + c: 1/═╩══╩═ + 0 0 + + and now the second ciruit is correctly optimized to:: + + q_0: ────── + ┌─┐┌─┐ + q_1: ┤M├┤M├ + └╥┘└╥┘ + c: 1/═╩══╩═ + 0 0 \ No newline at end of file diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 0fe4f3937795..75b0079c6650 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -73,6 +73,38 @@ def test_optimize_1swap_2measure(self): self.assertEqual(circuit_to_dag(expected), after) + def test_optimize_3swap_3measure(self): + """Remove three swaps affecting three measurements + ┌─┐ ┌─┐ ┌─┐ + q_0: ─X─┤M├────X──X─┤M├ q_0: ───┤M├─── + │ └╥┘┌─┐ │ │ └╥┘ ┌─┐└╥┘┌─┐ + q_1: ─X──╫─┤M├─┼──┼──╫─ q_1: ┤M├─╫─┤M├ + ║ └╥┘ │ │ ║ ==> └╥┘ ║ └╥┘ + q_2: ────╫──╫──X──X──╫─ q_2: ─╫──╫──╫─ + ║ ║ ║ ║ ║ ║ + c: 2/════╩══╩════════╩═ c: 2/═╩══╩══╩═ + 0 1 0 0 1 0 + """ + + qc = QuantumCircuit(3, 2) + qc.swap(0, 1) + qc.measure(0, 0) + qc.swap(0, 2) + qc.measure(1, 1) + qc.swap(0, 2) + qc.measure(0, 0) + dag = circuit_to_dag(qc) + + expected = QuantumCircuit(3, 2) + expected.measure(1, 0) + expected.measure(0, 1) + expected.measure(1, 0) + + pass_ = OptimizeSwapBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + def test_optimize_nswap_nmeasure(self): """Remove severals swap affecting multiple measurements ┌─┐ ┌─┐ @@ -139,6 +171,45 @@ def test_optimize_nswap_nmeasure(self): self.assertEqual(circuit_to_dag(expected), after) + def test_optimize_nswap_nmeasure_2(self): + """Remove multiple swaps affecting multiple measurements + ┌─┐┌─┐ ┌─┐┌─┐┌─┐┌─┐┌─┐ + q_0: ─X──X─┤M├┤M├─X──────────────X─ q_0: ┤M├┤M├┤M├┤M├┤M├ + │ │ └╥┘└╥┘ │ ┌─┐ │ └╥┘└╥┘└╥┘└╥┘└╥┘ + q_1: ─X──X──╫──╫──┼─────X─┤M├─X──X─ q_1: ─╫──╫──╫──╫──╫─ + ║ ║ │ ┌─┐ │ └╥┘ │ ┌─┐ ==> ║ ║ ║ ║ ║ + q_2: ───────╫──╫──X─┤M├─X──╫──X─┤M├ q_2: ─╫──╫──╫──╫──╫─ + ║ ║ └╥┘ ║ └╥┘ ║ ║ ║ ║ ║ + c: 1/═══════╩══╩═════╩═════╩═════╩═ c: 1/═╩══╩══╩══╩══╩═ + 0 0 0 0 0 0 0 0 0 0 + """ + + qc = QuantumCircuit(3, 1) + qc.swap(0, 1) + qc.swap(0, 1) + qc.measure(0, 0) + qc.measure(0, 0) + qc.swap(2, 0) + qc.measure(2, 0) + qc.swap(1, 2) + qc.measure(1, 0) + qc.swap(1, 2) + qc.swap(0, 1) + qc.measure(2, 0) + dag = circuit_to_dag(qc) + + expected = QuantumCircuit(3, 1) + expected.measure(0, 0) + expected.measure(0, 0) + expected.measure(0, 0) + expected.measure(0, 0) + expected.measure(0, 0) + + pass_ = OptimizeSwapBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + def test_cannot_optimize(self): """Cannot optimize when swap is not at the end in all of the successors qr0:--X-----m--