rust_igraph/algorithms/properties/
graph_bandwidth.rs1#![allow(
14 clippy::cast_possible_truncation,
15 clippy::cast_precision_loss,
16 clippy::many_single_char_names,
17 clippy::needless_range_loop,
18 clippy::too_many_lines
19)]
20
21use crate::core::{Graph, IgraphResult};
22
23pub fn bandwidth_of_labeling(graph: &Graph, labeling: &[u32]) -> IgraphResult<u32> {
40 let n = graph.vcount() as usize;
41 if labeling.len() != n {
42 return Err(crate::core::IgraphError::InvalidArgument(format!(
43 "bandwidth_of_labeling: labeling length {} != vcount {n}",
44 labeling.len()
45 )));
46 }
47
48 let mut bw: u32 = 0;
49 for (u, v) in graph.edges() {
50 let lu = labeling[u as usize];
51 let lv = labeling[v as usize];
52 let diff = lu.abs_diff(lv);
53 if diff > bw {
54 bw = diff;
55 }
56 }
57
58 Ok(bw)
59}
60
61pub fn bandwidth_lower_bound(graph: &Graph) -> IgraphResult<u32> {
77 let n = graph.vcount() as usize;
78 if n <= 1 {
79 return Ok(0);
80 }
81
82 let mut max_deg: usize = 0;
83 for v in 0..n as u32 {
84 let d = graph.degree(v)?;
85 if d > max_deg {
86 max_deg = d;
87 }
88 }
89
90 let deg_bound = (max_deg.saturating_add(1)) / 2;
91
92 Ok(deg_bound as u32)
93}
94
95pub fn bandwidth(graph: &Graph) -> IgraphResult<u32> {
110 let n = graph.vcount() as usize;
111 if n == 0 {
112 return Ok(0);
113 }
114 if graph.ecount() == 0 {
115 return Ok(0);
116 }
117
118 let edges: Vec<(usize, usize)> = graph
119 .edges()
120 .map(|(u, v)| (u as usize, v as usize))
121 .collect();
122
123 let mut perm: Vec<u32> = (0..n as u32).collect();
124 let mut best = n as u32;
125
126 permute_and_minimize(&edges, &mut perm, 0, &mut best);
127
128 Ok(best)
129}
130
131fn permute_and_minimize(
132 edges: &[(usize, usize)],
133 perm: &mut Vec<u32>,
134 depth: usize,
135 best: &mut u32,
136) {
137 let n = perm.len();
138
139 if depth == n {
140 let bw = compute_bw(edges, perm);
141 if bw < *best {
142 *best = bw;
143 }
144 return;
145 }
146
147 for i in depth..n {
148 perm.swap(depth, i);
149
150 let partial_bw = partial_bandwidth(edges, perm, depth);
151 if partial_bw < *best {
152 permute_and_minimize(edges, perm, depth.saturating_add(1), best);
153 }
154
155 perm.swap(depth, i);
156 }
157}
158
159fn compute_bw(edges: &[(usize, usize)], perm: &[u32]) -> u32 {
160 let mut bw: u32 = 0;
161 for &(u, v) in edges {
162 let lu = perm[u];
163 let lv = perm[v];
164 let diff = lu.abs_diff(lv);
165 if diff > bw {
166 bw = diff;
167 }
168 }
169 bw
170}
171
172fn partial_bandwidth(edges: &[(usize, usize)], perm: &[u32], depth: usize) -> u32 {
173 let mut bw: u32 = 0;
174 for &(u, v) in edges {
175 if u <= depth && v <= depth {
176 let lu = perm[u];
177 let lv = perm[v];
178 let diff = lu.abs_diff(lv);
179 if diff > bw {
180 bw = diff;
181 }
182 }
183 }
184 bw
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 fn path4() -> Graph {
192 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
193 }
194
195 fn k3() -> Graph {
196 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
197 }
198
199 fn k4() -> Graph {
200 Graph::from_edges(
201 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
202 false,
203 Some(4),
204 )
205 .unwrap()
206 }
207
208 fn cycle4() -> Graph {
209 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
210 }
211
212 fn cycle5() -> Graph {
213 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
214 }
215
216 fn star5() -> Graph {
217 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
218 }
219
220 #[test]
223 fn bol_identity() {
224 let g = path4();
225 assert_eq!(bandwidth_of_labeling(&g, &[0, 1, 2, 3]).unwrap(), 1);
226 }
227
228 #[test]
229 fn bol_reversed() {
230 let g = path4();
231 assert_eq!(bandwidth_of_labeling(&g, &[3, 2, 1, 0]).unwrap(), 1);
232 }
233
234 #[test]
235 fn bol_bad_labeling() {
236 let g = path4();
237 assert_eq!(bandwidth_of_labeling(&g, &[0, 3, 1, 2]).unwrap(), 3);
238 }
239
240 #[test]
241 fn bol_k3() {
242 let g = k3();
243 assert_eq!(bandwidth_of_labeling(&g, &[0, 1, 2]).unwrap(), 2);
244 }
245
246 #[test]
247 fn bol_wrong_length() {
248 let g = path4();
249 assert!(bandwidth_of_labeling(&g, &[0, 1]).is_err());
250 }
251
252 #[test]
253 fn bol_empty() {
254 let g = Graph::with_vertices(0);
255 assert_eq!(bandwidth_of_labeling(&g, &[]).unwrap(), 0);
256 }
257
258 #[test]
259 fn bol_no_edges() {
260 let g = Graph::with_vertices(3);
261 assert_eq!(bandwidth_of_labeling(&g, &[0, 1, 2]).unwrap(), 0);
262 }
263
264 #[test]
267 fn blb_path4() {
268 assert!(bandwidth_lower_bound(&path4()).unwrap() >= 1);
269 }
270
271 #[test]
272 fn blb_k4() {
273 let lb = bandwidth_lower_bound(&k4()).unwrap();
274 assert!(lb >= 2);
275 }
276
277 #[test]
278 fn blb_empty() {
279 let g = Graph::with_vertices(0);
280 assert_eq!(bandwidth_lower_bound(&g).unwrap(), 0);
281 }
282
283 #[test]
284 fn blb_single() {
285 let g = Graph::with_vertices(1);
286 assert_eq!(bandwidth_lower_bound(&g).unwrap(), 0);
287 }
288
289 #[test]
292 fn bw_empty() {
293 let g = Graph::with_vertices(0);
294 assert_eq!(bandwidth(&g).unwrap(), 0);
295 }
296
297 #[test]
298 fn bw_single() {
299 let g = Graph::with_vertices(1);
300 assert_eq!(bandwidth(&g).unwrap(), 0);
301 }
302
303 #[test]
304 fn bw_no_edges() {
305 let g = Graph::with_vertices(3);
306 assert_eq!(bandwidth(&g).unwrap(), 0);
307 }
308
309 #[test]
310 fn bw_single_edge() {
311 let g = Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap();
312 assert_eq!(bandwidth(&g).unwrap(), 1);
313 }
314
315 #[test]
316 fn bw_path4() {
317 assert_eq!(bandwidth(&path4()).unwrap(), 1);
318 }
319
320 #[test]
321 fn bw_k3() {
322 assert_eq!(bandwidth(&k3()).unwrap(), 2);
323 }
324
325 #[test]
326 fn bw_k4() {
327 assert_eq!(bandwidth(&k4()).unwrap(), 3);
329 }
330
331 #[test]
332 fn bw_cycle4() {
333 assert_eq!(bandwidth(&cycle4()).unwrap(), 2);
334 }
335
336 #[test]
337 fn bw_cycle5() {
338 assert_eq!(bandwidth(&cycle5()).unwrap(), 2);
339 }
340
341 #[test]
342 fn bw_star5() {
343 assert_eq!(bandwidth(&star5()).unwrap(), 2);
344 }
345
346 #[test]
349 fn bw_geq_lower_bound() {
350 for g in &[path4(), k3(), k4(), cycle4(), cycle5(), star5()] {
351 let bw = bandwidth(g).unwrap();
352 let lb = bandwidth_lower_bound(g).unwrap();
353 assert!(bw >= lb, "bandwidth {bw} < lower bound {lb}");
354 }
355 }
356
357 #[test]
358 fn bw_leq_identity_labeling() {
359 for g in &[path4(), k3(), k4(), cycle4()] {
360 let n = g.vcount() as usize;
361 let identity: Vec<u32> = (0..n as u32).collect();
362 let id_bw = bandwidth_of_labeling(g, &identity).unwrap();
363 let opt_bw = bandwidth(g).unwrap();
364 assert!(opt_bw <= id_bw);
365 }
366 }
367
368 #[test]
369 fn path_bandwidth_is_one() {
370 for n in 2_u32..=6 {
371 let edges: Vec<(u32, u32)> = (0..n - 1).map(|i| (i, i + 1)).collect();
372 let g = Graph::from_edges(&edges, false, Some(n)).unwrap();
373 assert_eq!(bandwidth(&g).unwrap(), 1);
374 }
375 }
376
377 #[test]
378 fn complete_graph_bandwidth() {
379 let g2 = Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap();
381 assert_eq!(bandwidth(&g2).unwrap(), 1);
382 assert_eq!(bandwidth(&k3()).unwrap(), 2);
383 assert_eq!(bandwidth(&k4()).unwrap(), 3);
384 }
385}