diff --git a/qiskit/transpiler/passes/utils/gates_basis.py b/qiskit/transpiler/passes/utils/gates_basis.py index 7f24a11f27ca..34ea539890ed 100644 --- a/qiskit/transpiler/passes/utils/gates_basis.py +++ b/qiskit/transpiler/passes/utils/gates_basis.py @@ -13,27 +13,51 @@ """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. + 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. """ 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: + 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 not self._target.instruction_supported( + gate.name, tuple(qubit_map[bit] for bit in gate.qargs) + ): + gates_out_of_basis = True + break + 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 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..299d1dff7b06 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -13,11 +13,15 @@ """Test GatesInBasis pass.""" from qiskit.circuit import QuantumCircuit +from qiskit.circuit.library import HGate, CXGate, UGate +from qiskit.circuit.measure import Measure from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary 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 +92,117 @@ 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_ideal_sim_target(self): + """Test with target that has ideal gates.""" + target = Target(num_qubits=2) + target.add_instruction(HGate()) + target.add_instruction(CXGate()) + target.add_instruction(Measure()) + property_set = {} + analysis_pass = GatesInBasis(target=target) + circuit = QuantumCircuit(2) + circuit.h(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_ideal_sim_target(self): + """Test with target that has ideal gates.""" + target = Target() + target.add_instruction(HGate()) + target.add_instruction(UGate(0, 0, 0)) + target.add_instruction(Measure()) + property_set = {} + analysis_pass = GatesInBasis(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"])