diff --git a/qiskit/transpiler/passes/utils/check_map.py b/qiskit/transpiler/passes/utils/check_map.py index f71e5640f163..d56a709253c9 100644 --- a/qiskit/transpiler/passes/utils/check_map.py +++ b/qiskit/transpiler/passes/utils/check_map.py @@ -15,6 +15,7 @@ from qiskit.transpiler.basepasses import AnalysisPass from qiskit.transpiler.target import Target from qiskit.circuit.controlflow import ControlFlowOp +from qiskit.converters import circuit_to_dag class CheckMap(AnalysisPass): @@ -64,35 +65,30 @@ def run(self, dag): Args: dag (DAGCircuit): DAG to map. """ - from qiskit.converters import circuit_to_dag - - self.property_set[self.property_set_field] = True - if not self.qargs: + self.property_set[self.property_set_field] = True return + wire_map = {bit: index for index, bit in enumerate(dag.qubits)} + self.property_set[self.property_set_field] = self._recurse(dag, wire_map) - qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)} + def _recurse(self, dag, wire_map) -> bool: for node in dag.op_nodes(include_directives=False): - is_controlflow_op = isinstance(node.op, ControlFlowOp) - if len(node.qargs) == 2 and not is_controlflow_op: - if dag.has_calibration_for(node): - continue - physical_q0 = qubit_indices[node.qargs[0]] - physical_q1 = qubit_indices[node.qargs[1]] - if (physical_q0, physical_q1) not in self.qargs: - self.property_set["check_map_msg"] = "{}({}, {}) failed".format( - node.name, - physical_q0, - physical_q1, - ) - self.property_set[self.property_set_field] = False - return - elif is_controlflow_op: - order = [qubit_indices[bit] for bit in node.qargs] + if isinstance(node.op, ControlFlowOp): for block in node.op.blocks: - dag_block = circuit_to_dag(block) - mapped_dag = dag.copy_empty_like() - mapped_dag.compose(dag_block, qubits=order) - self.run(mapped_dag) - if not self.property_set[self.property_set_field]: - return + inner_wire_map = { + inner: wire_map[outer] for inner, outer in zip(block.qubits, node.qargs) + } + if not self._recurse(circuit_to_dag(block), inner_wire_map): + return False + elif ( + len(node.qargs) == 2 + and not dag.has_calibration_for(node) + and (wire_map[node.qargs[0]], wire_map[node.qargs[1]]) not in self.qargs + ): + self.property_set["check_map_msg"] = "{}({}, {}) failed".format( + node.name, + wire_map[node.qargs[0]], + wire_map[node.qargs[1]], + ) + return False + return True diff --git a/releasenotes/notes/fix-checkmap-nested-condition-1776f952f6c6722a.yaml b/releasenotes/notes/fix-checkmap-nested-condition-1776f952f6c6722a.yaml new file mode 100644 index 000000000000..0805524cd7b0 --- /dev/null +++ b/releasenotes/notes/fix-checkmap-nested-condition-1776f952f6c6722a.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + The :class:`.CheckMap` transpiler pass will no longer spuriously error when dealing with nested + conditional structures created by the control-flow builder interface. See `#10394 + `__. diff --git a/test/python/transpiler/test_check_map.py b/test/python/transpiler/test_check_map.py index 8368e3cc94eb..27802c708a96 100644 --- a/test/python/transpiler/test_check_map.py +++ b/test/python/transpiler/test_check_map.py @@ -255,6 +255,28 @@ def test_nested_controlflow_false(self): pass_.run(dag) self.assertFalse(pass_.property_set["is_swap_mapped"]) + def test_nested_conditional_unusual_bit_order(self): + """Test that `CheckMap` succeeds when inner conditional blocks have clbits that are involved + in their own (nested conditionals), and the binding order is not the same as the + bit-definition order. See gh-10394.""" + qr = QuantumRegister(2, "q") + cr1 = ClassicalRegister(2, "c1") + cr2 = ClassicalRegister(2, "c2") + + # Note that the bits here are not in the same order as in the outer circuit object, but they + # are the same as the binding order in the `if_test`, so everything maps `{x: x}` and it + # should all be fine. This kind of thing is a staple of the control-flow builders. + inner_order = [cr2[0], cr1[0], cr2[1], cr1[1]] + inner = QuantumCircuit(qr, inner_order, cr1, cr2) + inner.cx(0, 1).c_if(cr2, 3) + + outer = QuantumCircuit(qr, cr1, cr2) + outer.if_test((cr1, 3), inner, outer.qubits, inner_order) + + pass_ = CheckMap(CouplingMap.from_line(2)) + pass_(outer) + self.assertTrue(pass_.property_set["is_swap_mapped"]) + if __name__ == "__main__": unittest.main()