arboretum_td/
rule_based_reducer.rs1use crate::graph::BaseGraph;
2use crate::graph::HashMapGraph;
3use crate::graph::MutableGraph;
4use crate::tree_decomposition::TreeDecomposition;
5use fxhash::FxHashSet;
6use std::borrow::BorrowMut;
7use std::cmp::max;
8use std::collections::VecDeque;
9
10#[cfg(feature = "handle-ctrlc")]
11use crate::signals::received_ctrl_c;
12
13#[inline]
14fn eliminate(v: usize, graph: &mut HashMapGraph, stack: &mut Vec<FxHashSet<usize>>) {
15 let mut bag = graph.neighborhood_set(v).clone();
16 bag.insert(v);
17 stack.push(bag);
18 graph.eliminate_vertex(v);
19}
20
21struct Cube {
22 first_neighbor_of_x: usize,
23 second_neighbor_of_x: usize,
24 neighbor_of_y: usize,
25 to_eliminate: usize,
26 v: usize,
27}
28
29pub struct RuleBasedPreprocessor {
30 stack: Vec<FxHashSet<usize>>,
31 pub lower_bound: usize,
32 partial_tree_decomposition: TreeDecomposition,
33 processed_graph: HashMapGraph,
34}
35
36impl RuleBasedPreprocessor {
37 pub fn preprocess(&mut self) {
38 self.lower_bound = 0;
39 if self.processed_graph.order() == 0 {
40 return;
41 }
42
43 self.remove_low_degree();
44 if self.processed_graph.order() == 0 {
45 self.process_stack();
46 return;
47 }
48 while self.apply_rules() {
49 #[cfg(feature = "handle-ctrlc")]
50 if received_ctrl_c() {
51 self.lower_bound = 0;
53 return;
54 }
55
56 #[cfg(feature = "cli")]
57 if crate::timeout::timeout() {
58 self.lower_bound = 0;
60 return;
61 }
62 }
63 if self.processed_graph.order() == 0 {
64 self.process_stack();
65 }
66 }
67
68 pub fn combine_into_td(mut self, td: TreeDecomposition) -> TreeDecomposition {
69 let mut vertices = FxHashSet::default();
70 for bag in &td.bags {
71 vertices.extend(bag.vertex_set.iter().copied())
72 }
73 self.stack.push(vertices);
74 self.process_stack();
75 self.partial_tree_decomposition.flatten();
76 self.partial_tree_decomposition.replace_bag(0, td);
77 self.partial_tree_decomposition
78 }
79
80 pub fn into_td(mut self) -> TreeDecomposition {
81 self.process_stack();
82 self.partial_tree_decomposition
83 }
84
85 pub fn graph(&self) -> &HashMapGraph {
86 &self.processed_graph
87 }
88
89 pub fn new(graph: &HashMapGraph) -> Self {
90 Self {
91 stack: vec![],
92 lower_bound: 0,
93 partial_tree_decomposition: TreeDecomposition::default(),
94 processed_graph: graph.clone(),
95 }
96 }
97
98 fn apply_rules(&mut self) -> bool {
99 let found = self
101 .processed_graph
102 .vertices()
103 .find(|v| self.processed_graph.degree(*v) == 0);
104 if let Some(v) = found {
105 let mut bag = FxHashSet::with_capacity_and_hasher(1, Default::default());
106 bag.insert(v);
107 self.stack.push(bag);
108 self.processed_graph.remove_vertex(v);
109 return true;
110 }
111 #[cfg(feature = "handle-ctrlc")]
112 if received_ctrl_c() {
113 self.lower_bound = 0;
115 return false;
116 }
117
118 #[cfg(feature = "cli")]
119 if crate::timeout::timeout() {
120 self.lower_bound = 0;
122 return false;
123 }
124
125 let found = self
128 .processed_graph
129 .vertices()
130 .find(|v| self.processed_graph.degree(*v) == 1);
131 if let Some(v) = found {
132 let mut bag = self.processed_graph.neighborhood_set(v).clone();
133 bag.insert(v);
134 self.stack.push(bag);
135 self.processed_graph.remove_vertex(v);
136 return true;
137 }
138 #[cfg(feature = "handle-ctrlc")]
139 if received_ctrl_c() {
140 self.lower_bound = 0;
142 return false;
143 }
144 #[cfg(feature = "cli")]
145 if crate::timeout::timeout() {
146 self.lower_bound = 0;
148 return false;
149 }
150
151 let found = self
153 .processed_graph
154 .vertices()
155 .find(|v| self.processed_graph.degree(*v) == 2);
156 if let Some(v) = found {
157 eliminate(
158 v,
159 self.processed_graph.borrow_mut(),
160 self.stack.borrow_mut(),
161 );
162 self.lower_bound = max(self.lower_bound, 3);
163 return true;
164 }
165 #[cfg(feature = "handle-ctrlc")]
166 if received_ctrl_c() {
167 self.lower_bound = 0;
169 return false;
170 }
171 #[cfg(feature = "cli")]
172 if crate::timeout::timeout() {
173 self.lower_bound = 0;
175 return false;
176 }
177
178 self.lower_bound = max(self.lower_bound, 4);
180
181 let found = self
182 .processed_graph
183 .vertices()
184 .filter(|v| self.processed_graph.degree(*v) == 3)
185 .find(|v| {
186 let tmp: Vec<_> = self
187 .processed_graph
188 .neighborhood_set(*v)
189 .iter()
190 .copied()
191 .collect();
192 self.processed_graph.has_edge(tmp[0], tmp[1])
193 || self.processed_graph.has_edge(tmp[0], tmp[2])
194 || self.processed_graph.has_edge(tmp[1], tmp[2])
195 });
196 if let Some(v) = found {
197 eliminate(
198 v,
199 self.processed_graph.borrow_mut(),
200 self.stack.borrow_mut(),
201 );
202 return true;
203 }
204 #[cfg(feature = "handle-ctrlc")]
205 if received_ctrl_c() {
206 self.lower_bound = 0;
208 return false;
209 }
210 #[cfg(feature = "cli")]
211 if crate::timeout::timeout() {
212 self.lower_bound = 0;
214 return false;
215 }
216
217 let found = self.processed_graph.vertices().find(|v| {
219 self.processed_graph.vertices().any(|u| {
220 v < &u
221 && self.processed_graph.degree(*v) == 3
222 && self.processed_graph.degree(u) == 3
223 && self.processed_graph.neighborhood_set(u)
224 == self.processed_graph.neighborhood_set(*v)
225 })
226 });
227 if let Some(v) = found {
228 eliminate(
229 v,
230 self.processed_graph.borrow_mut(),
231 self.stack.borrow_mut(),
232 );
233 return true;
234 }
235 #[cfg(feature = "handle-ctrlc")]
236 if received_ctrl_c() {
237 self.lower_bound = 0;
239 return false;
240 }
241 #[cfg(feature = "cli")]
242 if crate::timeout::timeout() {
243 self.lower_bound = 0;
245 return false;
246 }
247
248 let mut cube: Option<Cube> = None;
250 for v in self.processed_graph.vertices() {
251 if self.processed_graph.degree(v) != 3 {
252 continue;
253 }
254 let nb: Vec<_> = self
255 .processed_graph
256 .neighborhood_set(v)
257 .iter()
258 .copied()
259 .collect();
260 let (neighbor_x, neighbor_y, to_eliminate) = (nb[0], nb[1], nb[2]);
261 if self.processed_graph.degree(neighbor_x) != 3
262 || self.processed_graph.degree(neighbor_y) != 3
263 || self.processed_graph.degree(to_eliminate) != 3
264 {
265 continue;
266 }
267 let x_nb: Vec<_> = self
268 .processed_graph
269 .neighborhood_set(neighbor_x)
270 .iter()
271 .copied()
272 .collect();
273 let mut first_neighbor_of_x = if x_nb[0] == v { x_nb[2] } else { x_nb[0] };
274 let mut second_neighbor_of_x = if x_nb[1] == v { x_nb[2] } else { x_nb[1] };
275 if !(self
276 .processed_graph
277 .has_edge(neighbor_y, first_neighbor_of_x)
278 && self
279 .processed_graph
280 .has_edge(to_eliminate, second_neighbor_of_x))
281 {
282 std::mem::swap(&mut first_neighbor_of_x, &mut second_neighbor_of_x);
283 }
284 if !(self
285 .processed_graph
286 .has_edge(neighbor_y, first_neighbor_of_x)
287 && self
288 .processed_graph
289 .has_edge(to_eliminate, second_neighbor_of_x))
290 {
291 continue;
292 }
293 let neighbor_of_y = self
294 .processed_graph
295 .neighborhood(neighbor_y)
296 .find(|c| *c != v && self.processed_graph.has_edge(to_eliminate, *c));
297 if neighbor_of_y.is_none() {
298 return false;
299 }
300 let neighbor_of_y = neighbor_of_y.unwrap();
301
302 cube = Some(Cube {
303 first_neighbor_of_x,
304 second_neighbor_of_x,
305 neighbor_of_y,
306 to_eliminate,
307 v,
308 });
309 break;
310 }
311
312 if let Some(cube) = cube {
313 let mut bag = FxHashSet::with_capacity_and_hasher(4, Default::default());
314 bag.insert(cube.second_neighbor_of_x);
315 bag.insert(cube.to_eliminate);
316 bag.insert(cube.v);
317 bag.insert(cube.neighbor_of_y);
318 self.processed_graph.remove_vertex(cube.to_eliminate);
319 self.processed_graph
320 .add_edge(cube.first_neighbor_of_x, cube.second_neighbor_of_x);
321 self.processed_graph
322 .add_edge(cube.first_neighbor_of_x, cube.neighbor_of_y);
323 self.processed_graph
324 .add_edge(cube.first_neighbor_of_x, cube.v);
325 self.processed_graph
326 .add_edge(cube.second_neighbor_of_x, cube.neighbor_of_y);
327 self.processed_graph
328 .add_edge(cube.second_neighbor_of_x, cube.v);
329 self.processed_graph.add_edge(cube.neighbor_of_y, cube.v);
330 self.stack.push(bag);
331
332 return true;
333 }
334 #[cfg(feature = "handle-ctrlc")]
335 if received_ctrl_c() {
336 self.lower_bound = 0;
338 return false;
339 }
340 #[cfg(feature = "cli")]
341 if crate::timeout::timeout() {
342 self.lower_bound = 0;
344 return false;
345 }
346
347 let found = self
348 .processed_graph
349 .vertices()
350 .find(|v| self.processed_graph.is_simplicial(*v));
351 if let Some(v) = found {
352 self.lower_bound = max(self.lower_bound, self.processed_graph.degree(v));
353 eliminate(
354 v,
355 self.processed_graph.borrow_mut(),
356 self.stack.borrow_mut(),
357 );
358 return true;
359 }
360 #[cfg(feature = "handle-ctrlc")]
361 if received_ctrl_c() {
362 self.lower_bound = 0;
364 return false;
365 }
366 #[cfg(feature = "cli")]
367 if crate::timeout::timeout() {
368 self.lower_bound = 0;
370 return false;
371 }
372
373 let found = self.processed_graph.vertices().find(|v| {
374 self.lower_bound > self.processed_graph.degree(*v)
375 && self.processed_graph.is_almost_simplicial(*v)
376 });
377 if let Some(v) = found {
378 eliminate(
379 v,
380 self.processed_graph.borrow_mut(),
381 self.stack.borrow_mut(),
382 );
383 return true;
384 }
385 false
386 }
387
388 fn process_stack(&mut self) {
389 for new_bag in self.stack.drain(..).rev() {
390 if let Some(old_bag) = self.partial_tree_decomposition.dfs().find(|old_bag| {
391 new_bag
392 .iter()
393 .filter(|v| !old_bag.vertex_set.contains(*v))
394 .count()
395 <= 1
396 }) {
397 let old_id = old_bag.id;
398 let id = self.partial_tree_decomposition.add_bag(new_bag);
399 self.partial_tree_decomposition.add_edge(old_id, id);
400 } else {
401 self.partial_tree_decomposition.add_bag(new_bag);
402 }
403 }
404 }
405
406 fn remove_low_degree(&mut self) {
407 for d in 0..3 {
410 #[cfg(feature = "handle-ctrlc")]
411 if received_ctrl_c() {
412 self.lower_bound = 0;
414 return;
415 }
416 #[cfg(feature = "cli")]
417 if crate::timeout::timeout() {
418 self.lower_bound = 0;
420 return;
421 }
422
423 let mut visited: FxHashSet<usize> = FxHashSet::with_capacity_and_hasher(
424 self.processed_graph.order(),
425 Default::default(),
426 );
427 let mut queue = VecDeque::new();
428 self.processed_graph
429 .vertices()
430 .filter(|v| self.processed_graph.degree(*v) <= d)
431 .for_each(|v| {
432 visited.insert(v);
433 queue.push_back(v);
434 });
435
436 while let Some(v) = queue.pop_front() {
437 #[cfg(feature = "handle-ctrlc")]
438 if received_ctrl_c() {
439 self.lower_bound = 0;
441 return;
442 }
443 #[cfg(feature = "cli")]
444 if crate::timeout::timeout() {
445 self.lower_bound = 0;
447 return;
448 }
449
450 let mut nb: FxHashSet<_> = self.processed_graph.neighborhood(v).collect();
451 if nb.len() > d {
452 continue;
453 }
454 self.processed_graph.eliminate_vertex(v);
455 nb.iter().copied().for_each(|v| {
456 if self.processed_graph.has_vertex(v)
457 && self.processed_graph.degree(v) <= d
458 && !visited.contains(&v)
459 {
460 queue.push_back(v);
461 visited.insert(v);
462 }
463 });
464 nb.insert(v);
465 self.lower_bound = max(self.lower_bound, nb.len() - 1);
466 self.stack.push(nb);
467 }
468 }
469 }
470}