#[cfg(feature = "hgraph")]
use crate::core::view::NodeId;
#[cfg(feature = "hgraph")]
use crate::hgraph::h_graph::HeterogeneousGraph;
#[cfg(feature = "hgraph")]
use std::fmt::Debug;
#[cfg(feature = "hgraph")]
use std::hash::Hash;
#[derive(Debug, thiserror::Error)]
pub enum ConnectivityError {
#[error("Invalid node reference: node ID {0} not found")]
InvalidNodeReference(NodeId),
}
#[cfg(feature = "hgraph")]
impl From<crate::core::TraversalError> for ConnectivityError {
fn from(error: crate::core::TraversalError) -> Self {
match error {
crate::core::TraversalError::InvalidNode(id) => {
ConnectivityError::InvalidNodeReference(id)
}
}
}
}
pub type Result<T> = std::result::Result<T, ConnectivityError>;
#[cfg(feature = "hgraph")]
#[deprecated(
since = "3.0.0",
note = "superseded by `xgraph::core::Connectivity`; the `*_by_types` variants become \
`graph.of_edge_data(&[..])` followed by the ordinary method"
)]
pub trait HeteroConnectivity<W, N, E> {
fn find_weakly_connected_components(&self) -> Result<Vec<Vec<NodeId>>>;
fn find_strongly_connected_components(&self) -> Result<Vec<Vec<NodeId>>>;
fn find_connected_components(&self) -> Result<Vec<Vec<NodeId>>> {
if self.is_directed() {
self.find_strongly_connected_components()
} else {
self.find_weakly_connected_components()
}
}
fn is_weakly_connected(&self) -> Result<bool>;
fn is_strongly_connected(&self) -> Result<bool>;
fn is_connected(&self) -> Result<bool> {
if self.is_directed() {
self.is_strongly_connected()
} else {
self.is_weakly_connected()
}
}
fn find_weakly_connected_components_by_types(
&self,
allowed_edge_types: &[E],
) -> Result<Vec<Vec<NodeId>>>;
fn find_strongly_connected_components_by_types(
&self,
allowed_edge_types: &[E],
) -> Result<Vec<Vec<NodeId>>>;
fn find_connected_components_by_types(
&self,
allowed_edge_types: &[E],
) -> Result<Vec<Vec<NodeId>>> {
if self.is_directed() {
self.find_strongly_connected_components_by_types(allowed_edge_types)
} else {
self.find_weakly_connected_components_by_types(allowed_edge_types)
}
}
fn is_weakly_connected_by_types(&self, allowed_edge_types: &[E]) -> Result<bool>;
fn is_strongly_connected_by_types(&self, allowed_edge_types: &[E]) -> Result<bool>;
fn is_connected_by_types(&self, allowed_edge_types: &[E]) -> Result<bool> {
if self.is_directed() {
self.is_strongly_connected_by_types(allowed_edge_types)
} else {
self.is_weakly_connected_by_types(allowed_edge_types)
}
}
fn is_directed(&self) -> bool;
}
#[cfg(feature = "hgraph")]
#[cfg(feature = "hgraph")]
#[allow(deprecated)]
impl<W, N, E> HeteroConnectivity<W, N, E> for HeterogeneousGraph<W, N, E>
where
W: Copy + Default + PartialEq + Debug,
N: Clone + Eq + Hash + Debug + crate::hgraph::h_node::NodeType,
E: Clone + Default + Debug + PartialEq + crate::hgraph::h_edge::EdgeType,
{
fn find_weakly_connected_components(&self) -> Result<Vec<Vec<NodeId>>> {
crate::core::Connectivity::weakly_connected_components(self)
.map_err(ConnectivityError::from)
}
fn find_strongly_connected_components(&self) -> Result<Vec<Vec<NodeId>>> {
crate::core::Connectivity::strongly_connected_components(self)
.map_err(ConnectivityError::from)
}
fn is_weakly_connected(&self) -> Result<bool> {
crate::core::Connectivity::is_weakly_connected(self).map_err(ConnectivityError::from)
}
fn is_strongly_connected(&self) -> Result<bool> {
crate::core::Connectivity::is_strongly_connected(self).map_err(ConnectivityError::from)
}
fn find_weakly_connected_components_by_types(
&self,
allowed_edge_types: &[E],
) -> Result<Vec<Vec<NodeId>>> {
crate::core::Connectivity::weakly_connected_components(
&self.of_edge_data(allowed_edge_types),
)
.map_err(ConnectivityError::from)
}
fn find_strongly_connected_components_by_types(
&self,
allowed_edge_types: &[E],
) -> Result<Vec<Vec<NodeId>>> {
crate::core::Connectivity::strongly_connected_components(
&self.of_edge_data(allowed_edge_types),
)
.map_err(ConnectivityError::from)
}
fn is_weakly_connected_by_types(&self, allowed_edge_types: &[E]) -> Result<bool> {
crate::core::Connectivity::is_weakly_connected(&self.of_edge_data(allowed_edge_types))
.map_err(ConnectivityError::from)
}
fn is_strongly_connected_by_types(&self, allowed_edge_types: &[E]) -> Result<bool> {
crate::core::Connectivity::is_strongly_connected(&self.of_edge_data(allowed_edge_types))
.map_err(ConnectivityError::from)
}
fn is_directed(&self) -> bool {
crate::core::GraphView::is_directed(self)
}
}
#[cfg(test)]
#[cfg(feature = "hgraph")]
#[allow(deprecated)]
mod tests {
use super::*;
use crate::hgraph::h_edge::EdgeType;
use crate::hgraph::h_node::NodeType;
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct TestNode(String);
impl NodeType for TestNode {
fn as_string(&self) -> String {
self.0.clone()
}
}
#[derive(Clone, Debug, Default, PartialEq)]
struct TestEdge(String);
impl EdgeType for TestEdge {
fn as_string(&self) -> String {
self.0.clone()
}
}
#[test]
fn test_weakly_connected_components() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(false);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 1.0, TestEdge("bridge".to_string()))
.unwrap();
let wcc = graph.find_weakly_connected_components().unwrap();
assert_eq!(wcc.len(), 1);
assert_eq!(wcc[0], vec![n0, n1, n2]);
}
#[test]
fn test_strongly_connected_components() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 1.0, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n2, n0, 1.0, TestEdge("path".to_string()))
.unwrap();
let scc = graph.find_strongly_connected_components().unwrap();
assert_eq!(scc.len(), 1);
assert_eq!(scc[0], vec![n0, n1, n2]);
}
#[test]
fn test_weakly_connected_components_by_types() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(false);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 1.0, TestEdge("bridge".to_string()))
.unwrap();
let allowed_edge_types = vec![TestEdge("road".to_string())];
let wcc = graph
.find_weakly_connected_components_by_types(&allowed_edge_types)
.unwrap();
assert_eq!(wcc.len(), 2);
assert_eq!(wcc[0], vec![n0, n1]);
assert_eq!(wcc[1], vec![n2]);
}
#[test]
fn test_strongly_connected_components_by_types() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n0, 1.0, TestEdge("path".to_string()))
.unwrap();
graph
.add_edge(n0, n2, 1.0, TestEdge("path".to_string()))
.unwrap();
graph
.add_edge(n2, n0, 1.0, TestEdge("path".to_string()))
.unwrap();
let allowed_edge_types = vec![TestEdge("road".to_string()), TestEdge("path".to_string())];
let scc = graph
.find_strongly_connected_components_by_types(&allowed_edge_types)
.unwrap();
assert_eq!(scc.len(), 1);
assert_eq!(scc[0], vec![n0, n1, n2]);
}
#[test]
fn test_is_weakly_connected() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(false);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
assert!(graph.is_weakly_connected().unwrap());
}
#[test]
fn test_is_strongly_connected() {
let mut graph = HeterogeneousGraph::<f64, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 1.0, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 1.0, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n2, n0, 1.0, TestEdge("path".to_string()))
.unwrap();
assert!(graph.is_strongly_connected().unwrap());
}
}