diff --git a/qiskit/transpiler/passes/utils/gates_basis.py b/qiskit/transpiler/passes/utils/gates_basis.py index 7f24a11f27ca..5e4455271c20 100644 --- a/qiskit/transpiler/passes/utils/gates_basis.py +++ b/qiskit/transpiler/passes/utils/gates_basis.py @@ -18,22 +18,37 @@ 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, target=None): """Initialize the GatesInBasis pass. Args: basis_gates (list): The list of strings representing the set of basis gates. + target (Target): If specified the target represnting the backend. If specified + this will be used instead of the ``basis_gates`` parameter """ super().__init__() self._basis_gates = set(basis_gates) + 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.topological_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]: + gates_out_of_basis = True + break + else: + 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 self.property_set["all_gates_in_basis"] = not gates_out_of_basis diff --git a/releasenotes/notes/gates_in_basis_target_aware-9bcd698adc3ecc28.yaml b/releasenotes/notes/gates_in_basis_target_aware-9bcd698adc3ecc28.yaml new file mode 100644 index 000000000000..c87cba7f8d11 --- /dev/null +++ b/releasenotes/notes/gates_in_basis_target_aware-9bcd698adc3ecc28.yaml @@ -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. + diff --git a/test/python/transpiler/test_gates_in_basis_pass.py b/test/python/transpiler/test_gates_in_basis_pass.py index a83809715e9c..7b4a92d0d568 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -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): @@ -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"])