Skip to content

Commit

Permalink
perf!: Manual impl of Region/FlatRegion for improved perf (#162)
Browse files Browse the repository at this point in the history
Followup to #157. Avoids using `FilteredGraph` for the implementation of
`Region` and `FlatRegion` and instead implements the graph traits
manually to avoid doing full-graph traversals when we can do
better/faster checks.

Closes #159. Closes #145 (BFS rather than DFS). Closes #135.

BREAKING CHANGE: `Region` and `FlatRegion` are no longer aliases of
FilteredGraph
  • Loading branch information
aborgna-q authored Nov 20, 2024
1 parent c2cd701 commit 0722b98
Show file tree
Hide file tree
Showing 4 changed files with 791 additions and 113 deletions.
69 changes: 69 additions & 0 deletions src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
//! hierarchy.shrink_to(graph.node_count());
//! ```
use std::collections::VecDeque;
use std::iter::FusedIterator;
use std::mem::{replace, take};
use thiserror::Error;
Expand Down Expand Up @@ -390,6 +391,18 @@ impl Hierarchy {
}
}

/// Iterates over the node's descendants.
///
/// Traverses the hierarchy in breadth-first order.
///
/// The iterator will yield the node itself first, followed by its children.
pub fn descendants(&self, node: NodeIndex) -> Descendants<'_> {
Descendants {
layout: self,
child_queue: VecDeque::from(vec![node]),
}
}

/// Returns the number of the node's children.
#[inline]
pub fn child_count(&self, node: NodeIndex) -> usize {
Expand Down Expand Up @@ -610,6 +623,58 @@ impl<'a> ExactSizeIterator for Children<'a> {

impl<'a> FusedIterator for Children<'a> {}

/// Iterator created by [`Hierarchy::descendants`].
///
/// Traverses the descendants of a node in breadth-first order.
#[derive(Clone, Debug)]
pub struct Descendants<'a> {
/// The hierarchy this iterator is iterating over.
layout: &'a Hierarchy,
/// A queue of regions to visit.
///
/// For each region, we point to a child node that has not been visited yet.
/// When a region is visited, we move to the next child node and queue its children region at the end of the queue.
child_queue: VecDeque<NodeIndex>,
}

impl Default for Descendants<'static> {
fn default() -> Self {
static HIERARCHY: Hierarchy = Hierarchy::new();
Self {
layout: &HIERARCHY,
child_queue: VecDeque::new(),
}
}
}

impl<'a> Iterator for Descendants<'a> {
type Item = NodeIndex;

fn next(&mut self) -> Option<Self::Item> {
// The next element is always the first node in the queue.
let next = self.child_queue.pop_front()?;

// Check if the node had a next sibling, and add it to the front of queue.
if let Some(next_sibling) = self.layout.next(next) {
self.child_queue.push_front(next_sibling);
}

// Now add the children region of `next` to the end of the queue.
if let Some(child) = self.layout.first(next) {
self.child_queue.push_back(child);
}

Some(next)
}

#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.child_queue.len(), None)
}
}

impl<'a> FusedIterator for Descendants<'a> {}

/// Error produced when trying to attach nodes in the Hierarchy.
#[derive(Debug, Clone, Error, PartialEq, Eq)]
#[allow(missing_docs)]
Expand Down Expand Up @@ -668,6 +733,10 @@ mod test {
hierarchy.children(root).collect::<Vec<_>>(),
vec![child0, child1, child2]
);
assert_eq!(
hierarchy.descendants(root).collect::<Vec<_>>(),
vec![root, child0, child1, child2]
);
assert_eq!(hierarchy.parent(root), None);
assert_eq!(hierarchy.first(root), Some(child0));
assert_eq!(hierarchy.last(root), Some(child2));
Expand Down
9 changes: 6 additions & 3 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
pub mod filter;
pub mod refs;
pub mod region;
pub mod subgraph;

mod flat_region;
mod region;
mod subgraph;

#[cfg(feature = "petgraph")]
pub mod petgraph;
Expand All @@ -13,7 +15,8 @@ use std::collections::HashMap;
use crate::{portgraph::PortOperation, Direction, LinkError, NodeIndex, PortIndex, PortOffset};

pub use filter::{FilteredGraph, LinkFilter, NodeFilter, NodeFiltered};
pub use region::{FlatRegion, Region};
pub use flat_region::FlatRegion;
pub use region::Region;
pub use subgraph::Subgraph;

/// Core capabilities for querying a graph containing nodes and ports.
Expand Down
Loading

0 comments on commit 0722b98

Please sign in to comment.