rust_igraph/algorithms/properties/
wiener_polarity_index.rs1#![allow(
12 clippy::cast_possible_truncation,
13 clippy::cast_precision_loss,
14 clippy::many_single_char_names,
15 clippy::needless_range_loop,
16 clippy::too_many_lines
17)]
18
19use crate::core::{Graph, IgraphResult};
20use std::collections::VecDeque;
21
22pub fn wiener_polarity_index(graph: &Graph) -> IgraphResult<u64> {
38 let n = graph.vcount() as usize;
39 if n < 4 {
40 return Ok(0);
41 }
42
43 let directed = graph.is_directed();
44 let adj = build_adj_list(graph, n);
45
46 let mut count: u64 = 0;
47
48 for src in 0..n {
49 let at3 = bfs_count_at_distance(&adj, n, src, 3);
50 count = count.saturating_add(at3 as u64);
51 }
52
53 if !directed {
54 count /= 2;
55 }
56
57 Ok(count)
58}
59
60pub fn count_pairs_at_distance(graph: &Graph, k: u32) -> IgraphResult<u64> {
76 let n = graph.vcount() as usize;
77 if k == 0 {
78 return Ok(n as u64);
79 }
80
81 let directed = graph.is_directed();
82 let adj = build_adj_list(graph, n);
83
84 let mut count: u64 = 0;
85
86 for src in 0..n {
87 let at_k = bfs_count_at_distance(&adj, n, src, k);
88 count = count.saturating_add(at_k as u64);
89 }
90
91 if !directed {
92 count /= 2;
93 }
94
95 Ok(count)
96}
97
98fn build_adj_list(graph: &Graph, n: usize) -> Vec<Vec<usize>> {
99 let mut adj = vec![Vec::new(); n];
100 for (u, v) in graph.edges() {
101 let ui = u as usize;
102 let vi = v as usize;
103 adj[ui].push(vi);
104 if !graph.is_directed() {
105 adj[vi].push(ui);
106 }
107 }
108 adj
109}
110
111fn bfs_count_at_distance(adj: &[Vec<usize>], n: usize, src: usize, target_dist: u32) -> usize {
112 let mut dist = vec![u32::MAX; n];
113 dist[src] = 0;
114 let mut queue = VecDeque::new();
115 queue.push_back(src);
116
117 let mut count: usize = 0;
118
119 while let Some(v) = queue.pop_front() {
120 let d = dist[v];
121 if d > target_dist {
122 break;
123 }
124 if d == target_dist {
125 count = count.saturating_add(1);
126 continue;
127 }
128 for &w in &adj[v] {
129 if dist[w] == u32::MAX {
130 dist[w] = d.saturating_add(1);
131 queue.push_back(w);
132 }
133 }
134 }
135
136 count
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 fn path4() -> Graph {
144 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
145 }
146
147 fn path5() -> Graph {
148 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], false, Some(5)).unwrap()
149 }
150
151 fn k3() -> Graph {
152 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
153 }
154
155 fn k4() -> Graph {
156 Graph::from_edges(
157 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
158 false,
159 Some(4),
160 )
161 .unwrap()
162 }
163
164 fn cycle5() -> Graph {
165 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
166 }
167
168 fn cycle6() -> Graph {
169 Graph::from_edges(
170 &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)],
171 false,
172 Some(6),
173 )
174 .unwrap()
175 }
176
177 fn star5() -> Graph {
178 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
179 }
180
181 fn petersen() -> Graph {
182 Graph::from_edges(
183 &[
184 (0, 1),
185 (1, 2),
186 (2, 3),
187 (3, 4),
188 (4, 0),
189 (0, 5),
190 (1, 6),
191 (2, 7),
192 (3, 8),
193 (4, 9),
194 (5, 7),
195 (7, 9),
196 (9, 6),
197 (6, 8),
198 (8, 5),
199 ],
200 false,
201 Some(10),
202 )
203 .unwrap()
204 }
205
206 #[test]
209 fn wpi_empty() {
210 let g = Graph::with_vertices(0);
211 assert_eq!(wiener_polarity_index(&g).unwrap(), 0);
212 }
213
214 #[test]
215 fn wpi_single() {
216 let g = Graph::with_vertices(1);
217 assert_eq!(wiener_polarity_index(&g).unwrap(), 0);
218 }
219
220 #[test]
221 fn wpi_no_edges() {
222 let g = Graph::with_vertices(5);
223 assert_eq!(wiener_polarity_index(&g).unwrap(), 0);
224 }
225
226 #[test]
227 fn wpi_path4() {
228 assert_eq!(wiener_polarity_index(&path4()).unwrap(), 1);
230 }
231
232 #[test]
233 fn wpi_path5() {
234 assert_eq!(wiener_polarity_index(&path5()).unwrap(), 2);
236 }
237
238 #[test]
239 fn wpi_k3() {
240 assert_eq!(wiener_polarity_index(&k3()).unwrap(), 0);
242 }
243
244 #[test]
245 fn wpi_k4() {
246 assert_eq!(wiener_polarity_index(&k4()).unwrap(), 0);
248 }
249
250 #[test]
251 fn wpi_star5() {
252 assert_eq!(wiener_polarity_index(&star5()).unwrap(), 0);
254 }
255
256 #[test]
257 fn wpi_cycle5() {
258 assert_eq!(wiener_polarity_index(&cycle5()).unwrap(), 0);
260 }
261
262 #[test]
263 fn wpi_cycle6() {
264 assert_eq!(wiener_polarity_index(&cycle6()).unwrap(), 3);
266 }
267
268 #[test]
269 fn wpi_petersen() {
270 assert_eq!(wiener_polarity_index(&petersen()).unwrap(), 0);
272 }
273
274 #[test]
275 fn wpi_two_components() {
276 let g = Graph::from_edges(&[(0, 1), (2, 3)], false, Some(4)).unwrap();
278 assert_eq!(wiener_polarity_index(&g).unwrap(), 0);
279 }
280
281 #[test]
284 fn cpad_zero() {
285 let g = path4();
286 assert_eq!(count_pairs_at_distance(&g, 0).unwrap(), 4);
288 }
289
290 #[test]
291 fn cpad_one_path4() {
292 assert_eq!(count_pairs_at_distance(&path4(), 1).unwrap(), 3);
294 }
295
296 #[test]
297 fn cpad_two_path5() {
298 assert_eq!(count_pairs_at_distance(&path5(), 2).unwrap(), 3);
300 }
301
302 #[test]
303 fn cpad_three_path5() {
304 assert_eq!(count_pairs_at_distance(&path5(), 3).unwrap(), 2);
306 }
307
308 #[test]
309 fn cpad_four_path5() {
310 assert_eq!(count_pairs_at_distance(&path5(), 4).unwrap(), 1);
312 }
313
314 #[test]
315 fn cpad_five_path5() {
316 assert_eq!(count_pairs_at_distance(&path5(), 5).unwrap(), 0);
318 }
319
320 #[test]
321 fn cpad_one_k4() {
322 assert_eq!(count_pairs_at_distance(&k4(), 1).unwrap(), 6);
324 }
325
326 #[test]
327 fn cpad_two_cycle6() {
328 assert_eq!(count_pairs_at_distance(&cycle6(), 2).unwrap(), 6);
330 }
331
332 #[test]
333 fn cpad_matches_wpi() {
334 for g in &[path4(), path5(), k3(), k4(), cycle5(), cycle6(), star5()] {
336 let wpi = wiener_polarity_index(g).unwrap();
337 let cpad = count_pairs_at_distance(g, 3).unwrap();
338 assert_eq!(wpi, cpad);
339 }
340 }
341
342 #[test]
343 fn cpad_empty() {
344 let g = Graph::with_vertices(0);
345 assert_eq!(count_pairs_at_distance(&g, 1).unwrap(), 0);
346 }
347
348 #[test]
351 fn sum_distances_path4() {
352 let g = path4();
354 let mut total: u64 = 0;
355 for k in 1..4 {
356 total = total.saturating_add(count_pairs_at_distance(&g, k).unwrap() * u64::from(k));
357 }
358 assert_eq!(total, 10);
359 }
360}