Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed bug in OptimizeSwapBeforeMeasure #11413

Merged
merged 2 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
71 changes: 71 additions & 0 deletions test/python/transpiler/test_optimize_swap_before_measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
┌─┐ ┌─┐
Expand Down Expand Up @@ -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--
Expand Down