arboretum_td/
rule_based_reducer.rs

1use 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                // unknown lowerbound
52                self.lower_bound = 0;
53                return;
54            }
55
56            #[cfg(feature = "cli")]
57            if crate::timeout::timeout() {
58                // unknown lowerbound
59                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        // islet
100        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            // unknown lowerbound
114            self.lower_bound = 0;
115            return false;
116        }
117
118        #[cfg(feature = "cli")]
119        if crate::timeout::timeout() {
120            // unknown lowerbound
121            self.lower_bound = 0;
122            return false;
123        }
124
125        // single-degree
126
127        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            // unknown lowerbound
141            self.lower_bound = 0;
142            return false;
143        }
144        #[cfg(feature = "cli")]
145        if crate::timeout::timeout() {
146            // unknown lowerbound
147            self.lower_bound = 0;
148            return false;
149        }
150
151        // series
152        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            // unknown lowerbound
168            self.lower_bound = 0;
169            return false;
170        }
171        #[cfg(feature = "cli")]
172        if crate::timeout::timeout() {
173            // unknown lowerbound
174            self.lower_bound = 0;
175            return false;
176        }
177
178        // triangle rule
179        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            // unknown lowerbound
207            self.lower_bound = 0;
208            return false;
209        }
210        #[cfg(feature = "cli")]
211        if crate::timeout::timeout() {
212            // unknown lowerbound
213            self.lower_bound = 0;
214            return false;
215        }
216
217        // buddy rule
218        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            // unknown lowerbound
238            self.lower_bound = 0;
239            return false;
240        }
241        #[cfg(feature = "cli")]
242        if crate::timeout::timeout() {
243            // unknown lowerbound
244            self.lower_bound = 0;
245            return false;
246        }
247
248        // cube rule
249        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            // unknown lowerbound
337            self.lower_bound = 0;
338            return false;
339        }
340        #[cfg(feature = "cli")]
341        if crate::timeout::timeout() {
342            // unknown lowerbound
343            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            // unknown lowerbound
363            self.lower_bound = 0;
364            return false;
365        }
366        #[cfg(feature = "cli")]
367        if crate::timeout::timeout() {
368            // unknown lowerbound
369            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        // remove all low degree vertices. First remove all islets, then all islets and 1-degree,
408        // then all islets, 1-degree and 2-degree
409        for d in 0..3 {
410            #[cfg(feature = "handle-ctrlc")]
411            if received_ctrl_c() {
412                // unknown lowerbound
413                self.lower_bound = 0;
414                return;
415            }
416            #[cfg(feature = "cli")]
417            if crate::timeout::timeout() {
418                // unknown lowerbound
419                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                    // unknown lowerbound
440                    self.lower_bound = 0;
441                    return;
442                }
443                #[cfg(feature = "cli")]
444                if crate::timeout::timeout() {
445                    // unknown lowerbound
446                    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}