1pub mod coloring;
22pub mod community;
23pub mod connectivity;
24pub mod decomposition;
25pub mod flow;
26pub mod hypergraph;
27pub mod isomorphism;
28pub mod matching;
29pub mod motifs;
30pub mod paths;
31pub mod properties;
32pub mod random_walk;
33pub mod shortest_path;
34pub mod similarity;
35pub mod transformations;
36pub mod traversal;
37
38pub use coloring::*;
40pub use community::{
42 fluid_communities_result,
44 girvan_newman_communities_result,
46 girvan_newman_result,
47 greedy_modularity_optimization_result,
48 hierarchical_communities_result,
49 infomap_communities,
50 label_propagation_result,
51 louvain_communities_result,
52 modularity,
53 modularity_optimization_result,
54 CommunityResult,
56 CommunityStructure,
57 DendrogramLevel,
58 GirvanNewmanConfig,
59 GirvanNewmanResult,
60 InfomapResult,
61};
62
63#[cfg(feature = "parallel")]
64pub use community::{
65 parallel_label_propagation_result, parallel_louvain_communities_result, parallel_modularity,
66};
67
68#[deprecated(since = "0.1.0", note = "Use `louvain_communities_result` instead")]
70#[allow(deprecated)]
71pub use community::louvain_communities;
72
73#[deprecated(since = "0.1.0", note = "Use `label_propagation_result` instead")]
74#[allow(deprecated)]
75pub use community::label_propagation;
76
77#[deprecated(since = "0.1.0", note = "Use `fluid_communities_result` instead")]
78#[allow(deprecated)]
79pub use community::fluid_communities;
80
81#[deprecated(
82 since = "0.1.0",
83 note = "Use `hierarchical_communities_result` instead"
84)]
85#[allow(deprecated)]
86pub use community::hierarchical_communities;
87
88#[deprecated(since = "0.1.0", note = "Use `modularity_optimization_result` instead")]
89#[allow(deprecated)]
90pub use community::modularity_optimization;
91
92#[deprecated(
93 since = "0.1.0",
94 note = "Use `greedy_modularity_optimization_result` instead"
95)]
96#[allow(deprecated)]
97pub use community::greedy_modularity_optimization;
98
99#[deprecated(
100 since = "0.1.0",
101 note = "Use `parallel_louvain_communities_result` instead"
102)]
103#[allow(deprecated)]
104pub use community::parallel_louvain_communities;
105pub use connectivity::*;
106pub use decomposition::*;
107pub use flow::{
108 capacity_scaling_max_flow, dinic_max_flow, dinic_max_flow_full, edmonds_karp_max_flow,
109 ford_fulkerson_max_flow, hopcroft_karp, isap_max_flow, min_cost_max_flow,
110 min_cost_max_flow_graph, minimum_cut, minimum_st_cut, multi_commodity_flow,
111 multi_source_multi_sink_max_flow, parallel_max_flow, push_relabel_max_flow,
112 push_relabel_max_flow_full, CostEdge, HopcroftKarpResult, MaxFlowResult, MinCostFlowResult,
113 MultiCommodityFlowResult,
114};
115pub use hypergraph::*;
116pub use isomorphism::*;
117pub use matching::*;
118pub use motifs::*;
119pub use paths::*;
120pub use properties::*;
121pub use random_walk::*;
122pub use shortest_path::*;
123pub use similarity::*;
124pub use transformations::*;
125pub use traversal::*;
126
127use crate::base::{DiGraph, EdgeWeight, Graph, IndexType, Node};
130use crate::error::{GraphError, Result};
131use petgraph::algo::toposort as petgraph_toposort;
132use petgraph::visit::EdgeRef;
133use petgraph::Direction;
134use scirs2_core::ndarray::{Array1, Array2};
135use std::cmp::Ordering;
136use std::collections::{HashMap, VecDeque};
137
138#[allow(dead_code)]
143pub fn minimum_spanning_tree<N, E, Ix>(
144 graph: &Graph<N, E, Ix>,
145) -> Result<Vec<crate::base::Edge<N, E>>>
146where
147 N: Node + std::fmt::Debug,
148 E: EdgeWeight + Into<f64> + std::cmp::PartialOrd,
149 Ix: petgraph::graph::IndexType,
150{
151 let mut edges: Vec<_> = graph
153 .inner()
154 .edge_references()
155 .map(|e| {
156 let source = graph.inner()[e.source()].clone();
157 let target = graph.inner()[e.target()].clone();
158 let weight = e.weight().clone();
159 (source, target, weight)
160 })
161 .collect();
162
163 edges.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Ordering::Equal));
164
165 let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
167 let mut parent: HashMap<N, N> = nodes.iter().map(|n| (n.clone(), n.clone())).collect();
168 let mut rank: HashMap<N, usize> = nodes.iter().map(|n| (n.clone(), 0)).collect();
169
170 fn find<N: Node>(parent: &mut HashMap<N, N>, node: &N) -> N {
171 if parent[node] != *node {
172 let root = find(parent, &parent[node].clone());
173 parent.insert(node.clone(), root.clone());
174 }
175 parent[node].clone()
176 }
177
178 fn union<N: Node>(
179 parent: &mut HashMap<N, N>,
180 rank: &mut HashMap<N, usize>,
181 x: &N,
182 y: &N,
183 ) -> bool {
184 let root_x = find(parent, x);
185 let root_y = find(parent, y);
186
187 if root_x == root_y {
188 return false; }
190
191 match rank[&root_x].cmp(&rank[&root_y]) {
193 Ordering::Less => {
194 parent.insert(root_x, root_y);
195 }
196 Ordering::Greater => {
197 parent.insert(root_y, root_x);
198 }
199 Ordering::Equal => {
200 parent.insert(root_y, root_x.clone());
201 *rank.get_mut(&root_x).expect("Operation failed") += 1;
202 }
203 }
204 true
205 }
206
207 let mut mst = Vec::new();
208
209 for (source, target, weight) in edges {
210 if union(&mut parent, &mut rank, &source, &target) {
211 mst.push(crate::base::Edge {
212 source,
213 target,
214 weight,
215 });
216 }
217 }
218
219 Ok(mst)
220}
221
222#[allow(dead_code)]
227pub fn topological_sort<N, E, Ix>(graph: &DiGraph<N, E, Ix>) -> Result<Vec<N>>
228where
229 N: Node + std::fmt::Debug,
230 E: EdgeWeight,
231 Ix: IndexType,
232{
233 match petgraph_toposort(graph.inner(), None) {
235 Ok(indices) => {
236 let sorted_nodes = indices
237 .into_iter()
238 .map(|idx| graph.inner()[idx].clone())
239 .collect();
240 Ok(sorted_nodes)
241 }
242 Err(_) => Err(GraphError::CycleDetected {
243 start_node: "unknown".to_string(),
244 cycle_length: 0,
245 }),
246 }
247}
248
249#[allow(dead_code)]
253pub fn pagerank<N, E, Ix>(
254 graph: &DiGraph<N, E, Ix>,
255 damping_factor: f64,
256 tolerance: f64,
257 max_iterations: usize,
258) -> HashMap<N, f64>
259where
260 N: Node + std::fmt::Debug,
261 E: EdgeWeight,
262 Ix: IndexType,
263{
264 let nodes: Vec<_> = graph.inner().node_indices().collect();
265 let n = nodes.len();
266
267 if n == 0 {
268 return HashMap::new();
269 }
270
271 let mut pr = vec![1.0 / n as f64; n];
273 let mut new_pr = vec![0.0; n];
274
275 let node_to_idx: HashMap<_, _> = nodes.iter().enumerate().map(|(i, &n)| (n, i)).collect();
277
278 for _ in 0..max_iterations {
280 for pr in new_pr.iter_mut().take(n) {
282 *pr = (1.0 - damping_factor) / n as f64;
283 }
284
285 for (i, &node_idx) in nodes.iter().enumerate() {
287 let out_degree = graph
288 .inner()
289 .edges_directed(node_idx, Direction::Outgoing)
290 .count();
291
292 if out_degree > 0 {
293 let contribution = damping_factor * pr[i] / out_degree as f64;
294
295 for edge in graph.inner().edges_directed(node_idx, Direction::Outgoing) {
296 if let Some(&j) = node_to_idx.get(&edge.target()) {
297 new_pr[j] += contribution;
298 }
299 }
300 } else {
301 let contribution = damping_factor * pr[i] / n as f64;
303 for pr_val in new_pr.iter_mut().take(n) {
304 *pr_val += contribution;
305 }
306 }
307 }
308
309 let diff: f64 = pr
311 .iter()
312 .zip(&new_pr)
313 .map(|(old, new)| (old - new).abs())
314 .sum();
315
316 std::mem::swap(&mut pr, &mut new_pr);
318
319 if diff < tolerance {
320 break;
321 }
322 }
323
324 nodes
326 .iter()
327 .enumerate()
328 .map(|(i, &node_idx)| (graph.inner()[node_idx].clone(), pr[i]))
329 .collect()
330}
331
332#[allow(dead_code)]
336pub fn betweenness_centrality<N, E, Ix>(
337 graph: &Graph<N, E, Ix>,
338 normalized: bool,
339) -> HashMap<N, f64>
340where
341 N: Node + std::fmt::Debug,
342 E: EdgeWeight,
343 Ix: IndexType,
344{
345 let node_indices: Vec<_> = graph.inner().node_indices().collect();
346 let nodes: Vec<N> = node_indices
347 .iter()
348 .map(|&idx| graph.inner()[idx].clone())
349 .collect();
350 let n = nodes.len();
351 let mut centrality: HashMap<N, f64> = nodes.iter().map(|n| (n.clone(), 0.0)).collect();
352
353 for s in &nodes {
355 let mut stack = Vec::new();
357 let mut paths: HashMap<N, Vec<N>> = HashMap::new();
358 let mut sigma: HashMap<N, f64> = nodes.iter().map(|n| (n.clone(), 0.0)).collect();
359 let mut dist: HashMap<N, Option<f64>> = nodes.iter().map(|n| (n.clone(), None)).collect();
360
361 sigma.insert(s.clone(), 1.0);
362 dist.insert(s.clone(), Some(0.0));
363
364 let mut queue = VecDeque::new();
365 queue.push_back(s.clone());
366
367 while let Some(v) = queue.pop_front() {
369 stack.push(v.clone());
370
371 if let Ok(neighbors) = graph.neighbors(&v) {
372 for w in neighbors {
373 if dist[&w].is_none() {
375 dist.insert(w.clone(), Some(dist[&v].expect("Operation failed") + 1.0));
376 queue.push_back(w.clone());
377 }
378
379 if dist[&w] == Some(dist[&v].expect("Operation failed") + 1.0) {
381 *sigma.get_mut(&w).expect("Operation failed") += sigma[&v];
382 paths.entry(w.clone()).or_default().push(v.clone());
383 }
384 }
385 }
386 }
387
388 let mut delta: HashMap<N, f64> = nodes.iter().map(|n| (n.clone(), 0.0)).collect();
390
391 while let Some(w) = stack.pop() {
392 if let Some(predecessors) = paths.get(&w) {
393 for v in predecessors {
394 *delta.get_mut(v).expect("Operation failed") +=
395 (sigma[v] / sigma[&w]) * (1.0 + delta[&w]);
396 }
397 }
398
399 if w != *s {
400 *centrality.get_mut(&w).expect("Operation failed") += delta[&w];
401 }
402 }
403 }
404
405 if normalized && n > 2 {
407 let scale = 1.0 / ((n - 1) * (n - 2)) as f64;
408 for value in centrality.values_mut() {
409 *value *= scale;
410 }
411 }
412
413 centrality
414}
415
416#[allow(dead_code)]
420pub fn closeness_centrality<N, E, Ix>(graph: &Graph<N, E, Ix>, normalized: bool) -> HashMap<N, f64>
421where
422 N: Node + std::fmt::Debug,
423 E: EdgeWeight
424 + Into<f64>
425 + scirs2_core::numeric::Zero
426 + scirs2_core::numeric::One
427 + std::ops::Add<Output = E>
428 + PartialOrd
429 + std::marker::Copy
430 + std::fmt::Debug
431 + std::default::Default,
432 Ix: IndexType,
433{
434 let node_indices: Vec<_> = graph.inner().node_indices().collect();
435 let nodes: Vec<N> = node_indices
436 .iter()
437 .map(|&idx| graph.inner()[idx].clone())
438 .collect();
439 let n = nodes.len();
440 let mut centrality = HashMap::new();
441
442 for node in &nodes {
443 let mut total_distance = 0.0;
444 let mut reachable_count = 0;
445
446 for other in &nodes {
448 if node != other {
449 if let Ok(Some(path)) = dijkstra_path(graph, node, other) {
450 let distance: f64 = path.total_weight.into();
451 total_distance += distance;
452 reachable_count += 1;
453 }
454 }
455 }
456
457 if reachable_count > 0 {
458 let closeness = reachable_count as f64 / total_distance;
459 let value = if normalized && n > 1 {
460 closeness * (reachable_count as f64 / (n - 1) as f64)
461 } else {
462 closeness
463 };
464 centrality.insert(node.clone(), value);
465 } else {
466 centrality.insert(node.clone(), 0.0);
467 }
468 }
469
470 centrality
471}
472
473#[allow(dead_code)]
477pub fn eigenvector_centrality<N, E, Ix>(
478 graph: &Graph<N, E, Ix>,
479 max_iter: usize,
480 tolerance: f64,
481) -> Result<HashMap<N, f64>>
482where
483 N: Node + std::fmt::Debug,
484 E: EdgeWeight + Into<f64>,
485 Ix: IndexType,
486{
487 let node_indices: Vec<_> = graph.inner().node_indices().collect();
488 let nodes: Vec<N> = node_indices
489 .iter()
490 .map(|&idx| graph.inner()[idx].clone())
491 .collect();
492 let n = nodes.len();
493
494 if n == 0 {
495 return Ok(HashMap::new());
496 }
497
498 let mut adj_matrix = Array2::<f64>::zeros((n, n));
500 for (i, node_i) in nodes.iter().enumerate() {
501 for (j, node_j) in nodes.iter().enumerate() {
502 if let Ok(weight) = graph.edge_weight(node_i, node_j) {
503 adj_matrix[[i, j]] = weight.into();
504 }
505 }
506 }
507
508 let mut x = Array1::<f64>::from_elem(n, 1.0 / (n as f64).sqrt());
510 let mut converged = false;
511
512 for _ in 0..max_iter {
514 let x_new = adj_matrix.dot(&x);
515
516 let norm = x_new.dot(&x_new).sqrt();
518 if norm == 0.0 {
519 return Err(GraphError::ComputationError(
520 "Eigenvector computation failed".to_string(),
521 ));
522 }
523
524 let x_normalized = x_new / norm;
525
526 let diff = (&x_normalized - &x).mapv(f64::abs).sum();
528 if diff < tolerance {
529 converged = true;
530 x = x_normalized;
531 break;
532 }
533
534 x = x_normalized;
535 }
536
537 if !converged {
538 return Err(GraphError::ComputationError(
539 "Eigenvector centrality did not converge".to_string(),
540 ));
541 }
542
543 Ok(nodes
545 .into_iter()
546 .enumerate()
547 .map(|(i, node)| (node, x[i]))
548 .collect())
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use crate::generators::create_graph;
555
556 #[test]
557 fn test_minimum_spanning_tree() {
558 let mut graph = create_graph::<&str, f64>();
559 graph.add_edge("A", "B", 1.0).expect("Operation failed");
560 graph.add_edge("B", "C", 2.0).expect("Operation failed");
561 graph.add_edge("A", "C", 3.0).expect("Operation failed");
562 graph.add_edge("C", "D", 1.0).expect("Operation failed");
563
564 let mst = minimum_spanning_tree(&graph).expect("Operation failed");
565
566 assert_eq!(mst.len(), 3);
568
569 let total_weight: f64 = mst.iter().map(|e| e.weight).sum();
571 assert_eq!(total_weight, 4.0);
572 }
573
574 #[test]
575 fn test_topological_sort() {
576 let mut graph = crate::generators::create_digraph::<&str, ()>();
577 graph.add_edge("A", "B", ()).expect("Operation failed");
578 graph.add_edge("A", "C", ()).expect("Operation failed");
579 graph.add_edge("B", "D", ()).expect("Operation failed");
580 graph.add_edge("C", "D", ()).expect("Operation failed");
581
582 let sorted = topological_sort(&graph).expect("Operation failed");
583
584 let a_pos = sorted
586 .iter()
587 .position(|n| n == &"A")
588 .expect("Operation failed");
589 let b_pos = sorted
590 .iter()
591 .position(|n| n == &"B")
592 .expect("Operation failed");
593 let c_pos = sorted
594 .iter()
595 .position(|n| n == &"C")
596 .expect("Operation failed");
597 let d_pos = sorted
598 .iter()
599 .position(|n| n == &"D")
600 .expect("Operation failed");
601
602 assert!(a_pos < b_pos);
603 assert!(a_pos < c_pos);
604 assert!(b_pos < d_pos);
605 assert!(c_pos < d_pos);
606 }
607
608 #[test]
609 fn test_pagerank() {
610 let mut graph = crate::generators::create_digraph::<&str, ()>();
611 graph.add_edge("A", "B", ()).expect("Operation failed");
612 graph.add_edge("A", "C", ()).expect("Operation failed");
613 graph.add_edge("B", "C", ()).expect("Operation failed");
614 graph.add_edge("C", "A", ()).expect("Operation failed");
615
616 let pr = pagerank(&graph, 0.85, 1e-6, 100);
617
618 assert!(pr.values().all(|&v| v > 0.0));
620
621 let sum: f64 = pr.values().sum();
623 assert!((sum - 1.0).abs() < 0.01);
624 }
625}