rust_igraph/algorithms/properties/
mostar_index.rs1#![allow(
13 clippy::cast_possible_truncation,
14 clippy::cast_precision_loss,
15 clippy::comparison_chain,
16 clippy::many_single_char_names,
17 clippy::needless_range_loop,
18 clippy::too_many_lines
19)]
20
21use crate::core::{Graph, IgraphResult};
22use std::collections::VecDeque;
23
24pub fn mostar_index(graph: &Graph) -> IgraphResult<u64> {
44 let n = graph.vcount() as usize;
45 if n == 0 {
46 return Ok(0);
47 }
48
49 let dist = all_pairs_bfs(graph, n);
50 let mut mo: u64 = 0;
51
52 for (u, v) in graph.edges() {
53 let ui = u as usize;
54 let vi = v as usize;
55 if ui == vi {
56 continue;
57 }
58 let (nu, nv) = count_closer(&dist, n, ui, vi);
59 mo = mo.saturating_add(nu.abs_diff(nv) as u64);
60 }
61
62 Ok(mo)
63}
64
65pub fn degree_distance(graph: &Graph) -> IgraphResult<u64> {
84 let n = graph.vcount() as usize;
85 if n == 0 {
86 return Ok(0);
87 }
88
89 let dist = all_pairs_bfs(graph, n);
90 let mut deg = vec![0_usize; n];
91 for v in 0..n as u32 {
92 deg[v as usize] = graph.degree(v)?;
93 }
94
95 let mut dd: u64 = 0;
96 for u in 0..n {
97 for v in 0..n {
98 if u == v {
99 continue;
100 }
101 let d = dist[u * n + v];
102 if d == u32::MAX {
103 continue;
104 }
105 let sum_deg = (deg[u] as u64).saturating_add(deg[v] as u64);
106 dd = dd.saturating_add(sum_deg.saturating_mul(u64::from(d)));
107 }
108 }
109
110 Ok(dd)
111}
112
113pub fn gutman_index(graph: &Graph) -> IgraphResult<u64> {
129 let n = graph.vcount() as usize;
130 if n == 0 {
131 return Ok(0);
132 }
133
134 let dist = all_pairs_bfs(graph, n);
135 let mut deg = vec![0_usize; n];
136 for v in 0..n as u32 {
137 deg[v as usize] = graph.degree(v)?;
138 }
139
140 let mut gut: u64 = 0;
141 for u in 0..n {
142 for v in (u + 1)..n {
143 let d = dist[u * n + v];
144 if d == u32::MAX {
145 continue;
146 }
147 let prod = (deg[u] as u64).saturating_mul(deg[v] as u64);
148 gut = gut.saturating_add(prod.saturating_mul(u64::from(d)));
149 }
150 }
151
152 Ok(gut)
153}
154
155fn count_closer(dist: &[u32], n: usize, u: usize, v: usize) -> (usize, usize) {
156 let mut nu = 0_usize;
157 let mut nv = 0_usize;
158 for w in 0..n {
159 let du = dist[u * n + w];
160 let dv = dist[v * n + w];
161 if du == u32::MAX || dv == u32::MAX {
162 continue;
163 }
164 if du < dv {
165 nu += 1;
166 } else if dv < du {
167 nv += 1;
168 }
169 }
170 (nu, nv)
171}
172
173fn all_pairs_bfs(graph: &Graph, n: usize) -> Vec<u32> {
174 let adj = build_adj_list(graph, n);
175 let mut dist = vec![u32::MAX; n * n];
176 for src in 0..n {
177 bfs_distances(&adj, n, src, &mut dist);
178 }
179 dist
180}
181
182fn build_adj_list(graph: &Graph, n: usize) -> Vec<Vec<usize>> {
183 let mut adj = vec![Vec::new(); n];
184 for (u, v) in graph.edges() {
185 let ui = u as usize;
186 let vi = v as usize;
187 adj[ui].push(vi);
188 if !graph.is_directed() && ui != vi {
189 adj[vi].push(ui);
190 }
191 }
192 adj
193}
194
195fn bfs_distances(adj: &[Vec<usize>], n: usize, src: usize, dist: &mut [u32]) {
196 dist[src * n + src] = 0;
197 let mut queue = VecDeque::new();
198 queue.push_back(src);
199 while let Some(v) = queue.pop_front() {
200 let d = dist[src * n + v];
201 for &w in &adj[v] {
202 if dist[src * n + w] == u32::MAX {
203 dist[src * n + w] = d.saturating_add(1);
204 queue.push_back(w);
205 }
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 fn single_edge() -> Graph {
215 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
216 }
217
218 fn path3() -> Graph {
219 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
220 }
221
222 fn path4() -> Graph {
223 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
224 }
225
226 fn k3() -> Graph {
227 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
228 }
229
230 fn k4() -> Graph {
231 Graph::from_edges(
232 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
233 false,
234 Some(4),
235 )
236 .unwrap()
237 }
238
239 fn cycle4() -> Graph {
240 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
241 }
242
243 fn star5() -> Graph {
244 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
245 }
246
247 #[test]
250 fn mo_empty() {
251 let g = Graph::with_vertices(0);
252 assert_eq!(mostar_index(&g).unwrap(), 0);
253 }
254
255 #[test]
256 fn mo_no_edges() {
257 let g = Graph::with_vertices(3);
258 assert_eq!(mostar_index(&g).unwrap(), 0);
259 }
260
261 #[test]
262 fn mo_single_edge() {
263 assert_eq!(mostar_index(&single_edge()).unwrap(), 0);
265 }
266
267 #[test]
268 fn mo_path3() {
269 assert_eq!(mostar_index(&path3()).unwrap(), 2);
273 }
274
275 #[test]
276 fn mo_path4() {
277 assert_eq!(mostar_index(&path4()).unwrap(), 4);
282 }
283
284 #[test]
285 fn mo_k3() {
286 assert_eq!(mostar_index(&k3()).unwrap(), 0);
289 }
290
291 #[test]
292 fn mo_cycle4() {
293 assert_eq!(mostar_index(&cycle4()).unwrap(), 0);
295 }
296
297 #[test]
298 fn mo_star5() {
299 assert_eq!(mostar_index(&star5()).unwrap(), 12);
301 }
302
303 #[test]
304 fn mo_regular_distance_balanced() {
305 assert_eq!(mostar_index(&k4()).unwrap(), 0);
307 assert_eq!(mostar_index(&cycle4()).unwrap(), 0);
308 }
309
310 #[test]
313 fn dd_empty() {
314 let g = Graph::with_vertices(0);
315 assert_eq!(degree_distance(&g).unwrap(), 0);
316 }
317
318 #[test]
319 fn dd_single_edge() {
320 assert_eq!(degree_distance(&single_edge()).unwrap(), 4);
322 }
323
324 #[test]
325 fn dd_path3() {
326 assert_eq!(degree_distance(&path3()).unwrap(), 20);
332 }
333
334 #[test]
335 fn dd_k3() {
336 assert_eq!(degree_distance(&k3()).unwrap(), 24);
339 }
340
341 #[test]
342 fn dd_k4() {
343 assert_eq!(degree_distance(&k4()).unwrap(), 72);
346 }
347
348 #[test]
349 fn dd_star5() {
350 assert_eq!(degree_distance(&star5()).unwrap(), 88);
355 }
356
357 #[test]
360 fn gut_empty() {
361 let g = Graph::with_vertices(0);
362 assert_eq!(gutman_index(&g).unwrap(), 0);
363 }
364
365 #[test]
366 fn gut_single_edge() {
367 assert_eq!(gutman_index(&single_edge()).unwrap(), 1);
369 }
370
371 #[test]
372 fn gut_path3() {
373 assert_eq!(gutman_index(&path3()).unwrap(), 6);
375 }
376
377 #[test]
378 fn gut_k3() {
379 assert_eq!(gutman_index(&k3()).unwrap(), 12);
381 }
382
383 #[test]
384 fn gut_k4() {
385 assert_eq!(gutman_index(&k4()).unwrap(), 54);
387 }
388
389 #[test]
390 fn gut_star5() {
391 assert_eq!(gutman_index(&star5()).unwrap(), 28);
395 }
396
397 #[test]
400 fn dd_is_twice_sum_unordered() {
401 for g in &[path3(), k3(), k4(), star5()] {
403 let dd = degree_distance(g).unwrap();
404 let n = g.vcount() as usize;
405 let dist = all_pairs_bfs(g, n);
406 let mut deg = vec![0_usize; n];
407 for v in 0..n as u32 {
408 deg[v as usize] = g.degree(v).unwrap();
409 }
410 let mut half: u64 = 0;
411 for u in 0..n {
412 for v in (u + 1)..n {
413 let d = dist[u * n + v];
414 if d == u32::MAX {
415 continue;
416 }
417 half += (deg[u] as u64 + deg[v] as u64) * u64::from(d);
418 }
419 }
420 assert_eq!(dd, 2 * half);
421 }
422 }
423
424 #[test]
425 fn gutman_leq_dd_div2_times_max_deg() {
426 for g in &[path3(), k3(), k4(), star5()] {
428 let gut = gutman_index(g).unwrap();
429 let dd = degree_distance(g).unwrap();
430 let max_d = u64::from(
431 crate::algorithms::properties::degree::max_degree(
432 g,
433 crate::algorithms::properties::degree::DegreeMode::All,
434 )
435 .unwrap(),
436 );
437 assert!(
438 gut <= dd / 2 * max_d + max_d,
439 "Gutman {gut} too large relative to DD {dd}"
440 );
441 }
442 }
443}