From 2697bc9392cf5e430a11cb74c0c7ceb9dbc3bc9e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Mon, 12 Sep 2022 09:53:25 -0400 Subject: [PATCH] Fix handling of conditions in SabreDAG creation In #8388 we moved all of the SabreSwap pass's processing into rust. To facilitate that we had to create a rust DAG data structure to represent the data flow graph solely in Rust to enable analyzing the DAG structure in rust code without having to callback to python. To do this the DAGCircuit (which has a nearly identical representation in python) would export a list of nodes and the bits (both quantum and classical) that they operated on. This information is then used to create the SabreDAG used in the rust code. However, in this process an edge case was missed with classical condtions. If a condition was specified solely as a property of the operation and not in cargs list the SabreDAG would not know about the classical bit dependency between any subsequent operations involving that classical bit. This would cause incorrect output because the ndoes would not get processed as they were in the circuit. This commit fixes this issue by explicitly checking if there is a condition on the operation and there are no cargs and if so adding the carg bits to the SabreDAG directly. This fixes the incorrect representation in SabreDAG. --- .../transpiler/passes/routing/sabre_swap.py | 8 ++++- test/python/transpiler/test_sabre_swap.py | 29 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/qiskit/transpiler/passes/routing/sabre_swap.py b/qiskit/transpiler/passes/routing/sabre_swap.py index 0e37bc8b4b7d..bd5f2152fca2 100644 --- a/qiskit/transpiler/passes/routing/sabre_swap.py +++ b/qiskit/transpiler/passes/routing/sabre_swap.py @@ -210,11 +210,17 @@ def run(self, dag): dag_list = [] for node in dag.topological_op_nodes(): + cargs = [] + if node.op.condition is not None and not node.cargs: + cargs = [self._clbit_indices[x] for x in dag._bits_in_condition(node.op.condition)] + else: + cargs = [self._clbit_indices[x] for x in node.cargs] + dag_list.append( ( node._node_id, [self._qubit_indices[x] for x in node.qargs], - [self._clbit_indices[x] for x in node.cargs], + cargs, ) ) front_layer = np.asarray([x._node_id for x in dag.front_layer()], dtype=np.uintp) diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index 76c14535433f..f25cbf04cdcb 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -262,6 +262,35 @@ def test_classical_condition(self): actual = PassManager([TrivialLayout(cm), SabreSwap(cm)]).run(qc) self.assertEqual(expected, actual) + def test_classical_condition_cargs(self): + """Test that classical conditions are preserved even if missing from cargs DAGNode field. + + Created from reproduction in https://github.com/Qiskit/qiskit-terra/issues/8675 + """ + with self.subTest("missing measurement"): + qc = QuantumCircuit(3, 1) + qc.cx(0, 2).c_if(0, 0) + qc.measure(1, 0) + qc.h(2).c_if(0, 0) + expected = QuantumCircuit(3, 1) + expected.swap(1, 2) + expected.cx(0, 1).c_if(0, 0) + expected.measure(2, 0) + expected.h(1).c_if(0, 0) + result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) + self.assertEqual(result, expected) + with self.subTest("reordered measurement"): + qc = QuantumCircuit(3, 1) + qc.cx(0, 1).c_if(0, 0) + qc.measure(1, 0) + qc.h(0).c_if(0, 0) + expected = QuantumCircuit(3, 1) + expected.cx(0, 1).c_if(0, 0) + expected.measure(1, 0) + expected.h(0).c_if(0, 0) + result = SabreSwap(CouplingMap.from_line(3), seed=12345)(qc) + self.assertEqual(result, expected) + if __name__ == "__main__": unittest.main()