qualia_core_db/solvers/graph_opt/
hierarchical_path.rs1use std::collections::HashMap;
13
14pub fn dijkstra(n: usize, edges_of: &[Vec<(usize, f64)>], source: usize) -> Vec<f64> {
18 let mut dist = vec![f64::INFINITY; n];
19 if source >= n {
20 return dist;
21 }
22 dist[source] = 0.0;
23 let mut visited = vec![false; n];
24 for _ in 0..n {
25 let mut u = usize::MAX;
27 let mut best = f64::INFINITY;
28 for i in 0..n {
29 if !visited[i] && dist[i] < best {
30 best = dist[i];
31 u = i;
32 }
33 }
34 if u == usize::MAX {
35 break;
36 }
37 visited[u] = true;
38 for &(v, w) in &edges_of[u] {
39 if dist[u] + w < dist[v] {
40 dist[v] = dist[u] + w;
41 }
42 }
43 }
44 dist
45}
46
47fn intra_distance(
51 edges_of: &[Vec<(usize, f64)>],
52 cluster_of: &[usize],
53 cluster: usize,
54 source: usize,
55 target: usize,
56) -> f64 {
57 let n = edges_of.len();
58 let mut dist = vec![f64::INFINITY; n];
59 dist[source] = 0.0;
60 let mut visited = vec![false; n];
61 loop {
62 let mut u = usize::MAX;
63 let mut best = f64::INFINITY;
64 for i in 0..n {
65 if !visited[i] && cluster_of[i] == cluster && dist[i] < best {
66 best = dist[i];
67 u = i;
68 }
69 }
70 if u == usize::MAX {
71 break;
72 }
73 visited[u] = true;
74 for &(v, w) in &edges_of[u] {
75 if cluster_of[v] == cluster && dist[u] + w < dist[v] {
76 dist[v] = dist[u] + w;
77 }
78 }
79 }
80 dist[target]
81}
82
83pub fn hierarchical_shortest_path(
88 n: usize,
89 edges_of: &[Vec<(usize, f64)>],
90 cluster_of: &[usize],
91 s: usize,
92 t: usize,
93) -> f64 {
94 if s >= n || t >= n {
95 return f64::INFINITY;
96 }
97 if cluster_of[s] == cluster_of[t] {
98 let intra = intra_distance(edges_of, cluster_of, cluster_of[s], s, t);
101 let portal = portal_distance(n, edges_of, cluster_of, s, t);
103 return intra.min(portal);
104 }
105 portal_distance(n, edges_of, cluster_of, s, t)
106}
107
108fn portal_distance(
110 n: usize,
111 edges_of: &[Vec<(usize, f64)>],
112 cluster_of: &[usize],
113 s: usize,
114 t: usize,
115) -> f64 {
116 let mut is_border = vec![false; n];
118 for u in 0..n {
119 for &(v, _) in &edges_of[u] {
120 if cluster_of[u] != cluster_of[v] {
121 is_border[u] = true;
122 is_border[v] = true;
123 }
124 }
125 }
126 let mut nodes: Vec<usize> = (0..n).filter(|&i| is_border[i]).collect();
128 if !nodes.contains(&s) {
129 nodes.push(s);
130 }
131 if !nodes.contains(&t) {
132 nodes.push(t);
133 }
134 let index: HashMap<usize, usize> = nodes
135 .iter()
136 .enumerate()
137 .map(|(i, &orig)| (orig, i))
138 .collect();
139 let m = nodes.len();
140 let mut portal_edges: Vec<Vec<(usize, f64)>> = vec![Vec::new(); m];
141
142 for &a in &nodes {
145 for &b in &nodes {
146 if a != b && cluster_of[a] == cluster_of[b] {
147 let d = intra_distance(edges_of, cluster_of, cluster_of[a], a, b);
148 if d.is_finite() {
149 portal_edges[index[&a]].push((index[&b], d));
150 }
151 }
152 }
153 }
154 for u in 0..n {
156 for &(v, w) in &edges_of[u] {
157 if cluster_of[u] != cluster_of[v] {
158 if let (Some(&iu), Some(&iv)) = (index.get(&u), index.get(&v)) {
159 portal_edges[iu].push((iv, w));
160 }
161 }
162 }
163 }
164
165 let dist = dijkstra(m, &portal_edges, index[&s]);
166 dist[index[&t]]
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 fn graph(n: usize, edges: &[(usize, usize, f64)]) -> Vec<Vec<(usize, f64)>> {
175 let mut adj = vec![Vec::new(); n];
176 for &(a, b, w) in edges {
177 adj[a].push((b, w));
178 adj[b].push((a, w));
179 }
180 adj
181 }
182
183 #[test]
184 fn dijkstra_matches_known_distances() {
185 let adj = graph(3, &[(0, 1, 1.0), (1, 2, 2.0), (0, 2, 4.0)]);
187 let d = dijkstra(3, &adj, 0);
188 assert!((d[2] - 3.0).abs() < 1e-9);
189 }
190
191 #[test]
192 fn hierarchical_equals_exact_across_clusters() {
193 let edges = [
195 (0, 1, 1.0),
196 (1, 2, 1.0),
197 (0, 2, 3.0), (3, 4, 1.0),
199 (4, 5, 1.0),
200 (3, 5, 3.0), (2, 3, 2.0), ];
203 let adj = graph(6, &edges);
204 let cluster = [0, 0, 0, 1, 1, 1];
205 for s in 0..6 {
207 let exact = dijkstra(6, &adj, s);
208 for t in 0..6 {
209 let h = hierarchical_shortest_path(6, &adj, &cluster, s, t);
210 assert!(
211 (h - exact[t]).abs() < 1e-9,
212 "s={s} t={t}: hier {h} vs exact {}",
213 exact[t]
214 );
215 }
216 }
217 }
218
219 #[test]
220 fn hierarchical_handles_leave_and_return() {
221 let edges = [
223 (0, 1, 10.0), (0, 2, 1.0),
225 (2, 3, 1.0),
226 (3, 1, 1.0), ];
228 let adj = graph(4, &edges);
229 let cluster = [0, 0, 1, 1];
230 let exact = dijkstra(4, &adj, 0);
231 let h = hierarchical_shortest_path(4, &adj, &cluster, 0, 1);
232 assert!(
233 (h - exact[1]).abs() < 1e-9,
234 "leave-and-return: {h} vs {}",
235 exact[1]
236 );
237 assert!(h < 10.0, "should take the cheap detour");
238 }
239}