From c7fc31de99d3d15098188c9fa3619b228bfd1f9f Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:33:57 -0400 Subject: [PATCH 1/6] Add: `ancestors`, `descendants`, `bfs_successors` to oxidized `DAGCircuit` --- crates/circuit/src/dag_circuit.rs | 56 +++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index f5c64b12b89..a1ba6df48a2 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -43,6 +43,11 @@ use rustworkx_core::petgraph::prelude::StableDiGraph; use rustworkx_core::petgraph::stable_graph::{DefaultIx, IndexType, Neighbors, NodeIndex}; use rustworkx_core::petgraph::visit::{IntoNodeReferences, NodeCount, NodeRef}; use rustworkx_core::petgraph::Incoming; +use rustworkx_core::traversal::{ + ancestors as core_ancestors, bfs_successors as core_bfs_successors, + descendants as core_descendants, +}; +use std::borrow::Borrow; use std::convert::Infallible; use std::f64::consts::PI; use std::ffi::c_double; @@ -253,7 +258,7 @@ impl PyCircuitModule { .downcast_into_exact()? .unbind(), quantum_register: module - .getattr("QuantumRegsiter")? + .getattr("QuantumRegister")? .downcast_into_exact()? .unbind(), control_flow_op: module @@ -2695,21 +2700,60 @@ def _format(operand): /// Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes. fn ancestors(&self, py: Python, node: &DAGNode) -> PyResult> { - // return {self._multi_graph[x] for x in rx.ancestors(self._multi_graph, node._node_id)} - todo!() + if let Some(node_index) = node.node { + let ancestors = core_ancestors(&self.dag, node_index) + .filter_map(|node| self.get_node(py, node).ok()); + let ancestor_set = PySet::empty_bound(py)?; + for ancestor in ancestors { + ancestor_set.add(ancestor)?; + } + return Ok(ancestor_set.unbind()); + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } } /// Returns set of the descendants of a node as DAGOpNodes and DAGOutNodes. fn descendants(&self, py: Python, node: &DAGNode) -> PyResult> { - // return {self._multi_graph[x] for x in rx.descendants(self._multi_graph, node._node_id)} - todo!() + if let Some(node_index) = node.node { + let descendants = core_descendants(&self.dag, node_index) + .filter_map(|node| self.get_node(py, node).ok()); + let descendant_set = PySet::empty_bound(py)?; + for descendant in descendants { + descendant_set.add(descendant)?; + } + return Ok(descendant_set.unbind()); + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. fn bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { // return iter(rx.bfs_successors(self._multi_graph, node._node_id)) - todo!() + if let Some(node_index) = node.node { + let descendants = + core_bfs_successors(&self.dag, node_index).filter_map(|(node, nodes)| { + match ( + self.get_node(py, node).ok(), + nodes + .iter() + .filter_map(|sub_node| self.get_node(py, *sub_node).ok()) + .collect::>(), + ) { + (Some(node), nodes) => Some((node, nodes)), + _ => None, + } + }); + let descendant_set = PySet::empty_bound(py)?; + for descendant in descendants { + descendant_set.add(descendant)?; + } + return Ok(descendant_set.unbind()); + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } } /// Returns iterator of the successors of a node that are From 7523deff577bffb32c9a91ad924fdcc2eb43c496 Mon Sep 17 00:00:00 2001 From: Kevin Hartman Date: Fri, 28 Jun 2024 13:23:30 -0400 Subject: [PATCH 2/6] Import fixes. --- crates/circuit/src/dag_circuit.rs | 52 ++++++++++++++++--- crates/circuit/src/imports.rs | 2 + crates/circuit/src/lib.rs | 13 ++++- .../passes/calibration/rzx_templates.py | 21 ++++---- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index d3131cdfacc..c64da1d6cac 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -17,7 +17,7 @@ use crate::circuit_instruction::{ }; use crate::dag_node::{DAGInNode, DAGNode, DAGOpNode, DAGOutNode}; use crate::error::DAGCircuitError; -use crate::imports::VARIABLE_MAPPER; +use crate::imports::{DAG_NODE, VARIABLE_MAPPER}; use crate::interner::{Index, IndexedInterner, Interner}; use crate::operations::{Operation, OperationType, Param}; use crate::{interner, BitType, Clbit, Qubit, SliceOrInt, TupleLikeArg}; @@ -133,7 +133,9 @@ pub struct DAGCircuit { dag: StableDiGraph, + #[pyo3(get)] qregs: Py, + #[pyo3(get)] cregs: Py, /// The cache used to intern instruction qargs. @@ -379,6 +381,35 @@ impl DAGCircuit { }) } + /// Returns the current sequence of registered :class:`.Qubit` instances as a list. + /// + /// .. warning:: + /// + /// Do not modify this list yourself. It will invalidate the :class:`DAGCircuit` data + /// structures. + /// + /// Returns: + /// list(:class:`.Qubit`): The current sequence of registered qubits. + #[getter] + pub fn qubits(&self, py: Python<'_>) -> Py { + self.qubits.cached().clone_ref(py) + } + + /// Returns the current sequence of registered :class:`.Clbit` + /// instances as a list. + /// + /// .. warning:: + /// + /// Do not modify this list yourself. It will invalidate the :class:`DAGCircuit` data + /// structures. + /// + /// Returns: + /// list(:class:`.Clbit`): The current sequence of registered clbits. + #[getter] + pub fn clbits(&self, py: Python<'_>) -> Py { + self.clbits.cached().clone_ref(py) + } + /// Return a list of the wires in order. #[getter] fn get_wires(&self, py: Python<'_>) -> Py { @@ -1873,14 +1904,23 @@ def _format(operand): } } - // - // def node_eq(node_self, node_other): - // return DAGNode.semantic_eq(node_self, node_other, self_bit_indices, other_bit_indices) - // - // return rx.is_isomorphic_node_match(self._multi_graph, other._multi_graph, node_eq) todo!() + // Check for VF2 isomorphic match. + // self.topological_nodes() + // let semantic_eq = DAG_NODE.get_bound(py).getattr(intern!(py, "semantic_eq"))?; + // let node_match = |n1, n2| -> bool { + // semantic_eq + // .call1((n1, n2, self_bit_indices, other_bit_indices)).map_or(false, |r| r.extract().unwrap_or(false)) + // }; + // Ok(petgraph::algo::is_isomorphic_matching( + // &self.dag, + // &other.dag, + // node_match, + // |_, _| true, + // )) } + /// Yield nodes in topological order. /// /// Args: diff --git a/crates/circuit/src/imports.rs b/crates/circuit/src/imports.rs index 0df3707cc02..b0c0d63ac86 100644 --- a/crates/circuit/src/imports.rs +++ b/crates/circuit/src/imports.rs @@ -74,6 +74,8 @@ pub static SINGLETON_CONTROLLED_GATE: ImportOnceCell = pub static VARIABLE_MAPPER: ImportOnceCell = ImportOnceCell::new("qiskit.circuit._classical_resource_map", "VariableMapper"); +pub static DAG_NODE: ImportOnceCell = ImportOnceCell::new("qiskit.dagcircuit", "DAGNode"); + /// A mapping from the enum variant in crate::operations::StandardGate to the python /// module path and class name to import it. This is used to populate the conversion table /// when a gate is added directly via the StandardGate path and there isn't a Python object diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 3d5b1c702c4..56cc3871e73 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -26,6 +26,7 @@ mod interner; use pyo3::prelude::*; use pyo3::types::{PySequence, PySlice, PyTuple}; use std::ops::Deref; +use pyo3::DowncastError; /// A private enumeration type used to extract arguments to pymethod /// that may be either an index or a slice @@ -49,8 +50,18 @@ pub struct TupleLikeArg<'py> { impl<'py> FromPyObject<'py> for TupleLikeArg<'py> { fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + let value = match ob.downcast::() { + Ok(seq) => {seq.to_tuple()?} + Err(_) => { + PyTuple::new_bound( + ob.py(), + ob.iter()? + .map(|o| Ok(o?.unbind())) + .collect::>>()?) + } + }; Ok(TupleLikeArg { - value: ob.downcast::()?.to_tuple()?, + value, }) } } diff --git a/qiskit/transpiler/passes/calibration/rzx_templates.py b/qiskit/transpiler/passes/calibration/rzx_templates.py index 406e5e75de0..12211f18b2b 100644 --- a/qiskit/transpiler/passes/calibration/rzx_templates.py +++ b/qiskit/transpiler/passes/calibration/rzx_templates.py @@ -20,17 +20,6 @@ from qiskit.circuit.library.templates import rzx -class RZXTemplateMap(Enum): - """Mapping of instruction name to decomposition template.""" - - ZZ1 = rzx.rzx_zz1() - ZZ2 = rzx.rzx_zz2() - ZZ3 = rzx.rzx_zz3() - YZ = rzx.rzx_yz() - XZ = rzx.rzx_xz() - CY = rzx.rzx_cy() - - def rzx_templates(template_list: List[str] = None) -> Dict: """Convenience function to get the cost_dict and templates for template matching. @@ -40,6 +29,16 @@ def rzx_templates(template_list: List[str] = None) -> Dict: Returns: Decomposition templates and cost values. """ + class RZXTempateMap(Enum): + """Mapping of instruction name to decomposition template.""" + + ZZ1 = rzx.rzx_zz1() + ZZ2 = rzx.rzx_zz2() + ZZ3 = rzx.rzx_zz3() + YZ = rzx.rzx_yz() + XZ = rzx.rzx_xz() + CY = rzx.rzx_cy() + if template_list is None: template_list = ["zz1", "zz2", "zz3", "yz", "xz", "cy"] From 80defc57d9228cceec66fe8faf2e0681260297ba Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:47:30 -0400 Subject: [PATCH 3/6] Add: Rust-native methods - Added rust native methods `ancestors`, `descendants` and `bfs_successors` that return iterators of node indices. --- crates/circuit/src/dag_circuit.rs | 90 +++++++++++++++++++------------ 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index e8429a22957..514991d0c3f 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -2808,7 +2808,8 @@ def _format(operand): } /// Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes. - fn ancestors(&self, py: Python, node: &DAGNode) -> PyResult> { + #[pyo3(name="ancestors")] + fn py_ancestors(&self, py: Python, node: &DAGNode) -> PyResult> { if let Some(node_index) = node.node { let ancestors = core_ancestors(&self.dag, node_index) .filter_map(|node| self.get_node(py, node).ok()); @@ -2823,47 +2824,41 @@ def _format(operand): } /// Returns set of the descendants of a node as DAGOpNodes and DAGOutNodes. - fn descendants(&self, py: Python, node: &DAGNode) -> PyResult> { - if let Some(node_index) = node.node { - let descendants = core_descendants(&self.dag, node_index) - .filter_map(|node| self.get_node(py, node).ok()); - let descendant_set = PySet::empty_bound(py)?; - for descendant in descendants { - descendant_set.add(descendant)?; - } - return Ok(descendant_set.unbind()); - } else { - unreachable!("The provided {:?} is not properly initialized.", node) + #[pyo3(name="descendants")] + fn py_descendants(&self, py: Python, node: &DAGNode) -> PyResult> { + let descendants = self.descendants(node) + .filter_map(|node| self.get_node(py, node).ok()); + let descendant_set = PySet::empty_bound(py)?; + for descendant in descendants { + descendant_set.add(descendant)?; } + return Ok(descendant_set.unbind()); } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. - fn bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { + #[pyo3(name="bfs_successors")] + fn py_bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { // return iter(rx.bfs_successors(self._multi_graph, node._node_id)) - if let Some(node_index) = node.node { - let descendants = - core_bfs_successors(&self.dag, node_index).filter_map(|(node, nodes)| { - match ( - self.get_node(py, node).ok(), - nodes - .iter() - .filter_map(|sub_node| self.get_node(py, *sub_node).ok()) - .collect::>(), - ) { - (Some(node), nodes) => Some((node, nodes)), - _ => None, - } - }); - let descendant_set = PySet::empty_bound(py)?; - for descendant in descendants { - descendant_set.add(descendant)?; + let successor_index = self.bfs_successors(node).filter_map(|(node, nodes)| { + match ( + self.get_node(py, node).ok(), + nodes + .iter() + .filter_map(|sub_node| self.get_node(py, *sub_node).ok()) + .collect::>(), + ) { + (Some(node), nodes) => Some((node, nodes)), + _ => None, } - return Ok(descendant_set.unbind()); - } else { - unreachable!("The provided {:?} is not properly initialized.", node) + }); + let successor_list = PyList::empty_bound(py); + for successors in successor_index { + successor_list.append(successors)?; } + return Ok(successor_list.unbind()); } + /// Returns iterator of the successors of a node that are /// connected by a quantum edge as DAGOpNodes and DAGOutNodes. @@ -3615,6 +3610,35 @@ impl DAGCircuit { } } + /// Returns an iterator of the ancestors indices of a node. + pub fn ancestors<'a>(&'a self, node: &DAGNode) -> impl Iterator + 'a { + if let Some(node_index) = node.node { + core_ancestors(&self.dag, node_index) + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } + } + + /// Returns an iterator of the descendants of a node as DAGOpNodes and DAGOutNodes. + pub fn descendants<'a>(&'a self, node: &'a DAGNode) -> impl Iterator + 'a { + if let Some(node_index) = node.node { + core_descendants(&self.dag, node_index) + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } + } + + /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node + /// and [DAGNode] is its successors in BFS order. + pub fn bfs_successors<'a>(&'a self, node: &'a DAGNode) -> impl Iterator)> + 'a { + // return iter(rx.bfs_successors(self._multi_graph, node._node_id)) + if let Some(node_index) = node.node { + core_bfs_successors(&self.dag, node_index) + } else { + unreachable!("The provided {:?} is not properly initialized.", node) + } + } + fn unpack_into(&self, py: Python, id: NodeIndex, weight: &NodeType) -> PyResult> { let dag_node = match weight { NodeType::QubitIn(qubit) => Py::new( From 96c787356f1b25daf61e961b0bcbe0e810b7fece Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:04:19 -0400 Subject: [PATCH 4/6] Fix: Avoid checking validity of `NodeIndex` - Make rust_native `ancestors`, `descendants` and `bfs_successors` return instances of `NodeIndex`. --- crates/circuit/src/dag_circuit.rs | 82 ++++++++++--------------------- 1 file changed, 26 insertions(+), 56 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index 514991d0c3f..60291eca209 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -2810,53 +2810,36 @@ def _format(operand): /// Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes. #[pyo3(name="ancestors")] fn py_ancestors(&self, py: Python, node: &DAGNode) -> PyResult> { - if let Some(node_index) = node.node { - let ancestors = core_ancestors(&self.dag, node_index) - .filter_map(|node| self.get_node(py, node).ok()); - let ancestor_set = PySet::empty_bound(py)?; - for ancestor in ancestors { - ancestor_set.add(ancestor)?; - } - return Ok(ancestor_set.unbind()); - } else { - unreachable!("The provided {:?} is not properly initialized.", node) - } + let ancestors: PyResult> = self.ancestors(node.node.unwrap()) + .map(|node| self.get_node(py, node)).collect(); + Ok(PySet::new_bound(py, &ancestors?)?.unbind()) } /// Returns set of the descendants of a node as DAGOpNodes and DAGOutNodes. #[pyo3(name="descendants")] fn py_descendants(&self, py: Python, node: &DAGNode) -> PyResult> { - let descendants = self.descendants(node) - .filter_map(|node| self.get_node(py, node).ok()); - let descendant_set = PySet::empty_bound(py)?; - for descendant in descendants { - descendant_set.add(descendant)?; - } - return Ok(descendant_set.unbind()); + let descendants: PyResult> = self.descendants(node.node.unwrap()) + .map(|node| self.get_node(py, node)).collect(); + Ok(PySet::new_bound(py, &descendants?)?.unbind()) } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. #[pyo3(name="bfs_successors")] - fn py_bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { - // return iter(rx.bfs_successors(self._multi_graph, node._node_id)) - let successor_index = self.bfs_successors(node).filter_map(|(node, nodes)| { - match ( - self.get_node(py, node).ok(), - nodes - .iter() - .filter_map(|sub_node| self.get_node(py, *sub_node).ok()) - .collect::>(), - ) { - (Some(node), nodes) => Some((node, nodes)), - _ => None, - } - }); - let successor_list = PyList::empty_bound(py); - for successors in successor_index { - successor_list.append(successors)?; - } - return Ok(successor_list.unbind()); + fn py_bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { + let successor_index: PyResult)>> = self.bfs_successors(node.node.unwrap()).map(|(node, nodes)| -> PyResult<(PyObject, Vec)> { + Ok(( + self.get_node(py, node)?, + nodes + .iter() + .map(|sub_node| self.get_node(py, *sub_node)) + .collect::>>()? + )) + }).collect(); + Ok(PyList::new_bound(py, successor_index?) + .into_any() + .iter()? + .unbind()) } @@ -3611,32 +3594,19 @@ impl DAGCircuit { } /// Returns an iterator of the ancestors indices of a node. - pub fn ancestors<'a>(&'a self, node: &DAGNode) -> impl Iterator + 'a { - if let Some(node_index) = node.node { - core_ancestors(&self.dag, node_index) - } else { - unreachable!("The provided {:?} is not properly initialized.", node) - } + pub fn ancestors<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { + core_ancestors(&self.dag, node) } /// Returns an iterator of the descendants of a node as DAGOpNodes and DAGOutNodes. - pub fn descendants<'a>(&'a self, node: &'a DAGNode) -> impl Iterator + 'a { - if let Some(node_index) = node.node { - core_descendants(&self.dag, node_index) - } else { - unreachable!("The provided {:?} is not properly initialized.", node) - } + pub fn descendants<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { + core_descendants(&self.dag, node) } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. - pub fn bfs_successors<'a>(&'a self, node: &'a DAGNode) -> impl Iterator)> + 'a { - // return iter(rx.bfs_successors(self._multi_graph, node._node_id)) - if let Some(node_index) = node.node { - core_bfs_successors(&self.dag, node_index) - } else { - unreachable!("The provided {:?} is not properly initialized.", node) - } + pub fn bfs_successors<'a>(&'a self, node: NodeIndex) -> impl Iterator)> + 'a { + core_bfs_successors(&self.dag, node) } fn unpack_into(&self, py: Python, id: NodeIndex, weight: &NodeType) -> PyResult> { From e55d5028684eebaf1471e155ce4dc416a91ee814 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:23:20 -0400 Subject: [PATCH 5/6] Fix: Filter out rust native functions to match previous behavior. - The recently implemented rustwork-x core functions include the origin node and instances with no successors. - Filter out those instances from the iterators. --- crates/circuit/src/dag_circuit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index 60291eca209..7710492c366 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -3595,18 +3595,18 @@ impl DAGCircuit { /// Returns an iterator of the ancestors indices of a node. pub fn ancestors<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { - core_ancestors(&self.dag, node) + core_ancestors(&self.dag, node).filter(move |next| next != &node) } /// Returns an iterator of the descendants of a node as DAGOpNodes and DAGOutNodes. pub fn descendants<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { - core_descendants(&self.dag, node) + core_descendants(&self.dag, node).filter(move |next| next != &node) } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. pub fn bfs_successors<'a>(&'a self, node: NodeIndex) -> impl Iterator)> + 'a { - core_bfs_successors(&self.dag, node) + core_bfs_successors(&self.dag, node).filter(move |(_, others)| !others.is_empty()) } fn unpack_into(&self, py: Python, id: NodeIndex, weight: &NodeType) -> PyResult> { From 3c402fec6d26683e47c167f4faf2a37ee57d17c7 Mon Sep 17 00:00:00 2001 From: Raynel Sanchez <87539502+raynelfss@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:41:49 -0400 Subject: [PATCH 6/6] Format: Run cargo fmt --- crates/circuit/src/dag_circuit.rs | 48 ++++++++++++++++++------------- crates/circuit/src/lib.rs | 21 ++++++-------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index 7710492c366..f70c90863bd 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -1925,7 +1925,6 @@ def _format(operand): // )) } - /// Yield nodes in topological order. /// /// Args: @@ -2808,40 +2807,46 @@ def _format(operand): } /// Returns set of the ancestors of a node as DAGOpNodes and DAGInNodes. - #[pyo3(name="ancestors")] + #[pyo3(name = "ancestors")] fn py_ancestors(&self, py: Python, node: &DAGNode) -> PyResult> { - let ancestors: PyResult> = self.ancestors(node.node.unwrap()) - .map(|node| self.get_node(py, node)).collect(); + let ancestors: PyResult> = self + .ancestors(node.node.unwrap()) + .map(|node| self.get_node(py, node)) + .collect(); Ok(PySet::new_bound(py, &ancestors?)?.unbind()) } /// Returns set of the descendants of a node as DAGOpNodes and DAGOutNodes. - #[pyo3(name="descendants")] + #[pyo3(name = "descendants")] fn py_descendants(&self, py: Python, node: &DAGNode) -> PyResult> { - let descendants: PyResult> = self.descendants(node.node.unwrap()) - .map(|node| self.get_node(py, node)).collect(); + let descendants: PyResult> = self + .descendants(node.node.unwrap()) + .map(|node| self.get_node(py, node)) + .collect(); Ok(PySet::new_bound(py, &descendants?)?.unbind()) } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. - #[pyo3(name="bfs_successors")] + #[pyo3(name = "bfs_successors")] fn py_bfs_successors(&self, py: Python, node: &DAGNode) -> PyResult> { - let successor_index: PyResult)>> = self.bfs_successors(node.node.unwrap()).map(|(node, nodes)| -> PyResult<(PyObject, Vec)> { - Ok(( - self.get_node(py, node)?, - nodes - .iter() - .map(|sub_node| self.get_node(py, *sub_node)) - .collect::>>()? - )) - }).collect(); + let successor_index: PyResult)>> = self + .bfs_successors(node.node.unwrap()) + .map(|(node, nodes)| -> PyResult<(PyObject, Vec)> { + Ok(( + self.get_node(py, node)?, + nodes + .iter() + .map(|sub_node| self.get_node(py, *sub_node)) + .collect::>>()?, + )) + }) + .collect(); Ok(PyList::new_bound(py, successor_index?) .into_any() .iter()? .unbind()) } - /// Returns iterator of the successors of a node that are /// connected by a quantum edge as DAGOpNodes and DAGOutNodes. @@ -3599,13 +3604,16 @@ impl DAGCircuit { } /// Returns an iterator of the descendants of a node as DAGOpNodes and DAGOutNodes. - pub fn descendants<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { + pub fn descendants<'a>(&'a self, node: NodeIndex) -> impl Iterator + 'a { core_descendants(&self.dag, node).filter(move |next| next != &node) } /// Returns an iterator of tuples of (DAGNode, [DAGNodes]) where the DAGNode is the current node /// and [DAGNode] is its successors in BFS order. - pub fn bfs_successors<'a>(&'a self, node: NodeIndex) -> impl Iterator)> + 'a { + pub fn bfs_successors<'a>( + &'a self, + node: NodeIndex, + ) -> impl Iterator)> + 'a { core_bfs_successors(&self.dag, node).filter(move |(_, others)| !others.is_empty()) } diff --git a/crates/circuit/src/lib.rs b/crates/circuit/src/lib.rs index 56cc3871e73..af641d6a5ad 100644 --- a/crates/circuit/src/lib.rs +++ b/crates/circuit/src/lib.rs @@ -25,8 +25,8 @@ mod interner; use pyo3::prelude::*; use pyo3::types::{PySequence, PySlice, PyTuple}; -use std::ops::Deref; use pyo3::DowncastError; +use std::ops::Deref; /// A private enumeration type used to extract arguments to pymethod /// that may be either an index or a slice @@ -51,18 +51,15 @@ pub struct TupleLikeArg<'py> { impl<'py> FromPyObject<'py> for TupleLikeArg<'py> { fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { let value = match ob.downcast::() { - Ok(seq) => {seq.to_tuple()?} - Err(_) => { - PyTuple::new_bound( - ob.py(), - ob.iter()? - .map(|o| Ok(o?.unbind())) - .collect::>>()?) - } + Ok(seq) => seq.to_tuple()?, + Err(_) => PyTuple::new_bound( + ob.py(), + ob.iter()? + .map(|o| Ok(o?.unbind())) + .collect::>>()?, + ), }; - Ok(TupleLikeArg { - value, - }) + Ok(TupleLikeArg { value }) } }