oxicuda_graphalg/separation/
planar_separator.rs1use std::collections::VecDeque;
36
37use crate::error::{GraphalgError, GraphalgResult};
38use crate::repr::adjacency_list::AdjacencyList;
39
40#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct SeparatorResult {
45 pub part_a: Vec<usize>,
47 pub separator: Vec<usize>,
49 pub part_b: Vec<usize>,
51}
52
53#[derive(Debug, Clone)]
55pub struct PlanarSeparator {
56 n: usize,
57 adj: Vec<Vec<usize>>,
58}
59
60impl PlanarSeparator {
61 pub fn new(n: usize) -> Self {
63 Self {
64 n,
65 adj: vec![Vec::new(); n],
66 }
67 }
68
69 pub fn add_edge(&mut self, u: usize, v: usize) -> GraphalgResult<()> {
72 if u >= self.n || v >= self.n {
73 return Err(GraphalgError::IndexOutOfBounds {
74 index: u.max(v),
75 len: self.n,
76 });
77 }
78 if u == v {
79 return Ok(());
80 }
81 if !self.adj[u].contains(&v) {
82 self.adj[u].push(v);
83 }
84 if !self.adj[v].contains(&u) {
85 self.adj[v].push(u);
86 }
87 Ok(())
88 }
89
90 pub fn from_adjacency_list(g: &AdjacencyList) -> GraphalgResult<Self> {
93 let mut sep = Self::new(g.n);
94 for u in 0..g.n {
95 for &v in &g.edges[u] {
96 sep.add_edge(u, v)?;
97 }
98 }
99 Ok(sep)
100 }
101
102 pub fn num_nodes(&self) -> usize {
104 self.n
105 }
106
107 pub fn separate(&self) -> GraphalgResult<SeparatorResult> {
109 if self.n == 0 {
110 return Ok(SeparatorResult {
111 part_a: Vec::new(),
112 separator: Vec::new(),
113 part_b: Vec::new(),
114 });
115 }
116 if self.n == 1 {
117 return Ok(SeparatorResult {
118 part_a: vec![0],
119 separator: Vec::new(),
120 part_b: Vec::new(),
121 });
122 }
123 let comps = self.connected_components();
124 let limit = (2 * self.n) / 3;
125 if comps.len() == 1 {
126 let (a, s, b) = self.separate_component(&comps[0])?;
127 return Ok(finalize(a, s, b));
128 }
129 let max_comp = comps.iter().map(|c| c.len()).max().unwrap_or(0);
131 if max_comp <= limit {
132 let sizes: Vec<usize> = comps.iter().map(|c| c.len()).collect();
134 let (a_idx, b_idx) = best_partition(&sizes);
135 let mut part_a = Vec::new();
136 let mut part_b = Vec::new();
137 for &i in &a_idx {
138 part_a.extend_from_slice(&comps[i]);
139 }
140 for &i in &b_idx {
141 part_b.extend_from_slice(&comps[i]);
142 }
143 return Ok(finalize(part_a, Vec::new(), part_b));
144 }
145 let giant_idx = comps
148 .iter()
149 .enumerate()
150 .max_by_key(|(_, c)| c.len())
151 .map(|(i, _)| i)
152 .unwrap_or(0);
153 let (mut ga, gs, mut gb) = self.separate_component(&comps[giant_idx])?;
154 for (i, c) in comps.iter().enumerate() {
155 if i == giant_idx {
156 continue;
157 }
158 if ga.len() <= gb.len() {
159 ga.extend_from_slice(c);
160 } else {
161 gb.extend_from_slice(c);
162 }
163 }
164 Ok(finalize(ga, gs, gb))
165 }
166
167 fn connected_components(&self) -> Vec<Vec<usize>> {
169 let mut comp_id = vec![usize::MAX; self.n];
170 let mut comps: Vec<Vec<usize>> = Vec::new();
171 for start in 0..self.n {
172 if comp_id[start] != usize::MAX {
173 continue;
174 }
175 let id = comps.len();
176 let mut members = Vec::new();
177 let mut queue = VecDeque::new();
178 queue.push_back(start);
179 comp_id[start] = id;
180 while let Some(u) = queue.pop_front() {
181 members.push(u);
182 for &w in &self.adj[u] {
183 if comp_id[w] == usize::MAX {
184 comp_id[w] = id;
185 queue.push_back(w);
186 }
187 }
188 }
189 comps.push(members);
190 }
191 comps
192 }
193
194 fn component_edge_count(&self, in_set: &[bool], verts: &[usize]) -> usize {
196 let mut deg_sum = 0usize;
197 for &u in verts {
198 for &w in &self.adj[u] {
199 if in_set[w] {
200 deg_sum += 1;
201 }
202 }
203 }
204 deg_sum / 2
205 }
206
207 fn separate_component(
209 &self,
210 verts: &[usize],
211 ) -> GraphalgResult<(Vec<usize>, Vec<usize>, Vec<usize>)> {
212 let k = verts.len();
213 if k == 0 {
214 return Ok((Vec::new(), Vec::new(), Vec::new()));
215 }
216 if k == 1 {
217 return Ok((vec![verts[0]], Vec::new(), Vec::new()));
218 }
219 let mut in_set = vec![false; self.n];
220 for &v in verts {
221 in_set[v] = true;
222 }
223 let edge_count = self.component_edge_count(&in_set, verts);
224 if edge_count == k - 1 {
225 self.tree_centroid_separator(verts, &in_set)
226 } else {
227 self.bfs_level_separator(verts, &in_set)
228 }
229 }
230
231 fn tree_centroid_separator(
233 &self,
234 verts: &[usize],
235 in_set: &[bool],
236 ) -> GraphalgResult<(Vec<usize>, Vec<usize>, Vec<usize>)> {
237 let k = verts.len();
238 let root = verts[0];
239 let mut parent = vec![usize::MAX; self.n];
241 let mut visited = vec![false; self.n];
242 let mut order = Vec::with_capacity(k);
243 let mut queue = VecDeque::new();
244 queue.push_back(root);
245 visited[root] = true;
246 while let Some(u) = queue.pop_front() {
247 order.push(u);
248 for &w in &self.adj[u] {
249 if in_set[w] && !visited[w] {
250 visited[w] = true;
251 parent[w] = u;
252 queue.push_back(w);
253 }
254 }
255 }
256 let mut size = vec![0usize; self.n];
258 for &v in verts {
259 size[v] = 1;
260 }
261 for &u in order.iter().rev() {
262 if parent[u] != usize::MAX {
263 size[parent[u]] += size[u];
264 }
265 }
266 let mut best_c = root;
268 let mut best_max = usize::MAX;
269 for &u in verts {
270 let mut mx = k - size[u]; for &w in &self.adj[u] {
272 if in_set[w] && parent[w] == u {
273 mx = mx.max(size[w]);
274 }
275 }
276 if mx < best_max {
277 best_max = mx;
278 best_c = u;
279 }
280 }
281 let mut removed = vec![false; self.n];
283 removed[best_c] = true;
284 let mut branch_visited = vec![false; self.n];
285 branch_visited[best_c] = true;
286 let mut branches: Vec<Vec<usize>> = Vec::new();
287 for &w in &self.adj[best_c] {
288 if in_set[w] && !branch_visited[w] {
289 let mut comp = Vec::new();
290 let mut q = VecDeque::new();
291 q.push_back(w);
292 branch_visited[w] = true;
293 while let Some(u) = q.pop_front() {
294 comp.push(u);
295 for &x in &self.adj[u] {
296 if in_set[x] && !branch_visited[x] && x != best_c {
297 branch_visited[x] = true;
298 q.push_back(x);
299 }
300 }
301 }
302 branches.push(comp);
303 }
304 }
305 let sizes: Vec<usize> = branches.iter().map(|c| c.len()).collect();
306 let (a_idx, b_idx) = best_partition(&sizes);
307 let mut part_a = Vec::new();
308 let mut part_b = Vec::new();
309 for &i in &a_idx {
310 part_a.extend_from_slice(&branches[i]);
311 }
312 for &i in &b_idx {
313 part_b.extend_from_slice(&branches[i]);
314 }
315 Ok((part_a, vec![best_c], part_b))
316 }
317
318 fn bfs_level_separator(
320 &self,
321 verts: &[usize],
322 in_set: &[bool],
323 ) -> GraphalgResult<(Vec<usize>, Vec<usize>, Vec<usize>)> {
324 let k = verts.len();
325 let root = verts[0];
326 let mut level = vec![usize::MAX; self.n];
327 let mut queue = VecDeque::new();
328 level[root] = 0;
329 queue.push_back(root);
330 let mut max_level = 0usize;
331 while let Some(u) = queue.pop_front() {
332 let lu = level[u];
333 if lu > max_level {
334 max_level = lu;
335 }
336 for &w in &self.adj[u] {
337 if in_set[w] && level[w] == usize::MAX {
338 level[w] = lu + 1;
339 queue.push_back(w);
340 }
341 }
342 }
343 let mut level_size = vec![0usize; max_level + 1];
345 for &v in verts {
346 level_size[level[v]] += 1;
347 }
348 let mut prefix = vec![0usize; max_level + 2];
349 for l in 0..=max_level {
350 prefix[l + 1] = prefix[l] + level_size[l];
351 }
352 let limit = (2 * k) / 3;
353 let mut chosen = usize::MAX;
355 let mut chosen_size = usize::MAX;
356 for l in 0..=max_level {
357 let below = prefix[l];
358 let above = k - prefix[l + 1];
359 if below <= limit && above <= limit && level_size[l] < chosen_size {
360 chosen_size = level_size[l];
361 chosen = l;
362 }
363 }
364 if chosen == usize::MAX {
366 chosen = 0;
367 }
368 let mut part_a = Vec::new();
369 let mut separator = Vec::new();
370 let mut part_b = Vec::new();
371 for &v in verts {
372 let lv = level[v];
373 if lv < chosen {
374 part_a.push(v);
375 } else if lv == chosen {
376 separator.push(v);
377 } else {
378 part_b.push(v);
379 }
380 }
381 Ok((part_a, separator, part_b))
382 }
383}
384
385fn finalize(mut a: Vec<usize>, mut s: Vec<usize>, mut b: Vec<usize>) -> SeparatorResult {
387 a.sort_unstable();
388 s.sort_unstable();
389 b.sort_unstable();
390 SeparatorResult {
391 part_a: a,
392 separator: s,
393 part_b: b,
394 }
395}
396
397fn best_partition(sizes: &[usize]) -> (Vec<usize>, Vec<usize>) {
400 let m = sizes.len();
401 if m == 0 {
402 return (Vec::new(), Vec::new());
403 }
404 let total: usize = sizes.iter().sum();
405 if total == 0 {
406 return ((0..m).collect(), Vec::new());
407 }
408 let mut reach = vec![vec![false; total + 1]; m + 1];
409 reach[0][0] = true;
410 for i in 1..=m {
411 let s = sizes[i - 1];
412 for x in 0..=total {
413 if reach[i - 1][x] {
414 reach[i][x] = true;
415 if x + s <= total {
416 reach[i][x + s] = true;
417 }
418 }
419 }
420 }
421 let target = total / 2;
422 let mut best_x = 0usize;
423 let mut best_diff = usize::MAX;
424 for x in 0..=total {
425 if reach[m][x] {
426 let diff = x.abs_diff(target);
427 if diff < best_diff {
428 best_diff = diff;
429 best_x = x;
430 }
431 }
432 }
433 let mut a = Vec::new();
434 let mut b = Vec::new();
435 let mut x = best_x;
436 for i in (1..=m).rev() {
437 let s = sizes[i - 1];
438 if x >= s && reach[i - 1][x - s] {
439 a.push(i - 1);
440 x -= s;
441 } else {
442 b.push(i - 1);
443 }
444 }
445 (a, b)
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use std::collections::BTreeSet;
452
453 fn check_separator(
455 n: usize,
456 edges: &[(usize, usize)],
457 res: &SeparatorResult,
458 balance_limit: usize,
459 ) {
460 let mut seen = vec![0u8; n];
462 for &v in res.part_a.iter().chain(&res.separator).chain(&res.part_b) {
463 seen[v] += 1;
464 }
465 for (v, &c) in seen.iter().enumerate() {
466 assert_eq!(c, 1, "vertex {v} appears {c} times (must be exactly once)");
467 }
468 let a: BTreeSet<usize> = res.part_a.iter().copied().collect();
470 let b: BTreeSet<usize> = res.part_b.iter().copied().collect();
471 for &(u, v) in edges {
472 let ab = a.contains(&u) && b.contains(&v);
473 let ba = b.contains(&u) && a.contains(&v);
474 assert!(!ab && !ba, "edge ({u},{v}) crosses A-B separator");
475 }
476 assert!(
478 res.part_a.len() <= balance_limit,
479 "|A|={} exceeds {balance_limit}",
480 res.part_a.len()
481 );
482 assert!(
483 res.part_b.len() <= balance_limit,
484 "|B|={} exceeds {balance_limit}",
485 res.part_b.len()
486 );
487 }
488
489 fn build_grid(a: usize) -> (PlanarSeparator, Vec<(usize, usize)>) {
490 let n = a * a;
491 let mut sep = PlanarSeparator::new(n);
492 let mut edges = Vec::new();
493 for i in 0..a {
494 for j in 0..a {
495 let id = i * a + j;
496 if j + 1 < a {
497 sep.add_edge(id, id + 1).expect("edge");
498 edges.push((id, id + 1));
499 }
500 if i + 1 < a {
501 sep.add_edge(id, id + a).expect("edge");
502 edges.push((id, id + a));
503 }
504 }
505 }
506 (sep, edges)
507 }
508
509 #[test]
510 fn grid_separator_is_small_and_balanced() {
511 let a = 7;
512 let n = a * a;
513 let (sep, edges) = build_grid(a);
514 let res = sep.separate().expect("separate");
515 let limit = (2 * n) / 3;
516 check_separator(n, &edges, &res, limit);
517 let bound = (3.0 * (n as f64).sqrt()).ceil() as usize;
519 assert!(
520 res.separator.len() <= bound,
521 "separator size {} exceeds O(sqrt(n)) bound {bound}",
522 res.separator.len()
523 );
524 assert!(!res.part_a.is_empty() && !res.part_b.is_empty());
526 }
527
528 #[test]
529 fn grid_partition_property_holds() {
530 let a = 5;
531 let n = a * a;
532 let (sep, edges) = build_grid(a);
533 let res = sep.separate().expect("separate");
534 check_separator(n, &edges, &res, (2 * n) / 3);
535 }
536
537 #[test]
538 fn path_has_size_one_separator() {
539 let n = 11;
540 let mut sep = PlanarSeparator::new(n);
541 let mut edges = Vec::new();
542 for i in 0..n - 1 {
543 sep.add_edge(i, i + 1).expect("edge");
544 edges.push((i, i + 1));
545 }
546 let res = sep.separate().expect("separate");
547 check_separator(n, &edges, &res, (2 * n) / 3);
548 assert_eq!(
549 res.separator.len(),
550 1,
551 "path centroid separator is one vertex"
552 );
553 }
554
555 #[test]
556 fn balanced_binary_tree_uses_centroid() {
557 let n = 15;
560 let mut sep = PlanarSeparator::new(n);
561 let mut edges = Vec::new();
562 for i in 1..n {
563 let p = (i - 1) / 2;
564 sep.add_edge(p, i).expect("edge");
565 edges.push((p, i));
566 }
567 let res = sep.separate().expect("separate");
568 check_separator(n, &edges, &res, (2 * n) / 3);
569 assert_eq!(res.separator.len(), 1, "tree separator is the centroid");
570 }
571
572 #[test]
573 fn star_separator_is_center() {
574 let n = 9;
575 let mut sep = PlanarSeparator::new(n);
576 let mut edges = Vec::new();
577 for i in 1..n {
578 sep.add_edge(0, i).expect("edge");
579 edges.push((0, i));
580 }
581 let res = sep.separate().expect("separate");
582 check_separator(n, &edges, &res, (2 * n) / 3);
583 assert_eq!(res.separator, vec![0]);
585 }
586
587 #[test]
588 fn disconnected_components_partition_with_empty_separator() {
589 let n = 6;
591 let mut sep = PlanarSeparator::new(n);
592 let edges = [(0, 1), (1, 2), (0, 2), (3, 4), (4, 5), (3, 5)];
593 for &(u, v) in &edges {
594 sep.add_edge(u, v).expect("edge");
595 }
596 let res = sep.separate().expect("separate");
597 check_separator(n, &edges, &res, (2 * n) / 3);
598 assert!(
599 res.separator.is_empty(),
600 "two equal triangles split with no separator"
601 );
602 assert_eq!(res.part_a.len(), 3);
603 assert_eq!(res.part_b.len(), 3);
604 }
605
606 #[test]
607 fn trivial_graphs_handled() {
608 let empty = PlanarSeparator::new(0);
609 let r0 = empty.separate().expect("separate");
610 assert!(r0.part_a.is_empty() && r0.separator.is_empty() && r0.part_b.is_empty());
611
612 let single = PlanarSeparator::new(1);
613 let r1 = single.separate().expect("separate");
614 assert_eq!(r1.part_a, vec![0]);
615 assert!(r1.separator.is_empty() && r1.part_b.is_empty());
616 }
617
618 #[test]
619 fn rejects_out_of_range_edge() {
620 let mut sep = PlanarSeparator::new(3);
621 assert!(sep.add_edge(0, 5).is_err());
622 }
623
624 #[test]
625 fn from_adjacency_list_round_trip() {
626 let mut g = AdjacencyList::new(4);
627 g.add_undirected_edge(0, 1).expect("edge");
628 g.add_undirected_edge(1, 2).expect("edge");
629 g.add_undirected_edge(2, 3).expect("edge");
630 let sep = PlanarSeparator::from_adjacency_list(&g).expect("build");
631 let res = sep.separate().expect("separate");
632 let edges = [(0, 1), (1, 2), (2, 3)];
633 check_separator(4, &edges, &res, (2 * 4) / 3);
634 }
635}