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

Make GatesInBasis transpiler pass Target aware #7548

Merged
merged 12 commits into from
Mar 30, 2022
Merged
41 changes: 33 additions & 8 deletions qiskit/transpiler/passes/utils/gates_basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,52 @@
"""Check if all gates in the DAGCircuit are in the specified basis gates."""

from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError


class GatesInBasis(AnalysisPass):
"""Check if all gates in a DAG are in a given set of gates"""

def __init__(self, basis_gates):
def __init__(self, basis_gates=None, target=None):
"""Initialize the GatesInBasis pass.

Args:
basis_gates (list): The list of strings representing the set of basis gates.
kevinhartman marked this conversation as resolved.
Show resolved Hide resolved
target (Target): The target representing the backend. If specified
this will be used instead of the ``basis_gates`` parameter

Raises:
TranspilerError: If neither basis_gates or target is set.
kevinhartman marked this conversation as resolved.
Show resolved Hide resolved
"""
super().__init__()
self._basis_gates = set(basis_gates)
if basis_gates is None and target is None:
raise TranspilerError(
"A value for 'basis_gates' or 'target' must be set to use this pass"
)
if basis_gates is not None:
kevinhartman marked this conversation as resolved.
Show resolved Hide resolved
self._basis_gates = set(basis_gates).union(
{"measure", "reset", "barrier", "snapshot", "delay"}
)
self._target = target

def run(self, dag):
"""Run the GatesInBasis pass on `dag`."""
gates_out_of_basis = False
basic_instrs = {"measure", "reset", "barrier", "snapshot", "delay"}
for gate in dag._op_names:
if gate not in self._basis_gates and gate not in basic_instrs:
gates_out_of_basis = True
break

if self._target is not None:
qubit_map = {qubit: index for index, qubit in enumerate(dag.qubits)}
for gate in dag.op_nodes():
# Barrier is universal and supported by all backends
if gate.name == "barrier":
continue
if gate.name not in self._target:
gates_out_of_basis = True
break
if tuple(qubit_map[bit] for bit in gate.qargs) not in self._target[gate.name]:
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
gates_out_of_basis = True
break
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
else:
for gate in dag._op_names:
if gate not in self._basis_gates:
gates_out_of_basis = True
break
self.property_set["all_gates_in_basis"] = not gates_out_of_basis
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
features:
- |
Added a new kwarg, ``target``, to the constructor for the
:class:`.GatesInBasis` transpiler pass. This new argument can be used to
optionally specify a :class:`.Target` object that represents the backend.
When set this :class:`.Target` will be used for determining whether
a :class:`.DAGCircuit` contains gates outside the basis set.

86 changes: 86 additions & 0 deletions test/python/transpiler/test_gates_in_basis_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import BasisTranslator
from qiskit.transpiler.passes import GatesInBasis
from qiskit.transpiler.target import Target
from qiskit.test import QiskitTestCase
from qiskit.test.mock.fake_backend_v2 import FakeBackend5QV2


class TestGatesInBasisPass(QiskitTestCase):
Expand Down Expand Up @@ -88,3 +90,87 @@ def test_all_gates_in_basis_after_translation(self):
pm.append(analysis_pass)
pm.run(circuit)
self.assertTrue(pm.property_set["all_gates_in_basis"])

def test_all_gates_in_basis_with_target(self):
"""Test circuit with all gates in basis with target."""
target = FakeBackend5QV2().target
basis_gates = ["cx", "u"] # not used
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target=target)
circuit = QuantumCircuit(2)
circuit.u(0, 0, 0, 0)
circuit.cx(0, 1)
circuit.measure_all()
analysis_pass(circuit, property_set=property_set)
self.assertTrue(property_set["all_gates_in_basis"])

def test_all_gates_not_in_basis_with_target(self):
"""Test circuit with not all gates in basis with target."""
target = FakeBackend5QV2().target
basis_gates = ["cx", "h"]
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target=target)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
analysis_pass(circuit, property_set=property_set)
self.assertFalse(property_set["all_gates_in_basis"])

def test_all_gates_in_basis_not_on_all_qubits_with_target(self):
"""Test circuit with gate in global basis but not local basis."""
target = FakeBackend5QV2().target
basis_gates = ["ecr", "cx", "h"]
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target=target)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.ecr(0, 1)
circuit.measure_all()
analysis_pass(circuit, property_set=property_set)
self.assertFalse(property_set["all_gates_in_basis"])

def test_all_gates_in_basis_empty_circuit_with_target(self):
"""Test circuit with no gates with target."""
target = FakeBackend5QV2().target
basis_gates = ["cx", "u"]
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target=target)
circuit = QuantumCircuit(2)
analysis_pass(circuit, property_set=property_set)
self.assertTrue(property_set["all_gates_in_basis"])

def test_all_gates_in_empty_target(self):
"""Test circuit with gates and empty basis with target."""
target = Target()
basis_gates = []
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target=target)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
analysis_pass(circuit, property_set=property_set)
self.assertFalse(property_set["all_gates_in_basis"])

def test_all_gates_in_basis_after_translation_with_target(self):
"""Test circuit with gates in basis after conditional translation."""
target = FakeBackend5QV2().target
basis_gates = ["cx", "u"]
property_set = {}
analysis_pass = GatesInBasis(basis_gates, target)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()
analysis_pass(circuit, property_set=property_set)
self.assertFalse(property_set["all_gates_in_basis"])
pm = PassManager()
pm.append(analysis_pass)
pm.append(
BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target),
condition=lambda property_set: not property_set["all_gates_in_basis"],
)
pm.append(analysis_pass)
pm.run(circuit)
self.assertTrue(pm.property_set["all_gates_in_basis"])