diff --git a/benches/benchmarks/toposort.rs b/benches/benchmarks/toposort.rs index be854d0..810429c 100644 --- a/benches/benchmarks/toposort.rs +++ b/benches/benchmarks/toposort.rs @@ -9,7 +9,7 @@ use portgraph::{ use super::generators::*; fn run_toposort(graph: &PortGraph, roots: impl IntoIterator) { - let topo: TopoSort = toposort(graph, roots, Direction::Outgoing); + let topo: TopoSort<_> = toposort(graph, roots, Direction::Outgoing); for n in topo { black_box(n); } diff --git a/src/algorithms/toposort.rs b/src/algorithms/toposort.rs index ff1a266..18d2db3 100644 --- a/src/algorithms/toposort.rs +++ b/src/algorithms/toposort.rs @@ -1,8 +1,12 @@ -use crate::{Direction, LinkView, NodeIndex, PortGraph, PortIndex, PortView, SecondaryMap}; +use crate::{Direction, LinkView, NodeIndex, PortIndex, SecondaryMap}; use bitvec::prelude::BitVec; use std::{collections::VecDeque, fmt::Debug, iter::FusedIterator}; -/// Returns an iterator over a [`PortGraph`] in topological order. +/// Returns an iterator over a [`LinkView`] in topological order. +/// +/// ## Type parameters +/// - `G`: The graph type implementing [`LinkView`], +/// - `Map`: Internal workspace for graph traversal (see below). /// /// The `Map` type parameter specifies the type of the secondary map that is /// used to store the dominator tree data. The default is [`BitVec`], which is @@ -23,26 +27,32 @@ use std::{collections::VecDeque, fmt::Debug, iter::FusedIterator}; /// graph.link_nodes(node_a, 0, node_b, 0).unwrap(); /// /// // Run a topological sort on the graph starting at node A. -/// let topo: TopoSort = toposort(&graph, [node_a], Direction::Outgoing); +/// let topo: TopoSort<_> = toposort(&graph, [node_a], Direction::Outgoing); /// assert_eq!(topo.collect::>(), [node_a, node_b]); /// ``` -pub fn toposort( - graph: &PortGraph, +pub fn toposort( + graph: G, source: impl IntoIterator, direction: Direction, -) -> TopoSort<'_, Map> +) -> TopoSort<'static, G, Map> where Map: SecondaryMap, + G: LinkView, { TopoSort::new(graph, source, direction, None, None) } -/// Returns an iterator over a [`PortGraph`] in topological order, applying a +/// Returns an iterator over a [`LinkView`] in topological order, applying a /// filter to the nodes and ports. No filtered nodes are returned, and neither /// are any nodes only accessible via filtered nodes or filtered ports. /// /// If the filter closures return false for a node or port, it is skipped. /// +/// ## Type parameters +/// - `'f`: The lifetime of the filter closures, +/// - `G`: The graph type implementing [`LinkView`], +/// - `Map`: Internal workspace for graph traversal (see below). +/// /// The `Map` type parameter specifies the type of the secondary map that is /// used to store the dominator tree data. The default is [`BitVec`], which is /// efficient for full graph traversal, i.e. when all nodes are reachable from @@ -66,7 +76,7 @@ where /// graph.link_nodes(node_a, 1, node_c, 0).unwrap(); /// /// // Run a topological sort on the graph starting at node A. -/// let topo: TopoSort = toposort_filtered( +/// let topo: TopoSort<_> = toposort_filtered( /// &graph, /// [node_a, node_d], /// Direction::Outgoing, @@ -75,15 +85,16 @@ where /// ); /// assert_eq!(topo.collect::>(), [node_a, node_b]); /// ``` -pub fn toposort_filtered<'graph, Map>( - graph: &'graph PortGraph, +pub fn toposort_filtered<'f, G, Map>( + graph: G, source: impl IntoIterator, direction: Direction, - node_filter: impl FnMut(NodeIndex) -> bool + 'graph, - port_filter: impl FnMut(NodeIndex, PortIndex) -> bool + 'graph, -) -> TopoSort<'_, Map> + node_filter: impl FnMut(NodeIndex) -> bool + 'f, + port_filter: impl FnMut(NodeIndex, PortIndex) -> bool + 'f, +) -> TopoSort<'f, G, Map> where Map: SecondaryMap, + G: LinkView, { TopoSort::new( graph, @@ -94,13 +105,13 @@ where ) } -/// Iterator over a [`PortGraph`] in topological order. +/// Iterator over a [`LinkView`] in topological order. /// /// See [`toposort`] for more information. /// /// Implements [Kahn's algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm). -pub struct TopoSort<'graph, Map = BitVec> { - graph: &'graph PortGraph, +pub struct TopoSort<'f, G, Map = BitVec> { + graph: G, visited_ports: Map, /// A VecDeque is used for the node list to produce a canonical ordering, /// as successors of nodes already have a canonical ordering due to ports. @@ -111,26 +122,27 @@ pub struct TopoSort<'graph, Map = BitVec> { nodes_seen: usize, /// A filter closure for the nodes to visit. If the closure returns false, /// the node is skipped. - node_filter: Option bool + 'graph>>, + node_filter: Option bool + 'f>>, /// A filter closure for the ports to visit. If the closure returns false, /// the port is skipped. - port_filter: Option bool + 'graph>>, + port_filter: Option bool + 'f>>, } -impl<'graph, Map> TopoSort<'graph, Map> +impl<'f, Map, G> TopoSort<'f, G, Map> where Map: SecondaryMap, + G: LinkView, { /// Initialises a new topological sort of a portgraph in a specified direction /// starting at a collection of `source` nodes. /// /// If the default value of `Map` is not `false`, this requires O(#ports) time. fn new( - graph: &'graph PortGraph, + graph: G, source: impl IntoIterator, direction: Direction, - mut node_filter: Option bool + 'graph>>, - port_filter: Option bool + 'graph>>, + mut node_filter: Option bool + 'f>>, + port_filter: Option bool + 'f>>, ) -> Self { let mut visited_ports: Map = SecondaryMap::new(); @@ -167,7 +179,8 @@ where /// Returns whether there are ports that have not been visited yet. /// If the iterator has seen all nodes this implies that there is a cycle. - pub fn ports_remaining(&self) -> impl DoubleEndedIterator + '_ { + // pub fn ports_remaining(&self) -> impl DoubleEndedIterator + '_ { + pub fn ports_remaining(&self) -> impl Iterator + '_ { self.graph .ports_iter() .filter(move |&p| !self.visited_ports.get(p)) @@ -175,11 +188,13 @@ where /// Checks if a node becomes ready once it is visited from `from_port`, i.e. /// it has been reached from all its linked ports. - fn becomes_ready(&mut self, node: NodeIndex, from_port: PortIndex) -> bool { + fn becomes_ready(&mut self, node: NodeIndex, from_port: impl Into) -> bool { + let from_port = from_port.into(); if self.ignore_node(node) { return false; } - self.graph.ports(node, self.direction.reverse()).all(|p| { + let ports: Vec<_> = self.graph.ports(node, self.direction.reverse()).collect(); + ports.into_iter().all(|p| { if p == from_port { // This port must have not been visited yet. Otherwise, the node // would have been already been reported as ready and added to @@ -216,16 +231,18 @@ where } } -impl<'graph, Map> Iterator for TopoSort<'graph, Map> +impl<'f, Map, G> Iterator for TopoSort<'f, G, Map> where Map: SecondaryMap, + G: LinkView, { type Item = NodeIndex; fn next(&mut self) -> Option { let node = self.candidate_nodes.pop_front()?; + let ports = self.graph.ports(node, self.direction).collect::>(); - for port in self.graph.ports(node, self.direction) { + for port in ports { self.visited_ports.set(port, true); if self.ignore_port(node, port) { @@ -238,7 +255,7 @@ where if self.becomes_ready(target, link) { self.candidate_nodes.push_back(target); } - self.visited_ports.set(link, true); + self.visited_ports.set(link.into(), true); } } @@ -255,11 +272,17 @@ where } } -impl<'graph, Map> FusedIterator for TopoSort<'graph, Map> where Map: SecondaryMap {} +impl<'f, Map, G> FusedIterator for TopoSort<'f, G, Map> +where + Map: SecondaryMap, + G: LinkView, +{ +} -impl<'graph, Map> Debug for TopoSort<'graph, Map> +impl<'f, Map, G> Debug for TopoSort<'f, G, Map> where Map: Debug, + G: Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TopoSort") @@ -275,7 +298,7 @@ where mod test { use super::*; - use crate::{Direction, LinkMut, PortMut, PortView}; + use crate::{Direction, LinkMut, PortGraph, PortMut, PortView}; #[test] fn small_toposort() { @@ -294,13 +317,13 @@ mod test { graph.link_nodes(node_c, 0, node_d, 0).unwrap(); // Run a topological sort on the graph starting at node A. - let topo: TopoSort = toposort(&graph, [node_a, node_d], Direction::Outgoing); + let topo: TopoSort<_> = toposort(&graph, [node_a, node_d], Direction::Outgoing); assert_eq!( topo.collect::>(), [node_a, node_d, node_b, node_e, node_c] ); - let topo_filtered: TopoSort = toposort_filtered( + let topo_filtered: TopoSort<_> = toposort_filtered( &graph, [node_a, node_d], Direction::Outgoing, diff --git a/src/lib.rs b/src/lib.rs index afd41fc..03610bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ //! graph.link_ports(port_a, port_b).unwrap(); //! //! // Run a topological sort on the graph starting at node A. -//! let topo: TopoSort = toposort(&graph, [node_a], Direction::Outgoing); +//! let topo: TopoSort<_> = toposort(&graph, [node_a], Direction::Outgoing); //! assert_eq!(topo.collect::>(), [node_a, node_b]); //! ``` //!