mod centrality;
mod community;
mod components;
mod cycles;
mod pagerank;
mod structural;
pub use centrality::{
BetweennessCentrality, BetweennessResult, ClosenessCentrality, ClosenessResult,
DegreeCentrality, DegreeCentralityResult, EigenvectorCentrality, EigenvectorResult,
};
pub use community::{CommunitiesResult, Community, LabelPropagation, Louvain, LouvainResult};
pub use components::{Component, ComponentsResult, ConnectedComponents};
pub use cycles::{Cycle, CycleDetector, CyclesResult};
pub use pagerank::{PageRank, PageRankResult, PersonalizedPageRank};
pub use structural::{
ClusteringCoefficient, ClusteringResult, HITSResult, SCCResult, StronglyConnectedComponents,
TriangleCounting, TriangleResult, WCCResult, WeaklyConnectedComponents, HITS,
};
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::engine::graph_store::GraphStore;
fn create_test_graph() -> GraphStore {
let graph = GraphStore::new();
let _ = graph.add_node_with_label("A", "Node A", "host");
let _ = graph.add_node_with_label("B", "Node B", "host");
let _ = graph.add_node_with_label("C", "Node C", "host");
let _ = graph.add_node_with_label("D", "Node D", "host");
let _ = graph.add_edge_with_label("A", "B", "connects_to", 1.0);
let _ = graph.add_edge_with_label("A", "C", "connects_to", 1.0);
let _ = graph.add_edge_with_label("B", "D", "connects_to", 1.0);
let _ = graph.add_edge_with_label("C", "D", "connects_to", 1.0);
graph
}
fn create_cycle_graph() -> GraphStore {
let graph = GraphStore::new();
let _ = graph.add_node_with_label("A", "Node A", "host");
let _ = graph.add_node_with_label("B", "Node B", "host");
let _ = graph.add_node_with_label("C", "Node C", "host");
let _ = graph.add_edge_with_label("A", "B", "connects_to", 1.0);
let _ = graph.add_edge_with_label("B", "C", "connects_to", 1.0);
let _ = graph.add_edge_with_label("C", "A", "connects_to", 1.0);
graph
}
fn create_disconnected_graph() -> GraphStore {
let graph = GraphStore::new();
let _ = graph.add_node_with_label("A", "Node A", "host");
let _ = graph.add_node_with_label("B", "Node B", "host");
let _ = graph.add_edge_with_label("A", "B", "connects_to", 1.0);
let _ = graph.add_node_with_label("C", "Node C", "host");
let _ = graph.add_node_with_label("D", "Node D", "host");
let _ = graph.add_node_with_label("E", "Node E", "host");
let _ = graph.add_edge_with_label("C", "D", "connects_to", 1.0);
let _ = graph.add_edge_with_label("D", "E", "connects_to", 1.0);
let _ = graph.add_node_with_label("F", "Node F", "host");
graph
}
#[test]
fn test_pagerank_empty_graph() {
let graph = GraphStore::new();
let result = PageRank::new().run(&graph);
assert!(result.scores.is_empty());
assert!(result.converged);
}
#[test]
fn test_pagerank_single_node() {
let graph = GraphStore::new();
let _ = graph.add_node_with_label("A", "Node A", "host");
let result = PageRank::new().run(&graph);
assert_eq!(result.scores.len(), 1);
assert!((result.scores["A"] - 1.0).abs() < 0.01);
}
#[test]
fn test_pagerank_diamond() {
let graph = create_test_graph();
let result = PageRank::new().run(&graph);
let top = result.top(4);
assert_eq!(top[0].0, "D");
assert!(result.converged);
}
#[test]
fn test_pagerank_top_n() {
let graph = create_test_graph();
let result = PageRank::new().run(&graph);
let top2 = result.top(2);
assert_eq!(top2.len(), 2);
}
#[test]
fn test_components_single_component() {
let graph = create_test_graph();
let result = ConnectedComponents::find(&graph);
assert_eq!(result.count, 1);
assert_eq!(result.components[0].size, 4);
}
#[test]
fn test_components_disconnected() {
let graph = create_disconnected_graph();
let result = ConnectedComponents::find(&graph);
assert_eq!(result.count, 3);
assert_eq!(result.largest().unwrap().size, 3);
}
#[test]
fn test_components_filter_by_size() {
let graph = create_disconnected_graph();
let result = ConnectedComponents::find(&graph);
let large = result.filter_by_size(2);
assert_eq!(large.len(), 2); }
#[test]
fn test_components_component_of() {
let graph = create_disconnected_graph();
let result = ConnectedComponents::find(&graph);
let comp = result.component_of("D").unwrap();
assert!(comp.nodes.contains(&"C".to_string()));
assert!(comp.nodes.contains(&"E".to_string()));
}
#[test]
fn test_betweenness_empty() {
let graph = GraphStore::new();
let result = BetweennessCentrality::compute(&graph, false);
assert!(result.scores.is_empty());
}
#[test]
fn test_betweenness_diamond() {
let graph = create_test_graph();
let result = BetweennessCentrality::compute(&graph, false);
assert!(result.score("A").unwrap() < result.score("B").unwrap());
}
#[test]
fn test_betweenness_normalized() {
let graph = create_test_graph();
let result = BetweennessCentrality::compute(&graph, true);
for score in result.scores.values() {
assert!(*score >= 0.0 && *score <= 1.0);
}
}
#[test]
fn test_communities_connected() {
let graph = create_test_graph();
let result = LabelPropagation::new().run(&graph);
assert!(result.communities.len() <= 2);
}
#[test]
fn test_communities_disconnected() {
let graph = create_disconnected_graph();
let result = LabelPropagation::new().run(&graph);
assert!(result.communities.len() >= 3);
}
#[test]
fn test_communities_convergence() {
let graph = create_test_graph();
let result = LabelPropagation::new().max_iterations(50).run(&graph);
assert!(result.converged || result.iterations == 50);
}
#[test]
fn test_cycles_no_cycle() {
let graph = create_test_graph(); let result = CycleDetector::new().find(&graph);
assert!(result.cycles.is_empty());
}
#[test]
fn test_cycles_simple_cycle() {
let graph = create_cycle_graph();
let result = CycleDetector::new().find(&graph);
assert!(!result.cycles.is_empty());
assert_eq!(result.cycles[0].length, 3); }
#[test]
fn test_cycles_max_length() {
let graph = create_cycle_graph();
let result = CycleDetector::new().max_length(2).find(&graph);
assert!(result.cycles.is_empty());
}
#[test]
fn test_cycles_max_cycles_limit() {
let graph = create_cycle_graph();
let result = CycleDetector::new().max_cycles(0).find(&graph);
assert!(result.cycles.is_empty());
assert!(result.limit_reached);
}
}