sssp_lib/lib.rs
1pub mod graph;
2use crate::graph::SSSPGraph;
3use std::collections::HashMap;
4
5/// A library for Single Source Shortest Path (SSSP) algorithms in graphs.
6///
7/// This crate provides an implementation of SSSP using a bounded-memory shortest path algorithm.
8/// It is designed for efficiency on large graphs with constraints on memory and computation.
9pub struct SSSPAlgorithm {
10 /// The underlying graph structure.
11 pub graph: SSSPGraph,
12}
13
14impl SSSPAlgorithm {
15 /// Creates a new SSSPAlgorithm instance with an empty graph of `n` nodes.
16 ///
17 /// # Arguments
18 ///
19 /// * `n` - The number of nodes in the graph.
20 ///
21 /// # Examples
22 ///
23 /// ```
24 /// use sssp_lib::SSSPAlgorithm;
25 ///
26 /// let algo = SSSPAlgorithm::new(10);
27 /// ```
28 pub fn new(n: usize) -> Self {
29 Self {
30 graph: SSSPGraph::new(n),
31 }
32 }
33
34 /// Runs the SSSP algorithm from the given source node.
35 ///
36 /// Returns a HashMap where keys are node indices and values are the shortest distances from the source.
37 /// If the source is invalid (out of bounds), returns an empty HashMap.
38 ///
39 /// # Arguments
40 ///
41 /// * `source` - The index of the source node.
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// use sssp_lib::SSSPAlgorithm;
47 ///
48 /// let mut algo = SSSPAlgorithm::new(3);
49 /// algo.graph.add_edge(0, 1, 1.0);
50 /// algo.graph.add_edge(1, 2, 2.0);
51 ///
52 /// let dists = algo.run(0);
53 /// assert_eq!(dists.get(&2), Some(&3.0));
54 /// ```
55 pub fn run(&self, source: usize) -> HashMap<usize, f64> {
56 if source >= self.graph.n {
57 return HashMap::new();
58 }
59
60 // To avoid errors in small graphs, we adjust the work limit.
61 // For large graphs (n > 100), we apply the constraint from the paper n^(2/3).
62 // For small graphs, we allow it to complete (n * n).
63 let n_f = self.graph.n as f64;
64 let u_bound = if self.graph.n < 50 {
65 self.graph.n * self.graph.n
66 } else {
67 n_f.powf(0.66).round() as usize
68 };
69
70 let b_bound = f64::INFINITY;
71
72 let (dists, _) = self.graph.bmssp(vec![(source, 0.0)], b_bound, u_bound);
73
74 dists
75 }
76 /// Generates a large random graph for performance testing.
77 ///
78 /// Creates a graph with `n` nodes, where each node has approximately `edges_per_node` outgoing edges.
79 /// Edge weights are random floats between 0.0 and 10.0.
80 ///
81 /// # Arguments
82 ///
83 /// * `n` - The number of nodes.
84 /// * `edges_per_node` - The average number of outgoing edges per node.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use sssp_lib::SSSPAlgorithm;
90 ///
91 /// let algo = SSSPAlgorithm::generate_large_random(100, 5);
92 /// ```
93 pub fn generate_large_random(n: usize, edges_per_node: usize) -> Self {
94 use std::time::{SystemTime, UNIX_EPOCH};
95
96 let mut algo = Self::new(n);
97 let mut seed = SystemTime::now()
98 .duration_since(UNIX_EPOCH)
99 .unwrap()
100 .as_nanos() as u64;
101
102 // Simple LCG generator to avoid adding dependencies like 'rand'
103 let mut next_rand = || {
104 seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
105 seed
106 };
107
108 for u in 0..n {
109 for _ in 0..edges_per_node {
110 let v = (next_rand() as usize) % n;
111 let weight = ((next_rand() % 100) as f64) / 10.0; // Pesos entre 0.0 y 10.0
112 if u != v {
113 algo.graph.add_edge(u, v, weight);
114 }
115 }
116 }
117 algo
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use std::time::Instant;
124
125 use super::*;
126
127 #[test]
128 fn test_shortest_path_logic() {
129 let mut algo = SSSPAlgorithm::new(5);
130 // Grafo del fallo anterior:
131 algo.graph.add_edge(0, 1, 10.0);
132 algo.graph.add_edge(0, 3, 5.0);
133 algo.graph.add_edge(3, 1, 2.0);
134 algo.graph.add_edge(1, 2, 1.0);
135 algo.graph.add_edge(3, 2, 10.0);
136
137 let dists = algo.run(0);
138
139 // Camino: 0 -> 3 (5) -> 1 (2) -> 2 (1) = Total 8.0
140 let result = *dists.get(&2).expect("Should find node 2");
141 assert_eq!(result, 8.0, "The shortest path should be 8.0");
142 }
143
144 #[test]
145 fn test_diamond_graph() {
146 let mut algo = SSSPAlgorithm::new(4);
147 algo.graph.add_edge(0, 1, 1.0);
148 algo.graph.add_edge(0, 2, 1.0);
149 algo.graph.add_edge(1, 3, 1.0);
150 algo.graph.add_edge(2, 3, 5.0);
151
152 let dists = algo.run(0);
153 assert_eq!(*dists.get(&3).unwrap(), 2.0);
154 }
155 #[test]
156 fn test_large_graph_performance() {
157 let n = 100_000;
158 let edges_per_node = 5;
159
160 println!("Generating graph with {} nodes...", n);
161 let algo = SSSPAlgorithm::generate_large_random(n, edges_per_node);
162
163 println!("Running SSSP Algorithm...");
164 let start = Instant::now();
165 let results = algo.run(0);
166 let duration = start.elapsed();
167
168 println!("Completed in: {:?}", duration);
169 println!("Nodes reached: {}", results.len());
170
171 // Verificación básica
172 assert!(results.len() > 0);
173 }
174}