From 2ba298481a2c7d0b498d22d2e9fc7710154c23bb Mon Sep 17 00:00:00 2001 From: Abby Mitchell <23662430+javabster@users.noreply.github.com> Date: Tue, 20 Jun 2023 12:35:49 -0400 Subject: [PATCH] Added classical_predecessors method (#9980) * Added classical_predecessors method * add accesed method * Added classsical_successor method * Added classical predecessors and successors method * added tol command * successors method * add test classical_successor method * changed DagOutNode * Apply suggestions from code review from @atharva-satpute. Thanks! Co-authored-by: atharva-satpute <55058959+atharva-satpute@users.noreply.github.com> * add assertRaises on test methods * correct typo variable name * change the descrption * change the text on the example * change the format of yaml * new example * new suggestions * fix test * new test example and change the release note * Fixup release note * Fix typo --------- Co-authored-by: Maldoalberto Co-authored-by: Maldoalberto Co-authored-by: Luciano Bello Co-authored-by: atharva-satpute <55058959+atharva-satpute@users.noreply.github.com> Co-authored-by: Jake Lishman --- qiskit/dagcircuit/dagcircuit.py | 18 +++++ ...assical-predecessors-9ecef0561822e934.yaml | 27 ++++++++ test/python/dagcircuit/test_dagcircuit.py | 67 +++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 releasenotes/notes/add-classical-predecessors-9ecef0561822e934.yaml diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index f40015d7f078..a0eb6feec718 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1565,6 +1565,15 @@ def quantum_predecessors(self, node): ) ) + def classical_predecessors(self, node): + """Returns iterator of the predecessors of a node that are + connected by a classical edge as DAGOpNodes and DAGInNodes.""" + return iter( + self._multi_graph.find_predecessors_by_edge( + node._node_id, lambda edge_data: isinstance(edge_data, Clbit) + ) + ) + def ancestors(self, node): """Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes.""" return {self._multi_graph[x] for x in rx.ancestors(self._multi_graph, node._node_id)} @@ -1589,6 +1598,15 @@ def quantum_successors(self, node): ) ) + def classical_successors(self, node): + """Returns iterator of the successors of a node that are + connected by a classical edge as DAGOpNodes and DAGInNodes.""" + return iter( + self._multi_graph.find_successors_by_edge( + node._node_id, lambda edge_data: isinstance(edge_data, Clbit) + ) + ) + def remove_op_node(self, node): """Remove an operation node n. diff --git a/releasenotes/notes/add-classical-predecessors-9ecef0561822e934.yaml b/releasenotes/notes/add-classical-predecessors-9ecef0561822e934.yaml new file mode 100644 index 000000000000..9c554633d4ff --- /dev/null +++ b/releasenotes/notes/add-classical-predecessors-9ecef0561822e934.yaml @@ -0,0 +1,27 @@ +--- +features: + - | + Added :meth:`.DAGCircuit.classical_predecessors` and + :meth:`.DAGCircuit.classical_successors`, an alternative to select the classical + wires without having to go to the inner graph object directly of a node in the DAG. + The following example illustrates the new functionality:: + + from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister + from qiskit.converters import circuit_to_dag + from qiskit.circuit.library import RZGate + + q = QuantumRegister(3, 'q') + c = ClassicalRegister(3, 'c') + circ = QuantumCircuit(q, c) + circ.h(q[0]) + circ.cx(q[0], q[1]) + circ.measure(q[0], c[0]) + circ.rz(0.5, q[1]).c_if(c, 2) + circ.measure(q[1], c[0]) + dag = circuit_to_dag(circ) + + rz_node = dag.op_nodes(RZGate)[0] + # Contains the "measure" on clbit 0, and the "wire start" nodes for clbits 1 and 2. + classical_predecessors = list(dag.classical_predecessors(rz_node)) + # Contains the "measure" on clbit 0, and the "wire end" nodes for clbits 1 and 2. + classical_successors = list(dag.classical_successors(rz_node)) diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 48ec6b7af29f..0a61bb499ce7 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -740,6 +740,73 @@ def test_quantum_predecessors(self): or (isinstance(predecessor2, DAGInNode) and isinstance(predecessor1.op, Reset)) ) + def test_classical_predecessors(self): + """The method dag.classical_predecessors() returns predecessors connected by classical edges""" + + # ┌───┐ ┌───┐ + # q_0: ┤ H ├──■───────────────────■──┤ H ├ + # ├───┤┌─┴─┐ ┌─┴─┐├───┤ + # q_1: ┤ H ├┤ X ├──■─────────■──┤ X ├┤ H ├ + # └───┘└───┘┌─┴─┐┌───┐┌─┴─┐└───┘└───┘ + # q_2: ──────────┤ X ├┤ H ├┤ X ├────────── + # └───┘└───┘└───┘ + # c: 5/═══════════════════════════════════ + + self.dag.apply_operation_back(HGate(), [self.qubit0], []) + self.dag.apply_operation_back(HGate(), [self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2], []) + self.dag.apply_operation_back(HGate(), [self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(HGate(), [self.qubit0], []) + self.dag.apply_operation_back(HGate(), [self.qubit1], []) + self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], []) + self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) + + predecessor_measure = self.dag.classical_predecessors(self.dag.named_nodes("measure").pop()) + + predecessor1 = next(predecessor_measure) + + with self.assertRaises(StopIteration): + next(predecessor_measure) + + self.assertIsInstance(predecessor1, DAGInNode) + self.assertIsInstance(predecessor1.wire, Clbit) + + def test_classical_successors(self): + """The method dag.classical_successors() returns successors connected by classical edges""" + + # ┌───┐ ┌───┐ + # q_0: ┤ H ├──■───────────────────■──┤ H ├ + # ├───┤┌─┴─┐ ┌─┴─┐├───┤ + # q_1: ┤ H ├┤ X ├──■─────────■──┤ X ├┤ H ├ + # └───┘└───┘┌─┴─┐┌───┐┌─┴─┐└───┘└───┘ + # q_2: ──────────┤ X ├┤ H ├┤ X ├────────── + # └───┘└───┘└───┘ + # c: 5/═══════════════════════════════════ + + self.dag.apply_operation_back(HGate(), [self.qubit0], []) + self.dag.apply_operation_back(HGate(), [self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2], []) + self.dag.apply_operation_back(HGate(), [self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit1, self.qubit2], []) + self.dag.apply_operation_back(CXGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(HGate(), [self.qubit0], []) + self.dag.apply_operation_back(HGate(), [self.qubit1], []) + self.dag.apply_operation_back(Measure(), [self.qubit0, self.clbit0], []) + self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) + + successors_measure = self.dag.classical_successors(self.dag.named_nodes("measure").pop()) + + successors1 = next(successors_measure) + with self.assertRaises(StopIteration): + next(successors_measure) + + self.assertIsInstance(successors1, DAGOutNode) + self.assertIsInstance(successors1.wire, Clbit) + def test_is_predecessor(self): """The method dag.is_predecessor(A, B) checks if node B is a predecessor of A"""