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