1use arboretum_td::tree_decomposition::TreeDecomposition;
4use fxhash::FxHashSet;
5
6#[derive(Debug, Clone, Copy, Eq, PartialEq)]
8pub enum NiceTdNodeType {
9 Join,
11 Introduce(usize),
13 Forget(usize),
15 Leaf,
17}
18
19pub struct NiceTreeDecomposition {
23 pub td: TreeDecomposition,
25 pub mapping: Vec<NiceTdNodeType>,
27}
28
29impl NiceTreeDecomposition {
30 pub fn new(mut td: TreeDecomposition) -> Self {
32 let root = td.root.unwrap_or(0);
33 td.root = Some(root);
34
35 Self::nicify_multi_child_nodes(root, &td.bags()[root].neighbors.clone(), &mut td);
36 Self::nicify_double_child_nodes(root, &td.bags()[root].neighbors.clone(), &mut td);
37 Self::nicify_single_child_nodes(root, &td.bags()[root].neighbors.clone(), &mut td);
38 Self::nicify_leaf_nodes(root, &td.bags()[root].neighbors.clone(), &mut td);
39
40 let mut mapping: Vec<Option<NiceTdNodeType>> = vec![None; td.bags().len()];
41
42 assert!(Self::is_nice_td(
43 &td,
44 root,
45 &td.bags()[root].neighbors.clone(),
46 &mut mapping
47 ));
48 assert!(mapping.iter().all(|node_type| { node_type.is_some() }));
49
50 Self {
51 td: td.to_owned(),
52 mapping: mapping.iter().map(|node_type| node_type.unwrap()).collect(),
53 }
54 }
55
56 fn nicify_multi_child_nodes(
57 id: usize,
58 children: &FxHashSet<usize>,
59 td: &mut TreeDecomposition,
60 ) {
61 if children.len() <= 2 {
62 for child_id in children {
63 Self::nicify_multi_child_nodes(*child_id, &get_children(td, *child_id, id), td);
64 }
65
66 return;
67 }
68
69 let vertex_set: FxHashSet<usize> = td.bags()[id].vertex_set.clone();
70 let left_child_id = td.add_bag(vertex_set);
71 td.add_edge(id, left_child_id);
72 let mut it = children.iter();
73 let right_child_id = *it.next().unwrap();
74
75 for child_id in it {
76 Self::remove_edge(td, id, *child_id);
77 td.add_edge(left_child_id, *child_id);
78 }
79
80 Self::nicify_multi_child_nodes(left_child_id, &get_children(td, left_child_id, id), td);
81
82 Self::nicify_multi_child_nodes(right_child_id, &get_children(td, right_child_id, id), td);
83 }
84
85 fn nicify_double_child_nodes(
86 id: usize,
87 children: &FxHashSet<usize>,
88 td: &mut TreeDecomposition,
89 ) {
90 if children.len() != 2 {
91 for child_id in children {
92 Self::nicify_double_child_nodes(*child_id, &get_children(td, *child_id, id), td);
93 }
94
95 return;
96 }
97
98 let mut it = children.iter();
99 let left_child_id = *it.next().unwrap();
100 let right_child_id = *it.next().unwrap();
101 let vertex_set: FxHashSet<usize> = td.bags()[id].vertex_set.clone();
102 let new_left_child_id = td.add_bag(vertex_set.clone());
103 let new_right_child_id = td.add_bag(vertex_set);
104
105 Self::remove_edge(td, id, left_child_id);
106 Self::remove_edge(td, id, right_child_id);
107 td.add_edge(id, new_left_child_id);
108 td.add_edge(id, new_right_child_id);
109 td.add_edge(new_left_child_id, left_child_id);
110 td.add_edge(new_right_child_id, right_child_id);
111
112 Self::nicify_double_child_nodes(
113 left_child_id,
114 &get_children(td, left_child_id, new_left_child_id),
115 td,
116 );
117
118 Self::nicify_double_child_nodes(
119 right_child_id,
120 &get_children(td, right_child_id, new_right_child_id),
121 td,
122 );
123 }
124
125 fn nicify_single_child_nodes(
126 id: usize,
127 children: &FxHashSet<usize>,
128 td: &mut TreeDecomposition,
129 ) {
130 if children.len() != 1 {
131 for child_id in children {
132 Self::nicify_single_child_nodes(*child_id, &get_children(td, *child_id, id), td);
133 }
134
135 return;
136 }
137
138 let mut vertex_set: FxHashSet<usize> = td.bags()[id].vertex_set.clone();
139 let child_id = *children.iter().next().unwrap();
140 let child_vertex_set: FxHashSet<usize> = td.bags()[child_id].vertex_set.clone();
141
142 if vertex_set.eq(&child_vertex_set.clone()) {
143 Self::remove_edge(td, id, child_id);
144
145 for grandchild_id in get_children(td, child_id, id) {
146 td.add_edge(id, grandchild_id);
147 Self::remove_edge(td, child_id, grandchild_id);
148 Self::nicify_single_child_nodes(
149 grandchild_id,
150 &get_children(td, grandchild_id, id),
151 td,
152 );
153 }
154
155 Self::remove_bag(td, child_id);
156
157 return;
158 }
159
160 let mut parent_id = id;
161
162 for v in vertex_set.clone().difference(&child_vertex_set) {
163 vertex_set.remove(v);
164
165 if vertex_set.eq(&child_vertex_set) {
166 break;
167 }
168
169 let new_child_id = td.add_bag(vertex_set.clone());
170 Self::remove_edge(td, parent_id, child_id);
171 td.add_edge(parent_id, new_child_id);
172 td.add_edge(new_child_id, child_id);
173 parent_id = new_child_id;
174 }
175
176 for v in child_vertex_set.difference(&vertex_set.clone()) {
177 vertex_set.insert(*v);
178
179 if vertex_set.eq(&child_vertex_set) {
180 break;
181 }
182
183 let new_child_id = td.add_bag(vertex_set.clone());
184 Self::remove_edge(td, parent_id, child_id);
185 td.add_edge(parent_id, new_child_id);
186 td.add_edge(new_child_id, child_id);
187 parent_id = new_child_id;
188 }
189
190 Self::nicify_single_child_nodes(child_id, &get_children(td, child_id, parent_id), td);
191 }
192
193 fn nicify_leaf_nodes(id: usize, children: &FxHashSet<usize>, td: &mut TreeDecomposition) {
194 if !children.is_empty() {
195 for child_id in children {
196 Self::nicify_leaf_nodes(*child_id, &get_children(td, *child_id, id), td);
197 }
198
199 return;
200 }
201
202 let bag = &td.bags()[id];
203 let mut vertex_set = bag.vertex_set.clone();
204 let mut parent_id = id;
205
206 while vertex_set.len() > 1 {
207 let v = *vertex_set.iter().next().unwrap();
208 vertex_set.remove(&v);
209 let new_child_id = td.add_bag(vertex_set.clone());
210 td.add_edge(parent_id, new_child_id);
211 parent_id = new_child_id;
212 }
213 }
214
215 fn is_nice_td(
216 td: &TreeDecomposition,
217 id: usize,
218 children: &FxHashSet<usize>,
219 mapping: &mut Vec<Option<NiceTdNodeType>>,
220 ) -> bool {
221 match Self::get_nice_td_node_type(td, id, children) {
222 Some(node_type) => {
223 mapping[id] = Some(node_type);
224 children.iter().all(|child_id| {
225 Self::is_nice_td(td, *child_id, &get_children(td, *child_id, id), mapping)
226 })
227 }
228 None => false,
229 }
230 }
231
232 fn get_nice_td_node_type(
233 td: &TreeDecomposition,
234 id: usize,
235 children: &FxHashSet<usize>,
236 ) -> Option<NiceTdNodeType> {
237 if Self::is_nice_td_join(td, id, children) {
238 return Some(NiceTdNodeType::Join);
239 } else if let Some(v) = Self::is_nice_td_intro(td, id, children) {
240 return Some(NiceTdNodeType::Introduce(v));
241 } else if let Some(v) = Self::is_nice_td_forget(td, id, children) {
242 return Some(NiceTdNodeType::Forget(v));
243 } else if Self::is_nice_td_leaf(td, id, children) {
244 return Some(NiceTdNodeType::Leaf);
245 }
246
247 None
248 }
249
250 fn is_nice_td_join(td: &TreeDecomposition, id: usize, children: &FxHashSet<usize>) -> bool {
251 let mut it = children.iter();
252 let left = it.next();
253 let right = it.next();
254 let none = it.next();
255
256 match (left, right, none) {
257 (Some(left), Some(right), None) => {
258 let vertex_set: &FxHashSet<usize> = &td.bags()[id].vertex_set;
259 let left_vertex_set: &FxHashSet<usize> = &td.bags()[*left].vertex_set;
260 let right_vertex_set: &FxHashSet<usize> = &td.bags()[*right].vertex_set;
261 vertex_set == left_vertex_set && vertex_set == right_vertex_set
262 }
263 _ => false,
264 }
265 }
266
267 fn is_nice_td_intro(
268 td: &TreeDecomposition,
269 id: usize,
270 children: &FxHashSet<usize>,
271 ) -> Option<usize> {
272 let mut it = children.iter();
273 let child = it.next();
274 let none = it.next();
275
276 match (child, none) {
277 (Some(child), None) => {
278 let vertex_set: &FxHashSet<usize> = &td.bags()[id].vertex_set;
279 let child_vertex_set: &FxHashSet<usize> = &td.bags()[*child].vertex_set;
280
281 if !vertex_set.is_superset(child_vertex_set)
282 || vertex_set.len() != child_vertex_set.len() + 1
283 {
284 return None;
285 }
286
287 Some(*vertex_set.difference(child_vertex_set).next().unwrap())
288 }
289 _ => None,
290 }
291 }
292
293 fn is_nice_td_forget(
294 td: &TreeDecomposition,
295 id: usize,
296 children: &FxHashSet<usize>,
297 ) -> Option<usize> {
298 let mut it = children.iter();
299 let child = it.next();
300
301 if it.next().is_some() {
302 return None;
303 }
304
305 match child {
306 Some(child) => {
307 let vertex_set: &FxHashSet<usize> = &td.bags()[id].vertex_set;
308 let child_vertex_set: &FxHashSet<usize> = &td.bags()[*child].vertex_set;
309
310 if !vertex_set.is_subset(child_vertex_set)
311 || vertex_set.len() + 1 != child_vertex_set.len()
312 {
313 return None;
314 }
315
316 Some(*child_vertex_set.difference(vertex_set).next().unwrap())
317 }
318 None => None,
319 }
320 }
321
322 fn is_nice_td_leaf(td: &TreeDecomposition, id: usize, children: &FxHashSet<usize>) -> bool {
323 let vertex_set = &td.bags()[id].vertex_set;
324
325 children.is_empty() && vertex_set.len() == 1
326 }
327
328 fn remove_edge(td: &mut TreeDecomposition, b1: usize, b2: usize) {
329 assert!(b1 < td.bags.len());
330 assert!(b2 < td.bags.len());
331 assert_ne!(b1, b2);
332 td.bags[b1].neighbors.remove(&b2);
333 td.bags[b2].neighbors.remove(&b1);
334 }
335
336 fn remove_bag(td: &mut TreeDecomposition, id: usize) {
338 assert!(td.bags[id].neighbors.is_empty());
339 if id == td.bags.len() - 1 {
340 td.bags.pop();
341 } else {
342 let old_last = td.bags.swap_remove(id);
343 assert!(old_last.neighbors.is_empty());
344 td.bags[id].id = id;
345 let old_last = td.bags.len();
346
347 for neighbor in td.bags[id].neighbors.clone() {
348 assert!(td.bags[neighbor].neighbors.remove(&old_last));
349 assert!(td.bags[neighbor].neighbors.insert(id));
350 }
351 }
352 }
353}
354
355pub fn get_children(td: &TreeDecomposition, id: usize, parent_id: usize) -> FxHashSet<usize> {
357 let mut children = td.bags()[id].neighbors.clone();
358 children.remove(&parent_id);
359
360 children
361}
362
363#[cfg(test)]
364mod tests {
365 use super::NiceTreeDecomposition;
366 use crate::{
367 algorithm::nice_tree_decomposition::get_children,
368 generation::erdos_renyi::generate_hash_map_graph,
369 };
370 use arboretum_td::{solver::Solver, tree_decomposition::TreeDecomposition};
371 use fxhash::FxHashSet;
372 use rand::{rngs::StdRng, Rng, SeedableRng};
373
374 #[test]
375 fn single_bag_with_1_vertex() {
376 let mut td = TreeDecomposition::default();
377 let mut vertex_set = FxHashSet::default();
378 vertex_set.insert(1);
379 td.add_bag(vertex_set);
380
381 let nice_td = NiceTreeDecomposition::new(td.clone());
382 assert!(nice_td.td.bags().len() == td.bags().len());
383 assert!(nice_td.td.bags()[0].vertex_set == td.bags()[0].vertex_set);
384 }
385
386 #[test]
387 fn single_bag_with_multiple_vertices() {
388 let mut td = TreeDecomposition::default();
389 let mut vertex_set = FxHashSet::default();
390 vertex_set.insert(1);
391 vertex_set.insert(2);
392 vertex_set.insert(3);
393 let id = td.add_bag(vertex_set);
394
395 let nice_td = NiceTreeDecomposition::new(td.clone());
396 let bag = &nice_td.td.bags()[id];
397 let child_id = *bag.neighbors.iter().next().unwrap();
398 let grandchild_id = get_child_bag_id(&nice_td.td, child_id, id).unwrap();
399
400 assert!(nice_td.td.bags().len() == 3);
401 assert!(bag.vertex_set.len() == 3);
402 assert!(
403 NiceTreeDecomposition::is_nice_td_intro(&nice_td.td, id, &bag.neighbors.clone())
404 .is_some()
405 );
406 assert!(NiceTreeDecomposition::is_nice_td_intro(
407 &nice_td.td,
408 child_id,
409 &get_children(&nice_td.td, child_id, id)
410 )
411 .is_some());
412 assert!(NiceTreeDecomposition::is_nice_td_leaf(
413 &nice_td.td,
414 grandchild_id,
415 &get_children(&nice_td.td, grandchild_id, child_id)
416 ));
417 }
418
419 #[test]
420 fn two_bags_with_multiple_vertices() {
421 let mut td = TreeDecomposition::default();
422 let mut vertex_set_1 = FxHashSet::default();
423 vertex_set_1.insert(1);
424 vertex_set_1.insert(2);
425 let mut vertex_set_2 = FxHashSet::default();
426 vertex_set_2.insert(2);
427 vertex_set_2.insert(3);
428
429 let id_1 = td.add_bag(vertex_set_1);
430 let id_2 = td.add_bag(vertex_set_2);
431 td.add_edge(id_1, id_2);
432
433 let nice_td = NiceTreeDecomposition::new(td.clone());
434 let bag = &nice_td.td.bags()[id_1];
435 let child_id = *bag.neighbors.iter().next().unwrap();
436 let grandchild_id = get_child_bag_id(&nice_td.td, child_id, id_1).unwrap();
437 let grandgrandchild_id = get_child_bag_id(&nice_td.td, grandchild_id, child_id).unwrap();
438
439 assert!(nice_td.td.bags().len() == 4);
440 assert_eq!(
441 NiceTreeDecomposition::is_nice_td_intro(&nice_td.td, id_1, &bag.neighbors.clone()),
442 Some(1)
443 );
444 assert_eq!(
445 NiceTreeDecomposition::is_nice_td_forget(
446 &nice_td.td,
447 child_id,
448 &get_children(&nice_td.td, child_id, id_1)
449 ),
450 Some(3)
451 );
452 assert!(NiceTreeDecomposition::is_nice_td_intro(
453 &nice_td.td,
454 grandchild_id,
455 &get_children(&nice_td.td, grandchild_id, child_id)
456 )
457 .is_some());
458 assert!(NiceTreeDecomposition::is_nice_td_leaf(
459 &nice_td.td,
460 grandgrandchild_id,
461 &get_children(&nice_td.td, grandgrandchild_id, grandchild_id)
462 ));
463 }
464
465 #[test]
466 fn random() {
467 let mut rng = StdRng::seed_from_u64(1);
468
469 for i in 0..100 {
470 let graph = generate_hash_map_graph(
471 rng.gen_range(1..30),
472 rng.gen_range(0.05..0.1),
473 Some(i as u64),
474 );
475 let td = Solver::default().solve(&graph);
476 let nice_td = NiceTreeDecomposition::new(td);
477 assert!(nice_td.td.verify(&graph).is_ok());
478 }
479 }
480
481 fn get_child_bag_id(td: &TreeDecomposition, id: usize, parent_id: usize) -> Option<usize> {
482 get_children(td, id, parent_id).iter().copied().next()
483 }
484}