diff --git a/qiskit/transpiler/passes/utils/gates_basis.py b/qiskit/transpiler/passes/utils/gates_basis.py index 06943ea2d652..657b1d134852 100644 --- a/qiskit/transpiler/passes/utils/gates_basis.py +++ b/qiskit/transpiler/passes/utils/gates_basis.py @@ -12,6 +12,8 @@ """Check if all gates in the DAGCircuit are in the specified basis gates.""" +from qiskit.circuit import ControlFlowOp +from qiskit.converters import circuit_to_dag from qiskit.transpiler.basepasses import AnalysisPass @@ -41,18 +43,32 @@ def run(self, dag): return gates_out_of_basis = False if self._target is not None: + + def _visit_target(dag, wire_map): + 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(wire_map[bit] for bit in gate.qargs) + ): + return True + # Control-flow ops still need to be supported, so don't skip them in the + # previous checks. + if isinstance(gate.op, ControlFlowOp): + for block in gate.op.blocks: + inner_wire_map = { + inner: wire_map[outer] + for outer, inner in zip(gate.qargs, block.qubits) + } + if _visit_target(circuit_to_dag(block), inner_wire_map): + return True + return False + 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 + gates_out_of_basis = _visit_target(dag, qubit_map) else: - for gate in dag._op_names: + for gate in dag.count_ops(recurse=True): if gate not in self._basis_gates: gates_out_of_basis = True break diff --git a/test/python/transpiler/test_gates_in_basis_pass.py b/test/python/transpiler/test_gates_in_basis_pass.py index 929ee7c89d93..bae16c921582 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -12,8 +12,8 @@ """Test GatesInBasis pass.""" -from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import HGate, CXGate, UGate +from qiskit.circuit import QuantumCircuit, ForLoopOp, IfElseOp, Clbit +from qiskit.circuit.library import HGate, CXGate, UGate, XGate, ZGate from qiskit.circuit.measure import Measure from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary from qiskit.transpiler import PassManager @@ -206,3 +206,60 @@ def test_all_gates_in_basis_after_translation_with_target(self): pm.append(analysis_pass) pm.run(circuit) self.assertTrue(pm.property_set["all_gates_in_basis"]) + + def test_basis_gates_control_flow(self): + """Test that the pass recurses into control flow.""" + circuit = QuantumCircuit(4, 1) + circuit.h(0) + circuit.measure(0, 0) + with circuit.for_loop((1, 2)): + circuit.cx(0, 1) + with circuit.if_test((circuit.clbits[0], True)) as else_: + circuit.x(2) + with else_: + circuit.z(3) + + one_missing = {"h", "measure", "for_loop", "cx", "if_else", "x"} + pass_ = GatesInBasis(one_missing) + pass_(circuit) + self.assertFalse(pass_.property_set["all_gates_in_basis"]) + + complete = one_missing | {"z"} + pass_ = GatesInBasis(complete) + pass_(circuit) + self.assertTrue(pass_.property_set["all_gates_in_basis"]) + + def test_basis_gates_target(self): + """Test that the pass recurses into control flow.""" + circuit = QuantumCircuit(4, 1) + circuit.h(0) + circuit.measure(0, 0) + with circuit.for_loop((1, 2)): + circuit.cx(0, 1) + with circuit.if_test((circuit.clbits[0], True)) as else_: + circuit.x(2) + with else_: + circuit.z(3) + + instructions = [ + HGate(), + Measure(), + ForLoopOp((), None, QuantumCircuit(4)), + CXGate(), + IfElseOp((Clbit(), True), QuantumCircuit(2), QuantumCircuit(2)), + XGate(), + ZGate(), + ] + one_missing = Target(num_qubits=4) + for instruction in instructions[:-1]: + one_missing.add_instruction(instruction, {None: None}) + pass_ = GatesInBasis(target=one_missing) + pass_(circuit) + self.assertFalse(pass_.property_set["all_gates_in_basis"]) + + complete = Target(num_qubits=4) + for instruction in instructions: + complete.add_instruction(instruction, {None: None}) + pass_ = GatesInBasis(target=complete) + pass_(circuit) + self.assertTrue(pass_.property_set["all_gates_in_basis"])