qiskit/test/python/transpiler/test_optimize_swap_before_m...

384 lines
16 KiB
Python

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Test OptimizeSwapBeforeMeasure pass"""
import unittest
from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister
from qiskit.passmanager.flow_controllers import DoWhileController
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure, DAGFixedPoint
from qiskit.converters import circuit_to_dag
from test import QiskitTestCase # pylint: disable=wrong-import-order
class TestOptimizeSwapBeforeMeasure(QiskitTestCase):
"""Test swap-followed-by-measure optimizations."""
def test_optimize_1swap_1measure(self):
"""Remove a single swap
qr0:--X--m-- qr0:----
| |
qr1:--X--|-- ==> qr1:--m-
| |
cr0:-----.-- cr0:--.-
"""
qr = QuantumRegister(2, "qr")
cr = ClassicalRegister(1, "cr")
circuit = QuantumCircuit(qr, cr)
circuit.swap(qr[0], qr[1])
circuit.measure(qr[0], cr[0])
dag = circuit_to_dag(circuit)
expected = QuantumCircuit(qr, cr)
expected.measure(qr[1], cr[0])
pass_ = OptimizeSwapBeforeMeasure()
after = pass_.run(dag)
self.assertEqual(circuit_to_dag(expected), after)
def test_optimize_1swap_2measure(self):
"""Remove a single swap affecting two measurements
qr0:--X--m-- qr0:--m----
| | |
qr1:--X--|--m ==> qr1:--|--m-
| | | |
cr0:-----.--|-- cr0:--|--.-
cr1:--------.-- cr1:--.----
"""
qr = QuantumRegister(2, "qr")
cr = ClassicalRegister(2, "cr")
circuit = QuantumCircuit(qr, cr)
circuit.swap(qr[0], qr[1])
circuit.measure(qr[0], cr[0])
circuit.measure(qr[1], cr[1])
dag = circuit_to_dag(circuit)
expected = QuantumCircuit(qr, cr)
expected.measure(qr[1], cr[0])
expected.measure(qr[0], cr[1])
pass_ = OptimizeSwapBeforeMeasure()
after = pass_.run(dag)
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
┌─┐ ┌─┐
q_0: ─X──X─────X────┤M├───────────────────────────────── q_0: ──────┤M├───────────────
│ │ │ └╥┘ ┌─┐ ┌─┐└╥┘
q_1: ─X──X──X──X──X──╫─────X────┤M├───────────────────── q_1: ───┤M├─╫────────────────
│ │ ║ │ └╥┘ ┌─┐ ┌─┐└╥┘ ║
q_2: ───────X──X──X──╫──X──X─────╫──X────┤M├──────────── q_2: ┤M├─╫──╫────────────────
│ ║ │ ║ │ └╥┘┌─┐ └╥┘ ║ ║ ┌─┐
q_3: ─X─────X──X─────╫──X──X──X──╫──X─────╫─┤M├───────── q_3: ─╫──╫──╫────┤M├─────────
│ │ ║ │ │ ║ ║ └╥┘┌─┐ ║ ║ ║ └╥┘ ┌─┐
q_4: ─X──X──X──X─────╫──X──X──X──╫──X─────╫──╫─┤M├────── ==> q_4: ─╫──╫──╫─────╫───────┤M├
│ │ ║ │ ║ │ ║ ║ └╥┘┌─┐ ║ ║ ║ ┌─┐ ║ └╥┘
q_5: ────X──X──X──X──╫──X──X─────╫──X──X──╫──╫──╫─┤M├─── q_5: ─╫──╫──╫─┤M├─╫────────╫─
│ │ ║ │ ║ │ ║ ║ ║ └╥┘┌─┐ ║ ║ ║ └╥┘ ║ ┌─┐ ║
q_6: ─X──X──X──X──X──╫──X──X─────╫─────X──╫──╫──╫──╫─┤M├ q_6: ─╫──╫──╫──╫──╫─┤M├────╫─
│ │ │ ║ │ ┌─┐ ║ ║ ║ ║ ║ └╥┘ ║ ║ ║ ║ ║ └╥┘┌─┐ ║
q_7: ─X──X─────X─────╫──X─┤M├────╫────────╫──╫──╫──╫──╫─ q_7: ─╫──╫──╫──╫──╫──╫─┤M├─╫─
║ └╥┘ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ └╥┘ ║
c: 8/════════════════╩═════╩═════╩════════╩══╩══╩══╩══╩═ c: 8/═╩══╩══╩══╩══╩══╩══╩══╩═
0 7 1 2 3 4 5 6 0 1 2 3 4 5 6 7
"""
circuit = QuantumCircuit(8, 8)
circuit.swap(3, 4)
circuit.swap(6, 7)
circuit.swap(0, 1)
circuit.swap(6, 7)
circuit.swap(4, 5)
circuit.swap(0, 1)
circuit.swap(5, 6)
circuit.swap(3, 4)
circuit.swap(1, 2)
circuit.swap(6, 7)
circuit.swap(4, 5)
circuit.swap(2, 3)
circuit.swap(0, 1)
circuit.swap(5, 6)
circuit.swap(1, 2)
circuit.swap(6, 7)
circuit.swap(4, 5)
circuit.swap(2, 3)
circuit.swap(3, 4)
circuit.swap(3, 4)
circuit.swap(5, 6)
circuit.swap(1, 2)
circuit.swap(4, 5)
circuit.swap(2, 3)
circuit.swap(5, 6)
circuit.measure(range(8), range(8))
dag = circuit_to_dag(circuit)
expected = QuantumCircuit(8, 8)
expected.measure(0, 2)
expected.measure(1, 1)
expected.measure(2, 0)
expected.measure(3, 4)
expected.measure(4, 7)
expected.measure(5, 3)
expected.measure(6, 5)
expected.measure(7, 6)
pass_ = OptimizeSwapBeforeMeasure()
after = pass_.run(dag)
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--
| |
qr1:--X-[H]-|--
|
cr0:--------.--
"""
qr = QuantumRegister(2, "qr")
cr = ClassicalRegister(1, "cr")
circuit = QuantumCircuit(qr, cr)
circuit.swap(qr[0], qr[1])
circuit.h(qr[1])
circuit.measure(qr[0], cr[0])
dag = circuit_to_dag(circuit)
pass_ = OptimizeSwapBeforeMeasure()
after = pass_.run(dag)
self.assertEqual(circuit_to_dag(circuit), after)
def test_if_else(self):
"""Test that the pass recurses into a simple if-else."""
pass_ = OptimizeSwapBeforeMeasure()
base_test = QuantumCircuit(2, 1)
base_test.swap(0, 1)
base_test.measure(0, 0)
base_expected = QuantumCircuit(2, 1)
base_expected.measure(1, 0)
test = QuantumCircuit(2, 1)
test.if_else(
(test.clbits[0], True), base_test.copy(), base_test.copy(), test.qubits, test.clbits
)
expected = QuantumCircuit(2, 1)
expected.if_else(
(expected.clbits[0], True),
base_expected.copy(),
base_expected.copy(),
expected.qubits,
expected.clbits,
)
self.assertEqual(pass_(test), expected)
def test_nested_control_flow(self):
"""Test that the pass recurses into nested control flow."""
pass_ = OptimizeSwapBeforeMeasure()
base_test = QuantumCircuit(2, 1)
base_test.swap(0, 1)
base_test.measure(0, 0)
base_expected = QuantumCircuit(2, 1)
base_expected.measure(1, 0)
body_test = QuantumCircuit(2, 1)
body_test.for_loop((0,), None, base_expected.copy(), body_test.qubits, body_test.clbits)
body_expected = QuantumCircuit(2, 1)
body_expected.for_loop(
(0,), None, base_expected.copy(), body_expected.qubits, body_expected.clbits
)
test = QuantumCircuit(2, 1)
test.while_loop((test.clbits[0], True), body_test, test.qubits, test.clbits)
expected = QuantumCircuit(2, 1)
expected.while_loop(
(expected.clbits[0], True), body_expected, expected.qubits, expected.clbits
)
self.assertEqual(pass_(test), expected)
class TestOptimizeSwapBeforeMeasureFixedPoint(QiskitTestCase):
"""Test swap-followed-by-measure optimizations in a transpiler, using fixed point."""
def test_optimize_undone_swap(self):
"""Remove redundant swap
qr0:--X--X--m-- qr0:--m---
| | | |
qr1:--X--X--|-- ==> qr1:--|--
| |
cr0:--------.-- cr0:--.--
"""
qr = QuantumRegister(2, "qr")
cr = ClassicalRegister(1, "cr")
circuit = QuantumCircuit(qr, cr)
circuit.swap(qr[0], qr[1])
circuit.swap(qr[0], qr[1])
circuit.measure(qr[0], cr[0])
expected = QuantumCircuit(qr, cr)
expected.measure(qr[0], cr[0])
pass_manager = PassManager()
pass_manager.append(
DoWhileController(
[OptimizeSwapBeforeMeasure(), DAGFixedPoint()],
do_while=lambda property_set: not property_set["dag_fixed_point"],
)
)
after = pass_manager.run(circuit)
self.assertEqual(expected, after)
def test_optimize_overlap_swap(self):
"""Remove two swaps that overlap
qr0:--X-------- qr0:--m--
| |
qr1:--X--X----- qr1:--|--
| ==> |
qr2:-----X--m-- qr2:--|--
| |
cr0:--------.-- cr0:--.--
"""
qr = QuantumRegister(3, "qr")
cr = ClassicalRegister(1, "cr")
circuit = QuantumCircuit(qr, cr)
circuit.swap(qr[0], qr[1])
circuit.swap(qr[1], qr[2])
circuit.measure(qr[2], cr[0])
expected = QuantumCircuit(qr, cr)
expected.measure(qr[0], cr[0])
pass_manager = PassManager()
pass_manager.append(
DoWhileController(
[OptimizeSwapBeforeMeasure(), DAGFixedPoint()],
do_while=lambda property_set: not property_set["dag_fixed_point"],
)
)
after = pass_manager.run(circuit)
self.assertEqual(expected, after)
def test_no_optimize_swap_with_condition(self):
"""Do not remove swap if it has a condition
qr0:--X--m-- qr0:--X--m--
| | | |
qr1:--X--|-- ==> qr1:--X--|--
| | | |
cr0:--1--.-- cr0:--1--.--
"""
qr = QuantumRegister(2, "qr")
cr = ClassicalRegister(1, "cr")
circuit = QuantumCircuit(qr, cr)
with self.assertWarns(DeprecationWarning):
circuit.swap(qr[0], qr[1]).c_if(cr, 1)
circuit.measure(qr[0], cr[0])
dag = circuit_to_dag(circuit)
expected = QuantumCircuit(qr, cr)
with self.assertWarns(DeprecationWarning):
expected.swap(qr[0], qr[1]).c_if(cr, 1)
expected.measure(qr[0], cr[0])
pass_ = OptimizeSwapBeforeMeasure()
after = pass_.run(dag)
self.assertEqual(circuit_to_dag(expected), after)
if __name__ == "__main__":
unittest.main()