From ef070b13aaf60e2fac548b964c5b492b68d87a2a Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 22 Jun 2023 12:02:28 -0400 Subject: [PATCH] Add pass to remove labeled ops and label inserted barriers This commit adds a new transpiler pass RemoveLabeledOps which is used to remove any op nodes that match a given label. This is paired with a new label option for BarrierBeforeFinalMeasurements. These are combined in the preset pass managers to ensure we're not always adding a barrier before output measurements in the output of the transpiler. Fixes #10321 --- qiskit/transpiler/passes/__init__.py | 2 ++ qiskit/transpiler/passes/utils/__init__.py | 1 + .../utils/barrier_before_final_measurements.py | 8 +++++++- qiskit/transpiler/preset_passmanagers/common.py | 13 ++++++++++++- qiskit/transpiler/preset_passmanagers/level1.py | 8 +++++++- qiskit/transpiler/preset_passmanagers/level2.py | 8 +++++++- qiskit/transpiler/preset_passmanagers/level3.py | 8 +++++++- test/python/compiler/test_transpiler.py | 9 +++++++++ 8 files changed, 52 insertions(+), 5 deletions(-) diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 73e6ea055165..50a9eda52323 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -177,6 +177,7 @@ GatesInBasis ConvertConditionsToIfOps UnrollForLoops + RemoveLabeledOps """ # layout selection (placement) @@ -292,3 +293,4 @@ from .utils import GatesInBasis from .utils import ConvertConditionsToIfOps from .utils import UnrollForLoops +from .utils import RemoveLabeledOps diff --git a/qiskit/transpiler/passes/utils/__init__.py b/qiskit/transpiler/passes/utils/__init__.py index fd2d00bbc4b6..6a62b5cd203b 100644 --- a/qiskit/transpiler/passes/utils/__init__.py +++ b/qiskit/transpiler/passes/utils/__init__.py @@ -29,6 +29,7 @@ from .convert_conditions_to_if_ops import ConvertConditionsToIfOps from .unroll_forloops import UnrollForLoops from .minimum_point import MinimumPoint +from .remove_labeled_ops import RemoveLabeledOps # Utility functions from . import control_flow diff --git a/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py b/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py index e510ddc87aeb..f8ac7d4e83a1 100644 --- a/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py +++ b/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py @@ -27,6 +27,10 @@ class BarrierBeforeFinalMeasurements(TransformationPass): other measurements or barriers.) """ + def __init__(self, label=None): + super().__init__() + self.label = label + def run(self, dag): """Run the BarrierBeforeFinalMeasurements pass on `dag`.""" # Collect DAG nodes which are followed only by barriers or other measures. @@ -63,7 +67,9 @@ def run(self, dag): # from an unmeasured qubit after a measure. final_qubits = dag.qubits - barrier_layer.apply_operation_back(Barrier(len(final_qubits)), list(final_qubits), []) + barrier_layer.apply_operation_back( + Barrier(len(final_qubits), label=self.label), list(final_qubits), [] + ) # Preserve order of final ops collected earlier from the original DAG. ordered_final_nodes = [ diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 4645b4ca1385..11c8d3541c89 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -41,6 +41,7 @@ from qiskit.transpiler.passes import EnlargeWithAncilla from qiskit.transpiler.passes import ApplyLayout from qiskit.transpiler.passes import RemoveResetInZeroState +from qiskit.transpiler.passes import RemoveLabeledOps from qiskit.transpiler.passes import ValidatePulseGates from qiskit.transpiler.passes import PadDelay from qiskit.transpiler.passes import InstructionDurationCheck @@ -316,7 +317,15 @@ def _swap_condition(property_set): return not property_set["routing_not_needed"] if use_barrier_before_measurement: - routing.append([BarrierBeforeFinalMeasurements(), routing_pass], condition=_swap_condition) + routing.append( + [ + BarrierBeforeFinalMeasurements( + label="qiskit.transpiler.internal.routing.protection.barrier" + ), + routing_pass, + ], + condition=_swap_condition, + ) else: routing.append([routing_pass], condition=_swap_condition) @@ -336,6 +345,8 @@ def _swap_condition(property_set): ) routing.append(ApplyLayout(), condition=_apply_post_layout_condition) + routing.append([RemoveLabeledOps("qiskit.transpiler.internal.routing.protection.barrier")]) + return routing diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index db8b09b716b0..72c2d9d7a925 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -230,7 +230,13 @@ def _swap_mapped(property_set): layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append(_choose_layout_1, condition=_layout_not_perfect) layout.append( - [BarrierBeforeFinalMeasurements(), _improve_layout], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + _improve_layout, + ], + condition=_vf2_match_not_found, ) embed = common.generate_embed_passmanager(coupling_map_layout) layout.append( diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 743018881da3..ae6b24c8a094 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -190,7 +190,13 @@ def _swap_mapped(property_set): layout.append(_given_layout) layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append( - [BarrierBeforeFinalMeasurements(), _choose_layout_1], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + _choose_layout_1, + ], + condition=_vf2_match_not_found, ) embed = common.generate_embed_passmanager(coupling_map_layout) layout.append( diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 7fe64eed521d..857c72f93879 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -222,7 +222,13 @@ def _swap_mapped(property_set): layout.append(_given_layout) layout.append(_choose_layout_0, condition=_choose_layout_condition) layout.append( - [BarrierBeforeFinalMeasurements(), _choose_layout_1], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + _choose_layout_1, + ], + condition=_vf2_match_not_found, ) embed = common.generate_embed_passmanager(coupling_map_layout) layout.append( diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index ebfc79af9b04..85b01d131fce 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1657,6 +1657,15 @@ def test_paulis_to_constrained_1q_basis(self, opt_level, basis): self.assertGreaterEqual(set(basis) | {"barrier"}, transpiled.count_ops().keys()) self.assertEqual(Operator(qc), Operator(transpiled)) + @data(0, 1, 2, 3) + def test_barrier_not_output(self, opt_level): + """Test that barriers added as part internal transpiler operations do not leak out.""" + qc = QuantumCircuit(2, 2) + qc.cx(0, 1) + qc.measure(range(2), range(2)) + tqc = transpile(qc, initial_layout=[1, 4], coupling_map=[[1, 2], [2, 3], [3, 4]], optimization_level=opt_level) + self.assertNotIn("barrier", tqc.count_ops()) + @ddt class TestPostTranspileIntegration(QiskitTestCase):