qiskit/test/python/transpiler/test_barrier_before_final_m...

431 lines
16 KiB
Python

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
#
# 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 the BarrierBeforeFinalMeasurements pass"""
import random
import unittest
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.converters import circuit_to_dag
from qiskit.circuit import QuantumRegister, QuantumCircuit, ClassicalRegister, Clbit
from test import QiskitTestCase # pylint: disable=wrong-import-order
class TestBarrierBeforeFinalMeasurements(QiskitTestCase):
"""Tests the BarrierBeforeFinalMeasurements pass."""
def test_single_measure(self):
"""A single measurement at the end
|
q:--[m]-- q:--|-[m]---
| -> | |
c:---.--- c:-----.---
"""
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
circuit = QuantumCircuit(qr, cr)
circuit.measure(qr, cr)
expected = QuantumCircuit(qr, cr)
expected.barrier(qr)
expected.measure(qr, cr)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_ignore_single_measure(self):
"""Ignore single measurement because it is not at the end
q:--[m]-[H]- q:--[m]-[H]-
| -> |
c:---.------ c:---.------
"""
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
circuit = QuantumCircuit(qr, cr)
circuit.measure(qr, cr)
circuit.h(qr[0])
expected = QuantumCircuit(qr, cr)
expected.measure(qr, cr)
expected.h(qr[0])
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_single_measure_mix(self):
"""Two measurements, but only one is at the end
|
q0:--[m]--[H]--[m]-- q0:--[m]--[H]--|-[m]---
| | -> | | |
c:---.---------.--- c:---.-----------.---
"""
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
circuit = QuantumCircuit(qr, cr)
circuit.measure(qr, cr)
circuit.h(qr)
circuit.measure(qr, cr)
expected = QuantumCircuit(qr, cr)
expected.measure(qr, cr)
expected.h(qr)
expected.barrier(qr)
expected.measure(qr, cr)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_two_qregs(self):
"""Two measurements in different qregs to different cregs
|
q0:--[H]--[m]------ q0:--[H]--|--[m]------
| | |
q1:--------|--[m]-- -> q1:-------|---|--[m]--
| | | | |
c0:--------.---|--- c0:----------.---|---
| |
c1:------------.--- c0:--------------.---
"""
qr0 = QuantumRegister(1, "q0")
qr1 = QuantumRegister(1, "q1")
cr0 = ClassicalRegister(1, "c0")
cr1 = ClassicalRegister(1, "c1")
circuit = QuantumCircuit(qr0, qr1, cr0, cr1)
circuit.h(qr0)
circuit.measure(qr0, cr0)
circuit.measure(qr1, cr1)
expected = QuantumCircuit(qr0, qr1, cr0, cr1)
expected.h(qr0)
expected.barrier(qr0, qr1)
expected.measure(qr0, cr0)
expected.measure(qr1, cr1)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_two_qregs_to_a_single_creg(self):
"""Two measurements in different qregs to the same creg
|
q0:--[H]--[m]------ q0:--[H]--|--[m]------
| | |
q1:--------|--[m]-- -> q1:-------|---|--[m]--
| | | | |
c0:--------.---|--- c0:-----------.---|---
------------.--- ---------------.---
"""
qr0 = QuantumRegister(1, "q0")
qr1 = QuantumRegister(1, "q1")
cr0 = ClassicalRegister(2, "c0")
circuit = QuantumCircuit(qr0, qr1, cr0)
circuit.h(qr0)
circuit.measure(qr0, cr0[0])
circuit.measure(qr1, cr0[1])
expected = QuantumCircuit(qr0, qr1, cr0)
expected.h(qr0)
expected.barrier(qr0, qr1)
expected.measure(qr0, cr0[0])
expected.measure(qr1, cr0[1])
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_preserve_measure_for_conditional(self):
"""Test barrier is inserted after any measurements used for conditionals
q0:--[H]--[m]------------ q0:--[H]--[m]-------|-------
| | |
q1:--------|--[ z]--[m]-- -> q1:--------|--[ z]--|--[m]--
| | | | | |
c0:--------.--[=1]---|--- c0:--------.--[=1]------|---
| |
c1:------------------.--- c1:---------------------.---
"""
qr0 = QuantumRegister(1, "q0")
qr1 = QuantumRegister(1, "q1")
cr0 = ClassicalRegister(1, "c0")
cr1 = ClassicalRegister(1, "c1")
circuit = QuantumCircuit(qr0, qr1, cr0, cr1)
circuit.h(qr0)
circuit.measure(qr0, cr0)
with self.assertWarns(DeprecationWarning):
circuit.z(qr1).c_if(cr0, 1)
circuit.measure(qr1, cr1)
expected = QuantumCircuit(qr0, qr1, cr0, cr1)
expected.h(qr0)
expected.measure(qr0, cr0)
with self.assertWarns(DeprecationWarning):
expected.z(qr1).c_if(cr0, 1)
expected.barrier(qr0, qr1)
expected.measure(qr1, cr1)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
class TestBarrierBeforeMeasurementsWhenABarrierIsAlreadyThere(QiskitTestCase):
"""Tests the BarrierBeforeFinalMeasurements pass when there is a barrier already"""
def test_handle_redundancy(self):
"""The pass is idempotent
| |
q:--|-[m]-- q:--|-[m]---
| | -> | |
c:-----.--- c:-----.---
"""
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
circuit = QuantumCircuit(qr, cr)
circuit.barrier(qr)
circuit.measure(qr, cr)
expected = QuantumCircuit(qr, cr)
expected.barrier(qr)
expected.measure(qr, cr)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_preserve_barriers_for_measurement_ordering(self):
"""If the circuit has a barrier to enforce a measurement order,
preserve it in the output.
q:---[m]--|------- q:---|--[m]--|-------
----|---|--[m]-- -> ---|---|---|--[m]--
| | | |
c:----.-------|--- c:-------.-------|---
------------.--- ---------------.---
"""
qr = QuantumRegister(2, "q")
cr = ClassicalRegister(2, "c")
circuit = QuantumCircuit(qr, cr)
circuit.measure(qr[0], cr[0])
circuit.barrier(qr)
circuit.measure(qr[1], cr[1])
expected = QuantumCircuit(qr, cr)
expected.barrier(qr)
expected.measure(qr[0], cr[0])
expected.barrier(qr)
expected.measure(qr[1], cr[1])
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_measures_followed_by_barriers_should_be_final(self):
"""If a measurement is followed only by a barrier,
insert the barrier before it.
q:---[H]--|--[m]--|------- q:---[H]--|--[m]-|-------
---[H]--|---|---|--[m]-- -> ---[H]--|---|--|--[m]--
| | | |
c:------------.-------|--- c:------------.------|---
--------------------.--- -------------------.---
"""
qr = QuantumRegister(2, "q")
cr = ClassicalRegister(2, "c")
circuit = QuantumCircuit(qr, cr)
circuit.h(qr)
circuit.barrier(qr)
circuit.measure(qr[0], cr[0])
circuit.barrier(qr)
circuit.measure(qr[1], cr[1])
expected = QuantumCircuit(qr, cr)
expected.h(qr)
expected.barrier(qr)
expected.measure(qr[0], cr[0])
expected.barrier(qr)
expected.measure(qr[1], cr[1])
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_should_merge_with_smaller_duplicate_barrier(self):
"""If an equivalent barrier exists covering a subset of the qubits
covered by the new barrier, it should be replaced.
q:---|--[m]------------- q:---|--[m]-------------
---|---|---[m]-------- -> ---|---|---[m]--------
-------|----|---[m]--- ---|---|----|---[m]---
| | | | | |
c:-------.----|----|---- c:-------.----|----|----
------------.----|---- ------------.----|----
-----------------.---- -----------------.----
"""
qr = QuantumRegister(3, "q")
cr = ClassicalRegister(3, "c")
circuit = QuantumCircuit(qr, cr)
circuit.barrier(qr[0], qr[1])
circuit.measure(qr, cr)
expected = QuantumCircuit(qr, cr)
expected.barrier(qr)
expected.measure(qr, cr)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_should_merge_with_larger_duplicate_barrier(self):
"""If a barrier exists and is stronger than the barrier to be inserted,
preserve the existing barrier and do not insert a new barrier.
q:---|--[m]--|------- q:---|--[m]-|-------
---|---|---|--[m]-- -> ---|---|--|--[m]--
---|---|---|---|--- ---|---|--|---|---
| | | |
c:-------.-------|--- c:-------.------|---
---------------.--- --------------.---
------------------- ------------------
"""
qr = QuantumRegister(3, "q")
cr = ClassicalRegister(3, "c")
circuit = QuantumCircuit(qr, cr)
circuit.barrier(qr)
circuit.measure(qr[0], cr[0])
circuit.barrier(qr)
circuit.measure(qr[1], cr[1])
expected = circuit
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_barrier_doesnt_reorder_gates(self):
"""A barrier should not allow the reordering of gates, as pointed out in #2102
q:--[p(0)]----------[m]--------- q:--[p(0)]-----------|--[m]---------
--[p(1)]-----------|-[m]------ -> --[p(1)]-----------|---|-[m]------
--[p(2)]-|---------|--|-[m]---- --[p(2)]-|---------|---|--|-[m]----
---------|-[p(03)]-|--|--|-[m]- ---------|-[p(03)]-|---|--|--|-[m]-
| | | | | | | |
c:-------------------.--|--|--|- c:------------------------.--|--|--|-
----------------------.--|--|- ---------------------------.--|--|-
-------------------------.--|- ------------------------------.--|-
----------------------------.- ---------------------------------.-
"""
qr = QuantumRegister(4)
cr = ClassicalRegister(4)
circuit = QuantumCircuit(qr, cr)
circuit.p(0, qr[0])
circuit.p(1, qr[1])
circuit.p(2, qr[2])
circuit.barrier(qr[2], qr[3])
circuit.p(3, qr[3])
test_circuit = circuit.copy()
test_circuit.measure(qr, cr)
# expected circuit is the same, just with a barrier before the measurements
expected = circuit.copy()
expected.barrier(qr)
expected.measure(qr, cr)
pass_ = BarrierBeforeFinalMeasurements()
result = pass_.run(circuit_to_dag(test_circuit))
self.assertEqual(result, circuit_to_dag(expected))
def test_conditioned_on_single_bit(self):
"""Test that the pass can handle cases where there is a loose-bit condition."""
circuit = QuantumCircuit(QuantumRegister(3), ClassicalRegister(2), [Clbit()])
circuit.h(range(3))
circuit.measure(range(3), range(3))
with self.assertWarns(DeprecationWarning):
circuit.h(0).c_if(circuit.cregs[0], 3)
with self.assertWarns(DeprecationWarning):
circuit.h(1).c_if(circuit.clbits[-1], True)
with self.assertWarns(DeprecationWarning):
circuit.h(2).c_if(circuit.clbits[-1], False)
circuit.measure(range(3), range(3))
expected = circuit.copy_empty_like()
expected.h(range(3))
expected.measure(range(3), range(3))
with self.assertWarns(DeprecationWarning):
expected.h(0).c_if(expected.cregs[0], 3)
with self.assertWarns(DeprecationWarning):
expected.h(1).c_if(expected.clbits[-1], True)
with self.assertWarns(DeprecationWarning):
expected.h(2).c_if(expected.clbits[-1], False)
expected.barrier(range(3))
expected.measure(range(3), range(3))
pass_ = BarrierBeforeFinalMeasurements()
self.assertEqual(expected, pass_(circuit))
def test_output_deterministic(self):
"""Test that the output barriers have a deterministic ordering (independent of
PYTHONHASHSEED). This is important to guarantee that any subsequent topological iterations
through the circuit are also deterministic; it's in general not possible for all transpiler
passes to produce identical outputs across all valid topological orderings, especially if
those passes have some stochastic element."""
measure_order = list(range(20))
random.Random(2023_02_10).shuffle(measure_order)
circuit = QuantumCircuit(20, 20)
circuit.barrier([5, 2, 3])
circuit.barrier([7, 11, 14, 2, 4])
circuit.measure(measure_order, measure_order)
# All the barriers should get merged together.
expected = QuantumCircuit(20, 20)
expected.barrier(range(20))
expected.measure(measure_order, measure_order)
output = BarrierBeforeFinalMeasurements()(circuit)
self.assertEqual(expected, output)
# This assertion is that the ordering of the arguments in the barrier is fixed.
self.assertEqual(list(output.data[0].qubits), list(output.qubits))
if __name__ == "__main__":
unittest.main()