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

Add pass to filter ops and label inserted barriers #10323

Merged
merged 14 commits into from
Nov 22, 2023
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
GatesInBasis
ConvertConditionsToIfOps
UnrollForLoops
RemoveLabeledOps
"""

# layout selection (placement)
Expand Down Expand Up @@ -292,3 +293,4 @@
from .utils import GatesInBasis
from .utils import ConvertConditionsToIfOps
from .utils import UnrollForLoops
from .utils import RemoveLabeledOps
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 = [
Expand Down
53 changes: 53 additions & 0 deletions qiskit/transpiler/passes/utils/remove_labeled_ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# 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.

"""Remove all babeled ops from a circuit"""

from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.passes.utils import control_flow


class RemoveLabeledOps(TransformationPass):
"""Remove all operations with a specific label..

This transformation pass is used to remove

Example:

.. plot::
:include-source:

from qiskit import QuantumCircuit
from qiskit.transpiler.passes import RemoveBarriers

circuit = QuantumCircuit(1)
circuit.x(0, label='foo')
circuit.barrier()
circuit.h(0)

circuit = RemoveLabeledOps('foo')(circuit)
circuit.draw('mpl')

"""

def __init__(self, label: str):
super().__init__()
self.label = label

@control_flow.trivial_recurse
def run(self, dag: DAGCircuit) -> DAGCircuit:
"""Run the RemoveBarriers pass on `dag`."""
for node in dag.op_nodes():
if node.op.label == self.label:
dag.remove_op_node(node)
return dag
13 changes: 12 additions & 1 deletion qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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


Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
9 changes: 9 additions & 0 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down