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