From 5b25c8429c308afe29b696c79f64a5436d23df37 Mon Sep 17 00:00:00 2001 From: Kris Date: Sun, 3 Jul 2022 23:49:50 +0100 Subject: [PATCH 01/10] Draft: Move more connected components functions to retworkx-core --- .../src/connectivity/conn_components.rs | 19 ++++++++++++++++++- rustworkx-core/src/connectivity/mod.rs | 1 + src/connectivity/mod.rs | 5 +---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 5bdf19900..fc56cd032 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -13,7 +13,8 @@ use hashbrown::HashSet; use std::collections::VecDeque; use std::hash::Hash; - +use petgraph::algo; +// use super::digraph; use petgraph::visit::{GraphProp, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable}; use petgraph::{Incoming, Outgoing}; @@ -165,6 +166,22 @@ where num_components } +/// Compute the strongly connected components for a directed graph +/// +/// This function is implemented using Kosaraju's algorithm +/// +/// :param PyDiGraph graph: The input graph to find the strongly connected +/// components for. +/// +/// :return: A list of list of node ids for strongly connected components +/// :rtype: list +pub fn strongly_connected_components(graph: &digraph::PyDiGraph) -> Vec> { + algo::kosaraju_scc(&graph.graph) + .iter() + .map(|x| x.iter().map(|id| id.index()).collect()) + .collect() +} + #[cfg(test)] mod test_conn_components { use hashbrown::HashSet; diff --git a/rustworkx-core/src/connectivity/mod.rs b/rustworkx-core/src/connectivity/mod.rs index a86e97841..4641f6985 100644 --- a/rustworkx-core/src/connectivity/mod.rs +++ b/rustworkx-core/src/connectivity/mod.rs @@ -23,3 +23,4 @@ pub use chain::chain_decomposition; pub use conn_components::bfs_undirected; pub use conn_components::connected_components; pub use conn_components::number_connected_components; +pub use conn_components::strongly_connected_components; diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index 4def48ae3..c8c3e6922 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -138,10 +138,7 @@ pub fn cycle_basis(graph: &graph::PyGraph, root: Option) -> Vec Vec> { - algo::kosaraju_scc(&graph.graph) - .iter() - .map(|x| x.iter().map(|id| id.index()).collect()) - .collect() + connectivity::strongly_connected_components(graph) } /// Return the first cycle encountered during DFS of a given PyDiGraph, From 6ed29d0ad9b52fef1aff202acda3565248dc1a6f Mon Sep 17 00:00:00 2001 From: Kris Date: Sat, 23 Jul 2022 11:40:41 +0300 Subject: [PATCH 02/10] fix functions --- .../src/connectivity/conn_components.rs | 19 +++++++------------ rustworkx-core/src/connectivity/mod.rs | 2 ++ src/connectivity/mod.rs | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index fc56cd032..03d081629 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -11,9 +11,9 @@ // under the License. use hashbrown::HashSet; +use petgraph::algo; use std::collections::VecDeque; use std::hash::Hash; -use petgraph::algo; // use super::digraph; use petgraph::visit::{GraphProp, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable}; use petgraph::{Incoming, Outgoing}; @@ -166,17 +166,12 @@ where num_components } -/// Compute the strongly connected components for a directed graph -/// -/// This function is implemented using Kosaraju's algorithm -/// -/// :param PyDiGraph graph: The input graph to find the strongly connected -/// components for. -/// -/// :return: A list of list of node ids for strongly connected components -/// :rtype: list -pub fn strongly_connected_components(graph: &digraph::PyDiGraph) -> Vec> { - algo::kosaraju_scc(&graph.graph) +pub fn strongly_connected_components(graph: G) -> Vec> +where + G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, + G::NodeId: Eq + Hash, +{ + algo::kosaraju_scc(&graph) .iter() .map(|x| x.iter().map(|id| id.index()).collect()) .collect() diff --git a/rustworkx-core/src/connectivity/mod.rs b/rustworkx-core/src/connectivity/mod.rs index 4641f6985..49f502e02 100644 --- a/rustworkx-core/src/connectivity/mod.rs +++ b/rustworkx-core/src/connectivity/mod.rs @@ -22,5 +22,7 @@ pub use biconnected::articulation_points; pub use chain::chain_decomposition; pub use conn_components::bfs_undirected; pub use conn_components::connected_components; +pub use conn_components::is_connected; +pub use conn_components::node_connected_components; pub use conn_components::number_connected_components; pub use conn_components::strongly_connected_components; diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index c8c3e6922..158affe55 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -138,7 +138,7 @@ pub fn cycle_basis(graph: &graph::PyGraph, root: Option) -> Vec Vec> { - connectivity::strongly_connected_components(graph) + connectivity::strongly_connected_components(graph.graph) } /// Return the first cycle encountered during DFS of a given PyDiGraph, From 08ec2e0df30338639f57bfcdda5f6f6a93098581 Mon Sep 17 00:00:00 2001 From: Kris Date: Sat, 23 Jul 2022 16:35:25 +0300 Subject: [PATCH 03/10] add the two other functions --- .../src/connectivity/conn_components.rs | 37 ++++++++++++++++++- src/connectivity/mod.rs | 25 ++----------- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 03d081629..e0aff37a8 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -9,7 +9,8 @@ // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. - +use petgraph::graph::NodeIndex; +// use rs::NullGraph; use hashbrown::HashSet; use petgraph::algo; use std::collections::VecDeque; @@ -17,7 +18,6 @@ use std::hash::Hash; // use super::digraph; use petgraph::visit::{GraphProp, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable}; use petgraph::{Incoming, Outgoing}; - /// Given an graph, a node in the graph, and a visit_map, /// return the set of nodes connected to the given node /// using breadth first search and treating all edges @@ -177,6 +177,39 @@ where .collect() } +pub fn is_connected(graph: G) -> Vec> +where + G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, + G::NodeId: Eq + Hash, +{ + match graph.node_identifiers().next() { + Some(node) => { + let component = node_connected_component(graph, node.index())?; + Ok(component.len() == graph.node_count()) + } + None => Err(NullGraph::new_err("Invalid operation on a NullGraph")), + } +} + +pub fn node_connected_component(graph: G, node: usize) -> HashSet +where + G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, + G::NodeId: Eq + Hash, +{ + let node = NodeIndex::new(node); + + if !graph.contains_node(node) { + return Err(InvalidNode::new_err( + "The input index for 'node' is not a valid node index", + )); + } + + Ok(bfs_undirected(&graph, node, &mut graph.visit_map()) + .into_iter() + .map(|x| x.index()) + .collect()) +} + #[cfg(test)] mod test_conn_components { use hashbrown::HashSet; diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index 158affe55..82e5b93b5 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -138,7 +138,7 @@ pub fn cycle_basis(graph: &graph::PyGraph, root: Option) -> Vec Vec> { - connectivity::strongly_connected_components(graph.graph) + connectivity::strongly_connected_components(&graph.graph) } /// Return the first cycle encountered during DFS of a given PyDiGraph, @@ -258,20 +258,7 @@ pub fn connected_components(graph: &graph::PyGraph) -> Vec> { #[pyfunction] #[pyo3(text_signature = "(graph, node, /)")] pub fn node_connected_component(graph: &graph::PyGraph, node: usize) -> PyResult> { - let node = NodeIndex::new(node); - - if !graph.graph.contains_node(node) { - return Err(InvalidNode::new_err( - "The input index for 'node' is not a valid node index", - )); - } - - Ok( - connectivity::bfs_undirected(&graph.graph, node, &mut graph.graph.visit_map()) - .into_iter() - .map(|x| x.index()) - .collect(), - ) + connectivity::node_connected_component(&graph.graph, node) } /// Check if the graph is connected. @@ -285,13 +272,7 @@ pub fn node_connected_component(graph: &graph::PyGraph, node: usize) -> PyResult #[pyfunction] #[pyo3(text_signature = "(graph, /)")] pub fn is_connected(graph: &graph::PyGraph) -> PyResult { - match graph.graph.node_indices().next() { - Some(node) => { - let component = node_connected_component(graph, node.index())?; - Ok(component.len() == graph.graph.node_count()) - } - None => Err(NullGraph::new_err("Invalid operation on a NullGraph")), - } + connectivity::is_connected(&graph.graph) } /// Find the number of weakly connected components in a directed graph From d495ea27e695b2b142af59179520cd2ca39dd04d Mon Sep 17 00:00:00 2001 From: Kris Date: Thu, 25 Aug 2022 10:18:01 +0300 Subject: [PATCH 04/10] fix some errors --- rustworkx-core/src/connectivity/conn_components.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index e0aff37a8..2b9f2681f 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -10,7 +10,7 @@ // License for the specific language governing permissions and limitations // under the License. use petgraph::graph::NodeIndex; -// use rs::NullGraph; +use super::NullGraph; use hashbrown::HashSet; use petgraph::algo; use std::collections::VecDeque; @@ -166,7 +166,7 @@ where num_components } -pub fn strongly_connected_components(graph: G) -> Vec> +pub fn strongly_connected_components(graph: G) -> Vec> where G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, G::NodeId: Eq + Hash, @@ -177,14 +177,14 @@ where .collect() } -pub fn is_connected(graph: G) -> Vec> +pub fn is_connected(graph: G) -> Result where G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, G::NodeId: Eq + Hash, { match graph.node_identifiers().next() { Some(node) => { - let component = node_connected_component(graph, node.index())?; + let component = node_connected_component(graph, node.index()); Ok(component.len() == graph.node_count()) } None => Err(NullGraph::new_err("Invalid operation on a NullGraph")), From 179f0f32bf02a2ecc5138ac7f9510f7a4c1eb15e Mon Sep 17 00:00:00 2001 From: Kris Date: Thu, 25 Aug 2022 23:26:23 +0300 Subject: [PATCH 05/10] fix output of functions --- rustworkx-core/src/connectivity/conn_components.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 2b9f2681f..8aa7cf7f8 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -10,7 +10,7 @@ // License for the specific language governing permissions and limitations // under the License. use petgraph::graph::NodeIndex; -use super::NullGraph; +// use super::NullGraph; use hashbrown::HashSet; use petgraph::algo; use std::collections::VecDeque; @@ -191,7 +191,7 @@ where } } -pub fn node_connected_component(graph: G, node: usize) -> HashSet +pub fn node_connected_component(graph: G, node: usize) -> Result, InvalidNode > where G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, G::NodeId: Eq + Hash, From efe8287c7f66427e8c392fb7a80a8a5907e2f97c Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 6 Sep 2022 21:38:24 +0300 Subject: [PATCH 06/10] implement feedback --- .../src/connectivity/conn_components.rs | 48 +++++++------------ rustworkx-core/src/connectivity/mod.rs | 3 +- src/connectivity/mod.rs | 5 +- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 8aa7cf7f8..4ea95122e 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -9,14 +9,15 @@ // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. -use petgraph::graph::NodeIndex; -// use super::NullGraph; use hashbrown::HashSet; use petgraph::algo; use std::collections::VecDeque; use std::hash::Hash; // use super::digraph; -use petgraph::visit::{GraphProp, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable}; +use petgraph::visit::{ + GraphProp, IntoNeighborsDirected, IntoNodeIdentifiers, NodeCount, NodeIndexable, VisitMap, + Visitable, +}; use petgraph::{Incoming, Outgoing}; /// Given an graph, a node in the graph, and a visit_map, /// return the set of nodes connected to the given node @@ -166,48 +167,31 @@ where num_components } -pub fn strongly_connected_components(graph: G) -> Vec> +pub fn is_connected(graph: G) -> bool where - G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, - G::NodeId: Eq + Hash, -{ - algo::kosaraju_scc(&graph) - .iter() - .map(|x| x.iter().map(|id| id.index()).collect()) - .collect() -} - -pub fn is_connected(graph: G) -> Result -where - G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, + G: GraphProp + + IntoNeighborsDirected + + Visitable + + IntoNodeIdentifiers + + NodeIndexable + + NodeCount, G::NodeId: Eq + Hash, { match graph.node_identifiers().next() { Some(node) => { - let component = node_connected_component(graph, node.index()); - Ok(component.len() == graph.node_count()) + let component = node_connected_component(graph, node.to_index()); + component.len() == graph.node_count() } - None => Err(NullGraph::new_err("Invalid operation on a NullGraph")), + None => false, } } -pub fn node_connected_component(graph: G, node: usize) -> Result, InvalidNode > +pub fn node_connected_component(graph: G, node: G::NodeId) -> HashSet where G: GraphProp + IntoNeighborsDirected + Visitable + IntoNodeIdentifiers, G::NodeId: Eq + Hash, { - let node = NodeIndex::new(node); - - if !graph.contains_node(node) { - return Err(InvalidNode::new_err( - "The input index for 'node' is not a valid node index", - )); - } - - Ok(bfs_undirected(&graph, node, &mut graph.visit_map()) - .into_iter() - .map(|x| x.index()) - .collect()) + bfs_undirected(&graph, node, &mut graph.visit_map()) } #[cfg(test)] diff --git a/rustworkx-core/src/connectivity/mod.rs b/rustworkx-core/src/connectivity/mod.rs index 49f502e02..7d1d1e999 100644 --- a/rustworkx-core/src/connectivity/mod.rs +++ b/rustworkx-core/src/connectivity/mod.rs @@ -23,6 +23,5 @@ pub use chain::chain_decomposition; pub use conn_components::bfs_undirected; pub use conn_components::connected_components; pub use conn_components::is_connected; -pub use conn_components::node_connected_components; +// pub use conn_components::node_connected_components; pub use conn_components::number_connected_components; -pub use conn_components::strongly_connected_components; diff --git a/src/connectivity/mod.rs b/src/connectivity/mod.rs index 82e5b93b5..83d03dcd8 100644 --- a/src/connectivity/mod.rs +++ b/src/connectivity/mod.rs @@ -138,7 +138,10 @@ pub fn cycle_basis(graph: &graph::PyGraph, root: Option) -> Vec Vec> { - connectivity::strongly_connected_components(&graph.graph) + algo::kosaraju_scc(&graph.graph) + .iter() + .map(|x| x.iter().map(|id| id.index()).collect()) + .collect() } /// Return the first cycle encountered during DFS of a given PyDiGraph, From 44e0d1dace2cf0561288c01af9669fadb6ec9146 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 15 Nov 2022 23:30:20 +0000 Subject: [PATCH 07/10] changes --- rustworkx-core/src/connectivity/conn_components.rs | 3 +-- rustworkx-core/src/connectivity/mod.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 4ea95122e..81b7f288e 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -10,7 +10,6 @@ // License for the specific language governing permissions and limitations // under the License. use hashbrown::HashSet; -use petgraph::algo; use std::collections::VecDeque; use std::hash::Hash; // use super::digraph; @@ -179,7 +178,7 @@ where { match graph.node_identifiers().next() { Some(node) => { - let component = node_connected_component(graph, node.to_index()); + let component = node_connected_component(graph, node); component.len() == graph.node_count() } None => false, diff --git a/rustworkx-core/src/connectivity/mod.rs b/rustworkx-core/src/connectivity/mod.rs index 7d1d1e999..41f0ec025 100644 --- a/rustworkx-core/src/connectivity/mod.rs +++ b/rustworkx-core/src/connectivity/mod.rs @@ -23,5 +23,5 @@ pub use chain::chain_decomposition; pub use conn_components::bfs_undirected; pub use conn_components::connected_components; pub use conn_components::is_connected; -// pub use conn_components::node_connected_components; +pub use conn_components::node_connected_components; pub use conn_components::number_connected_components; From eb8c83a20984449edb9b6a01c89ad0b485422413 Mon Sep 17 00:00:00 2001 From: Kris Date: Wed, 16 Nov 2022 00:15:44 +0000 Subject: [PATCH 08/10] fix mod and add test --- rustworkx-core/src/connectivity/conn_components.rs | 14 ++++++++++++++ rustworkx-core/src/connectivity/mod.rs | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index 81b7f288e..e1940a747 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -204,12 +204,26 @@ mod test_conn_components { use crate::connectivity::{bfs_undirected, connected_components, number_connected_components}; + use super::is_connected; + #[test] fn test_number_connected() { let graph = Graph::<(), (), Undirected>::from_edges([(0, 1), (1, 2), (3, 4)]); assert_eq!(number_connected_components(&graph), 2); } + #[test] + fn test_is_connected() { + let graph = Graph::<(), (), Directed>::from_edges([(0,1), (1,2), (2,3)]); + assert_eq!(is_connected(&graph), true); + } + + #[test] + fn test_is_not_connected(){ + let disconnected_graph = Graph::<(), (), Directed>::from_edges([(0,1), (3,4)]); + assert_eq!(is_connected(&disconnected_graph), false); + } + #[test] fn test_number_node_holes() { let mut graph = Graph::<(), (), Directed>::from_edges([(0, 1), (1, 2)]); diff --git a/rustworkx-core/src/connectivity/mod.rs b/rustworkx-core/src/connectivity/mod.rs index 41f0ec025..e014529e0 100644 --- a/rustworkx-core/src/connectivity/mod.rs +++ b/rustworkx-core/src/connectivity/mod.rs @@ -23,5 +23,5 @@ pub use chain::chain_decomposition; pub use conn_components::bfs_undirected; pub use conn_components::connected_components; pub use conn_components::is_connected; -pub use conn_components::node_connected_components; +pub use conn_components::node_connected_component; pub use conn_components::number_connected_components; From 9bb8a1a90953e0f1eed4e5d44d7fea06c575a4bb Mon Sep 17 00:00:00 2001 From: Kris Date: Wed, 16 Nov 2022 23:21:49 +0000 Subject: [PATCH 09/10] node connected component test --- rustworkx-core/src/connectivity/conn_components.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index e1940a747..b1f01b8ee 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -202,7 +202,7 @@ mod test_conn_components { use petgraph::{Directed, Undirected}; use std::iter::FromIterator; - use crate::connectivity::{bfs_undirected, connected_components, number_connected_components}; + use crate::connectivity::{bfs_undirected, node_connected_component,connected_components, number_connected_components}; use super::is_connected; @@ -224,6 +224,17 @@ mod test_conn_components { assert_eq!(is_connected(&disconnected_graph), false); } + #[test] + fn test_node_connected_components(){ + let graph = Graph::<(), (), Directed>::from_edges(&[(0, 1), (1, 2)]); + let node_idx: NodeIndex = NodeIndex::new(3); + let expected = HashSet::from_iter([ndx(0), ndx(1), ndx(2)]); + let component = node_connected_component(&graph, node_idx); + assert_eq!(component, expected); + + } + + #[test] fn test_number_node_holes() { let mut graph = Graph::<(), (), Directed>::from_edges([(0, 1), (1, 2)]); From 99067b3b4a97d4205002c0fb47f5432f989a3af8 Mon Sep 17 00:00:00 2001 From: Kris Date: Thu, 17 Nov 2022 06:53:01 +0000 Subject: [PATCH 10/10] test works --- rustworkx-core/src/connectivity/conn_components.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rustworkx-core/src/connectivity/conn_components.rs b/rustworkx-core/src/connectivity/conn_components.rs index b1f01b8ee..6e6e53e0b 100644 --- a/rustworkx-core/src/connectivity/conn_components.rs +++ b/rustworkx-core/src/connectivity/conn_components.rs @@ -226,9 +226,9 @@ mod test_conn_components { #[test] fn test_node_connected_components(){ - let graph = Graph::<(), (), Directed>::from_edges(&[(0, 1), (1, 2)]); + let graph = Graph::<(), (), Directed>::from_edges(&[(0, 1), (1, 2), (2, 3), (4,5)]); let node_idx: NodeIndex = NodeIndex::new(3); - let expected = HashSet::from_iter([ndx(0), ndx(1), ndx(2)]); + let expected: HashSet = HashSet::from_iter([ndx(3),ndx(0),ndx(1),ndx(2)]); let component = node_connected_component(&graph, node_idx); assert_eq!(component, expected);