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