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 <amaldonador1300@alumno.ipn.mx>
Co-authored-by: Maldoalberto <Alberto.Maldonado.Romo@ibm.com>
Co-authored-by: Luciano Bello <bel@zurich.ibm.com>
Co-authored-by: atharva-satpute <55058959+atharva-satpute@users.noreply.github.com>
Co-authored-by: Jake Lishman <jake.lishman@ibm.com>
This commit is contained in:
Abby Mitchell 2023-06-20 12:35:49 -04:00 committed by GitHub
parent f9958143eb
commit 2ba298481a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 0 deletions

View File

@ -1565,6 +1565,15 @@ class DAGCircuit:
) )
) )
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): def ancestors(self, node):
"""Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes.""" """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)} return {self._multi_graph[x] for x in rx.ancestors(self._multi_graph, node._node_id)}
@ -1589,6 +1598,15 @@ class DAGCircuit:
) )
) )
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): def remove_op_node(self, node):
"""Remove an operation node n. """Remove an operation node n.

View File

@ -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))

View File

@ -740,6 +740,73 @@ class TestDagNodeSelection(QiskitTestCase):
or (isinstance(predecessor2, DAGInNode) and isinstance(predecessor1.op, Reset)) 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): def test_is_predecessor(self):
"""The method dag.is_predecessor(A, B) checks if node B is a predecessor of A""" """The method dag.is_predecessor(A, B) checks if node B is a predecessor of A"""