Task: Make SabreSwap account for clbits in predecessors (Qiskit/qiskit#7952)

org: Qiskit
repo: qiskit
pr_number: 7952
issue_title: Make SabreSwap account for clbits in predecessors
issue_body: ### Summary\r\n\r\nPreviously, SabreSwap only checked that it was valid to add a gate back\r\nto the DAG by ensuring that all its qubit predecessors had already been\r\nadded to the DAG.  It did not check for clbit successors.  This meant\r\nthat measurements and (potentially, unverified) classically conditioned\r\ngates could be re-ordered out from the correct topological ordering.\r\n\r\nThe most notable effect of this was that measurements writing to the\r\nsame bit could be re-ordered, changing the output of the circuit.\r\n\r\nSee gh-7950 for more detail.\r\n\r\n<!--\r\n⚠️ If you do not respect this template, your pull request will be closed.\r\n⚠️ Your pull request title should be short detailed and understandable for all.\r\n⚠️ Also, please add a release note file using reno if the change needs to be\r\n  documented in the release notes.\r\n⚠️ If your pull request fixes an open issue, please link to the issue.\r\n\r\n- [ ] I have added the tests to cover my changes.\r\n- [ ] I have updated the documentation accordingly.\r\n- [ ] I have read the CONTRIBUTING document.\r\n-->\r\n\r\n\r\n\r\n### Details and comments\r\n\r\nFix #7950.  I also believe this to be the cause of the sporadic randomised test failures (see an example in the linked issue).\r\n\r\nAli (@ajavadia) - this might be something you ought to look at, since I don't understand SabreSwap well.\r\n
resolved_issues: number: 7950
title: `SabreSwap` can reorder operations that depend on the same classical bit
body: ### Environment\n\n- **Qiskit Terra version**: main @ ef997f9\r\n- **Python version**: any\r\n- **Operating system**: any\n\n### What is happening?\n\n`SabreSwap` can re-order measurement operations (and probably classically conditioned gates) that write to the same classical bits.  This affects optimisation level 3 by default, since it uses sabre to route if nothing else is provided.\r\n\r\nI am 95% sure this is the cause of the failures we're seeing in the randomised test suite currently.\r\n\r\n<details><summary>Expand for logs of recent failed randomised run</summary>\r\nHypothesis failure at commit 618e0ea8b3daca194c1bde97292e57c15eddd762, in file `test/randomized/test_transpile_equivalence.py`\r\n\r\n```text\r\nFalsifying example:\r\nstate = QCircuitMachine()\r\nstate.qasm()\r\n(v1,) = state.add_creg(n=1)\r\nstate.qasm()\r\nv2, v3, v4 = state.add_qreg(n=3)\r\nstate.qasm()\r\nstate.add_3q_gate(gate=qiskit.circuit.library.standard_gates.x.CCXGate, qargs=[v4, v3, v2])\r\nstate.qasm()\r\nstate.add_1q1c_gate(carg=v1, gate=qiskit.circuit.measure.Measure, qarg=v3)\r\nstate.qasm()\r\nstate.add_1q2p_gate(gate=qiskit.circuit.library.standard_gates.u2.U2Gate, params=[0.0, 0.0], qarg=v4)\r\nstate.qasm()\r\nstate.add_1q1c_gate(carg=v1, gate=qiskit.circuit.measure.Measure, qarg=v4)\r\nstate.qasm()\r\nEvaluating circuit at level 3 on fake_ourense:\r\nOPENQASM 2.0;\r\ninclude "qelib1.inc";\r\nqreg q35069[3];\r\ncreg c1656[1];\r\nccx q35069[2],q35069[1],q35069[0];\r\nmeasure q35069[1] -> c1656[0];\r\nu2(0,0) q35069[2];\r\nmeasure q35069[2] -> c1656[0];\r\n\r\nstate.equivalent_transpile(backend=<FakeOurense('fake_ourense')>, opt_level=3)\r\nstate.teardown()\r\n\r\nYou can reproduce this example by temporarily adding @reproduce_failure('6.43.2', b'AXicY2Bk5GFgYGDkZWBiYARCIBPIYmAE8thAbEYOBkwAkgKSrEBlzAALaABL') as a decorator on your test case\r\n```\r\n</details>\n\n### How can we reproduce the issue?\n\nGiven a circuit:\r\n```python\r\nimport qiskit\r\n\r\nqc = qiskit.QuantumCircuit(3, 1)\r\nqc.ccx(2, 1, 0)\r\nqc.h(0)\r\nqc.measure(0, 0)\r\nqc.measure(1, 0)\r\n```\r\nwhich has a drawing\r\n```text\r\n     ┌───┐┌───┐┌─┐\r\nq_0: ┤ X ├┤ H ├┤M├───\r\n     └─┬─┘└───┘└╥┘┌─┐\r\nq_1: ──■────────╫─┤M├\r\n       │        ║ └╥┘\r\nq_2: ──■────────╫──╫─\r\n                ║  ║\r\nc: 1/═══════════╩══╩═\r\n                0  0\r\n```\r\nClearly the output of the circuit should always be 0, because qubit 1 is never altered, and is measured second.\r\n\r\nIf we transpile it with a special coupling map that essentially only has one valid routing for the default decomposition of ccx, the orders of the measurements get flipped, which Aer confirms:\r\n```python\r\ntranspiled = qiskit.transpile(qc, coupling_map=[[(0, 2), (2, 0), (1, 2), (2, 1)]], routing_method="sabre")\r\nbackend = qiskit.Aer.get_backend("aer_simulator")\r\nprint(backend.run(qc, shots=1024).result().get_counts())\r\nprint(backend.run(transpiled, shots=1024).result().get_counts())\r\n```\r\nThis produces, for example:\r\n```python\r\n{'0': 1024}\r\n{'1': 503, '0': 521}\r\n```\r\nbut the second dictionary should be the same as the first.\r\n\r\n(It's too long to draw `transpiled` here, but if you do it locally, you can see the measurements are swapped.)\n\n### What should happen?\n\nSabre should take into account the topological ordering of measurements (and other clbits).  In the above examples, the counts should both be `{'0': 1024}` (which they are for any other routing method).\n\n### Any suggestions?\n\nIn both `SabreSwap._successors` and `._is_resolved`, only the quantum bits are accounted for.  My very very rough intuition for what's going on makes it feel like it should account for _all_ successors, not just qubits.

Reference solution:
fix_patch: diff --git a/qiskit/transpiler/passes/routing/sabre_swap.py b/qiskit/transpiler/passes/routing/sabre_swap.py\nindex 3690c763f734..5e44b29e5d13 100644\n--- a/qiskit/transpiler/passes/routing/sabre_swap.py\n+++ b/qiskit/transpiler/passes/routing/sabre_swap.py\n@@ -18,7 +18,6 @@\n import numpy as np\n \n from qiskit.circuit.library.standard_gates import SwapGate\n-from qiskit.circuit.quantumregister import Qubit\n from qiskit.transpiler.basepasses import TransformationPass\n from qiskit.transpiler.exceptions import TranspilerError\n from qiskit.transpiler.layout import Layout\n@@ -276,15 +275,18 @@ def _reset_qubits_decay(self):\n         self.qubits_decay = {k: 1 for k in self.qubits_decay.keys()}\n \n     def _successors(self, node, dag):\n-        for _, successor, edge_data in dag.edges(node):\n-            if not isinstance(successor, DAGOpNode):\n-                continue\n-            if isinstance(edge_data, Qubit):\n+        """Return an iterable of the successors along each wire from the given node.\n+\n+        This yields the same successor multiple times if there are parallel wires (e.g. two adjacent\n+        operations that have one clbit and qubit in common), which is important in the swapping\n+        algorithm for detecting if each wire has been accounted for."""\n+        for _, successor, _ in dag.edges(node):\n+            if isinstance(successor, DAGOpNode):\n                 yield successor\n \n     def _is_resolved(self, node):\n         """Return True if all of a node's predecessors in dag are applied."""\n-        return self.applied_predecessors[node] == len(node.qargs)\n+        return self.applied_predecessors[node] == len(node.qargs) + len(node.cargs)\n \n     def _obtain_extended_set(self, dag, front_layer):\n         """Populate extended_set by looking ahead a fixed number of gates.\ndiff --git a/releasenotes/notes/fix-sabreswap-clbits-428eb5f3a46063da.yaml b/releasenotes/notes/fix-sabreswap-clbits-428eb5f3a46063da.yaml\nnew file mode 100644\nindex 000000000000..0401e5a36f74\n--- /dev/null\n+++ b/releasenotes/notes/fix-sabreswap-clbits-428eb5f3a46063da.yaml\n@@ -0,0 +1,9 @@\n+---\n+fixes:\n+  - |\n+    Fixed :class:`.SabreSwap`, and by extension :func:`.transpile` with\n+    ``optimization_level=3``, occasionally re-ordering measurements invalidly.\n+    Previously, if two measurements wrote to the same classical bit,\n+    :class:`.SabreSwap` could (depending on the coupling map) re-order them to\n+    produce a non-equivalent circuit.  This behaviour was stochastic, so may\n+    not have appeared reliably.\n