arboretum_td/
heuristic_elimination_order.rs

1use crate::datastructures::BinaryQueue;
2use crate::graph::BaseGraph;
3use crate::graph::HashMapGraph;
4use crate::graph::MutableGraph;
5use crate::solver::{AtomSolver, Bounds, ComputationResult};
6use crate::tree_decomposition::TreeDecomposition;
7use fxhash::{FxHashMap, FxHashSet};
8#[cfg(feature = "log")]
9use log::info;
10use std::cmp::max;
11
12pub struct MinFillDegreeSelector {
13    inner: MinFillSelector,
14}
15
16impl From<HashMapGraph> for MinFillDegreeSelector {
17    fn from(graph: HashMapGraph) -> Self {
18        Self {
19            inner: MinFillSelector::from(graph),
20        }
21    }
22}
23
24impl Selector for MinFillDegreeSelector {
25    fn graph(&self) -> &HashMapGraph {
26        self.inner.graph()
27    }
28
29    fn value(&self, v: usize) -> i64 {
30        (self.inner.value(v) << 32) + (self.inner.graph.degree(v) as i64)
31    }
32
33    fn eliminate_vertex(&mut self, v: usize) {
34        self.inner.eliminate_vertex(v);
35    }
36}
37
38pub struct MinDegreeSelector {
39    graph: HashMapGraph,
40}
41
42impl From<HashMapGraph> for MinDegreeSelector {
43    fn from(graph: HashMapGraph) -> Self {
44        Self { graph }
45    }
46}
47
48impl Selector for MinDegreeSelector {
49    fn graph(&self) -> &HashMapGraph {
50        &self.graph
51    }
52
53    fn value(&self, v: usize) -> i64 {
54        self.graph.degree(v) as i64
55    }
56
57    fn eliminate_vertex(&mut self, v: usize) {
58        self.graph.eliminate_vertex(v);
59    }
60}
61
62pub struct MinFillSelector {
63    graph: HashMapGraph,
64    cache: FxHashMap<usize, usize>,
65}
66
67impl From<HashMapGraph> for MinFillSelector {
68    fn from(graph: HashMapGraph) -> Self {
69        let mut cache = FxHashMap::with_capacity_and_hasher(graph.order(), Default::default());
70        for u in graph.vertices() {
71            cache.insert(u, 0);
72        }
73        for u in graph.vertices() {
74            for v in graph.vertices().filter(|v| u < *v && graph.has_edge(u, *v)) {
75                graph
76                    .neighborhood_set(u)
77                    .iter()
78                    .copied()
79                    .filter(|x| v < *x && graph.has_edge(*x, v))
80                    .for_each(|x| {
81                        *cache.get_mut(&x).unwrap() += 1;
82                        *cache.get_mut(&u).unwrap() += 1;
83                        *cache.get_mut(&v).unwrap() += 1;
84                    })
85            }
86        }
87        Self { graph, cache }
88    }
89}
90
91impl Selector for MinFillSelector {
92    fn graph(&self) -> &HashMapGraph {
93        &self.graph
94    }
95
96    fn value(&self, v: usize) -> i64 {
97        self.fill_in_count(v) as i64
98    }
99
100    fn eliminate_vertex(&mut self, v: usize) {
101        self.eliminate_with_info(v);
102    }
103}
104
105impl MinFillSelector {
106    pub(crate) fn add_edge(&mut self, u: usize, v: usize) {
107        self.graph.add_edge(u, v);
108        for x in self.graph.neighborhood_set(u) {
109            if self.graph.has_edge(*x, v) {
110                *self.cache.get_mut(x).unwrap() += 1;
111                *self.cache.get_mut(&u).unwrap() += 1;
112                *self.cache.get_mut(&v).unwrap() += 1;
113            }
114        }
115    }
116
117    pub(crate) fn add_edges<'a, T: IntoIterator<Item = &'a (usize, usize)>>(&mut self, iter: T) {
118        for (u, v) in iter {
119            self.add_edge(*u, *v);
120        }
121    }
122
123    pub(crate) fn remove_edges<'a, T: IntoIterator<Item = &'a (usize, usize)>>(&mut self, iter: T) {
124        for (u, v) in iter {
125            self.remove_edge(*u, *v);
126        }
127    }
128
129    fn remove_vertex(&mut self, u: usize) {
130        for v in self.graph.neighborhood_set(u).clone() {
131            self.remove_edge(u, v);
132        }
133        self.graph.remove_vertex(u);
134        self.cache.remove(&u);
135    }
136
137    pub(crate) fn remove_edge(&mut self, u: usize, v: usize) {
138        self.graph.remove_edge(u, v);
139
140        for x in self.graph.neighborhood_set(u) {
141            if self.graph.has_edge(*x, v) {
142                *self.cache.get_mut(x).unwrap() -= 1;
143                *self.cache.get_mut(&u).unwrap() -= 1;
144                *self.cache.get_mut(&v).unwrap() -= 1;
145            }
146        }
147    }
148
149    fn eliminate_fill0(&mut self, u: usize) -> FillInfo {
150        if self.graph.degree(u) > 1 {
151            let delta = self.graph.degree(u) - 1;
152            let graph = &self.graph;
153            let cache = &mut self.cache;
154            graph.neighborhood_set(u).iter().copied().for_each(|v| {
155                *cache.get_mut(&v).unwrap() -= delta;
156            });
157        }
158        let neighborhood = self.graph.neighborhood_set(u).clone();
159        self.graph.remove_vertex(u);
160        FillInfo {
161            added_edges: vec![],
162            neighborhood,
163            eliminated_vertex: u,
164        }
165    }
166
167    pub(crate) fn fill_in_count(&self, u: usize) -> usize {
168        let deg = self.graph.degree(u);
169        (deg * deg - deg) / 2 - self.cache.get(&u).unwrap()
170    }
171
172    pub(crate) fn eliminate_with_info(&mut self, v: usize) -> FillInfo {
173        if self.fill_in_count(v) == 0 {
174            self.eliminate_fill0(v)
175        } else {
176            let neighborhood = self.graph.neighborhood_set(v).clone();
177            let mut added_edges: Vec<(usize, usize)> = vec![];
178            for u in self.graph.neighborhood_set(v) {
179                for w in self
180                    .graph
181                    .neighborhood_set(v)
182                    .iter()
183                    .filter(|w| u < *w && !self.graph.has_edge(*u, **w))
184                {
185                    added_edges.push((*u, *w));
186                }
187            }
188            for (u, w) in &added_edges {
189                self.add_edge(*u, *w);
190            }
191            self.remove_vertex(v);
192            FillInfo {
193                added_edges,
194                neighborhood,
195                eliminated_vertex: v,
196            }
197        }
198    }
199
200    pub(crate) fn undo_elimination(&mut self, fill_info: FillInfo) {
201        for (u, v) in fill_info.added_edges {
202            self.add_edge(u, v);
203        }
204        self.graph.add_vertex(fill_info.eliminated_vertex);
205        self.cache
206            .insert(fill_info.eliminated_vertex, Default::default());
207        for u in fill_info.neighborhood {
208            self.add_edge(u, fill_info.eliminated_vertex);
209        }
210    }
211}
212
213pub(crate) struct FillInfo {
214    added_edges: Vec<(usize, usize)>,
215    neighborhood: FxHashSet<usize>,
216    eliminated_vertex: usize,
217}
218
219pub trait Selector: From<HashMapGraph> {
220    fn graph(&self) -> &HashMapGraph;
221    fn value(&self, v: usize) -> i64;
222    fn eliminate_vertex(&mut self, v: usize);
223}
224
225pub type MinFillDecomposer = HeuristicEliminationDecomposer<MinFillSelector>;
226pub type MinDegreeDecomposer = HeuristicEliminationDecomposer<MinDegreeSelector>;
227pub type MinFillDegree = HeuristicEliminationDecomposer<MinFillDegreeSelector>;
228
229pub struct HeuristicEliminationDecomposer<S: Selector> {
230    selector: S,
231    lowerbound: usize,
232    upperbound: usize,
233}
234
235pub struct EliminationOrderDecomposer {
236    graph: HashMapGraph,
237    permutation: Vec<usize>,
238    eliminated_in_bag: FxHashMap<usize, usize>,
239}
240
241impl EliminationOrderDecomposer {
242    pub fn new(graph: HashMapGraph, permutation: Vec<usize>) -> Self {
243        Self {
244            graph,
245            permutation,
246            eliminated_in_bag: Default::default(),
247        }
248    }
249
250    pub fn compute(mut self) -> PermutationDecompositionResult {
251        let mut tree_decomposition: TreeDecomposition = Default::default();
252        for u in self.permutation.iter().copied() {
253            let nb: FxHashSet<usize> = self.graph.neighborhood(u).collect();
254            let mut bag = nb.clone();
255            bag.insert(u);
256            self.eliminated_in_bag
257                .insert(u, tree_decomposition.add_bag(bag));
258            self.graph.eliminate_vertex(u);
259        }
260        permutation_td_connect_helper(
261            &mut tree_decomposition,
262            &self.permutation,
263            &self.eliminated_in_bag,
264        );
265
266        PermutationDecompositionResult {
267            permutation: self.permutation,
268            tree_decomposition,
269            eliminated_in_bag: self.eliminated_in_bag,
270        }
271    }
272}
273
274fn permutation_td_connect_helper(
275    tree_decomposition: &mut TreeDecomposition,
276    permutation: &[usize],
277    eliminated_in_bag: &FxHashMap<usize, usize>,
278) {
279    for v in permutation.iter() {
280        let bag_id = eliminated_in_bag.get(v).unwrap();
281
282        let mut neighbor: Option<usize> = None;
283        for u in tree_decomposition.bags[*bag_id].vertex_set.iter() {
284            let candidate_neighbor = &tree_decomposition.bags[*eliminated_in_bag
285                .get(u)
286                .unwrap_or(&(tree_decomposition.bags.len() - 1))];
287            if candidate_neighbor.id == *bag_id {
288                continue;
289            }
290            neighbor = {
291                if neighbor.is_none() || neighbor.unwrap() > candidate_neighbor.id {
292                    Some(candidate_neighbor.id)
293                } else {
294                    neighbor
295                }
296            };
297        }
298        if let Some(neighbor) = neighbor {
299            tree_decomposition.add_edge(*bag_id, neighbor);
300        }
301    }
302}
303
304impl<S: Selector> HeuristicEliminationDecomposer<S> {
305    pub fn compute_order_and_decomposition(self) -> Option<PermutationDecompositionResult> {
306        #[cfg(feature = "log")]
307        info!("computing heuristic elimination td");
308        let mut selector = self.selector;
309        let mut permutation: Vec<usize> = vec![];
310        let upperbound = self.upperbound;
311        let lowerbound = self.lowerbound;
312        let mut tree_decomposition = TreeDecomposition::default();
313        let mut eliminated_in_bag: FxHashMap<usize, usize> = FxHashMap::default();
314
315        if selector.graph().order() > self.lowerbound + 1 {
316            let mut max_bag = 2;
317            let mut pq = BinaryQueue::new();
318            for v in selector.graph().vertices() {
319                pq.insert(v, selector.value(v))
320            }
321            while let Some((u, _)) = pq.pop_min() {
322                if selector.graph().order() <= max_bag || selector.graph().order() <= lowerbound + 1
323                {
324                    break;
325                }
326
327                #[cfg(feature = "handle-ctrlc")]
328                if crate::signals::received_ctrl_c() {
329                    // unknown lowerbound
330                    #[cfg(feature = "log")]
331                    info!("breaking heuristic elimination td due to ctrl+c");
332                    break;
333                }
334
335                #[cfg(feature = "cli")]
336                if crate::timeout::timeout() {
337                    // unknown lowerbound
338                    #[cfg(feature = "log")]
339                    info!("breaking heuristic elimination td due to timeout!");
340                    break;
341                }
342
343                if selector.graph().degree(u) > upperbound {
344                    return None;
345                }
346
347                let nb: FxHashSet<usize> = selector.graph().neighborhood(u).collect();
348                max_bag = max(max_bag, nb.len() + 1);
349                permutation.push(u);
350                let mut bag = nb.clone();
351                bag.insert(u);
352                eliminated_in_bag.insert(u, tree_decomposition.add_bag(bag));
353                selector.eliminate_vertex(u);
354                for u in nb {
355                    pq.insert(u, selector.value(u));
356                }
357            }
358        }
359
360        // remaining vertices, arbitrary order
361        while selector.graph().order() > 0 {
362            let u = selector.graph().vertices().next().unwrap();
363            let nb: FxHashSet<usize> = selector.graph().neighborhood(u).collect();
364            permutation.push(u);
365            let mut bag = nb.clone();
366            bag.insert(u);
367            eliminated_in_bag.insert(u, tree_decomposition.add_bag(bag));
368            selector.eliminate_vertex(u);
369        }
370
371        permutation_td_connect_helper(&mut tree_decomposition, &permutation, &eliminated_in_bag);
372
373        Some(PermutationDecompositionResult {
374            permutation,
375            tree_decomposition,
376            eliminated_in_bag,
377        })
378    }
379}
380
381pub struct PermutationDecompositionResult {
382    pub permutation: Vec<usize>,
383    pub tree_decomposition: TreeDecomposition,
384    pub eliminated_in_bag: FxHashMap<usize, usize>,
385}
386
387impl<S: Selector> AtomSolver for HeuristicEliminationDecomposer<S> {
388    fn with_graph(graph: &HashMapGraph) -> Self {
389        Self {
390            selector: S::from(graph.clone()),
391            lowerbound: 0,
392            upperbound: if graph.order() > 0 {
393                graph.order() - 1
394            } else {
395                0
396            },
397        }
398    }
399
400    fn with_bounds(graph: &HashMapGraph, lowerbound: usize, upperbound: usize) -> Self {
401        Self {
402            selector: S::from(graph.clone()),
403            lowerbound,
404            upperbound,
405        }
406    }
407
408    fn compute(self) -> ComputationResult {
409        let bounds = Bounds {
410            lowerbound: self.lowerbound,
411            upperbound: self.upperbound,
412        };
413        match self.compute_order_and_decomposition() {
414            None => ComputationResult::Bounds(bounds),
415            Some(result) => ComputationResult::ComputedTreeDecomposition(result.tree_decomposition),
416        }
417    }
418}
419/*
420pub fn heuristic_elimination_decompose<S: Selector>(graph: HashMapGraph) -> TreeDecomposition {
421    let mut tree_decomposition = TreeDecomposition::default();
422    if graph.order() <= 2 {
423        tree_decomposition.add_bag(graph.vertices().collect());
424        return tree_decomposition;
425    }
426    let mut max_bag = 2;
427    let mut selector: S = S::from(graph);
428    let mut pq = BinaryQueue::new();
429
430    let mut bags: FxHashMap<usize, FxHashSet<usize>> = FxHashMap::default();
431    let mut eliminated_at: FxHashMap<usize, usize> = FxHashMap::default();
432
433    for v in selector.graph().vertices() {
434        pq.insert(v, selector.value(v))
435    }
436
437    let mut stack: Vec<usize> = vec![];
438    while let Some((u, _)) = pq.pop_min() {
439        if selector.graph().order() <= max_bag {
440            break;
441        }
442
443        #[cfg(feature = "handle-ctrlc")]
444        if received_ctrl_c() {
445            // simply adds all remaining vertices into a single bag
446            break;
447        }
448
449        let mut nb: FxHashSet<usize> = selector.graph().neighborhood(u).collect();
450        max_bag = max(max_bag, nb.len() + 1);
451        stack.push(u);
452        bags.insert(u, nb.clone());
453        eliminated_at.insert(u, stack.len() - 1);
454        selector.eliminate_vertex(u);
455
456        let tmp: Vec<_> = nb
457            .iter()
458            .copied()
459            .filter(|u| selector.graph().neighborhood_set(*u).len() < nb.len())
460            .collect();
461
462        // eliminate directly, as these are subsets of the current bag
463        /*for u in tmp {
464            selector.eliminate_vertex(u);
465            nb.remove(&u);
466            pq.remove(u);
467        }*/
468        for u in nb {
469            pq.insert(u, selector.value(u));
470        }
471    }
472
473    if selector.graph().order() > 0 {
474        let mut rest: FxHashSet<usize> = selector.graph().vertices().collect();
475        let u = rest.iter().next().copied().unwrap();
476        rest.remove(&u);
477        bags.insert(u, rest);
478        stack.push(u);
479        eliminated_at.insert(u, stack.len() - 1);
480    }
481
482    for v in stack.iter().rev() {
483        let mut nb = bags.remove(v).unwrap();
484        let old_bag_id = match tree_decomposition
485            .bags
486            .iter()
487            .find(|old_bag| old_bag.vertex_set.is_superset(&nb))
488        {
489            Some(old_bag) => Some(old_bag.id),
490            None => None,
491        };
492        match old_bag_id {
493            Some(old_bag_id) => {
494                nb.insert(*v);
495                let id = tree_decomposition.add_bag(nb);
496                tree_decomposition.add_edge(old_bag_id, id);
497            }
498            None => {
499                nb.insert(*v);
500                tree_decomposition.add_bag(nb);
501            }
502        }
503    }
504    tree_decomposition
505}*/
506
507#[cfg(test)]
508mod tests {
509    use crate::graph::BaseGraph;
510    use crate::graph::HashMapGraph;
511    use crate::graph::MutableGraph;
512    use crate::heuristic_elimination_order::{MinFillSelector, Selector};
513    use crate::io::PaceReader;
514    use fxhash::FxHashMap;
515    use std::convert::TryFrom;
516    use std::fs::File;
517    use std::io::BufReader;
518    use std::path::PathBuf;
519
520    #[test]
521    fn initial() {
522        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
523        d.push("tests");
524        d.push("td-validate");
525        d.push("test");
526        d.push("tw-solver-bugs");
527        d.push("dimacs_anna.gr");
528
529        let f = File::open(d).unwrap();
530        let reader = BufReader::new(f);
531        let reader = PaceReader(reader);
532        let graph = HashMapGraph::try_from(reader).unwrap();
533
534        let vertices: Vec<_> = graph.vertices().collect();
535        let fc1: FxHashMap<_, _> = vertices
536            .iter()
537            .map(|v| (*v, graph.fill_in_count(*v) as i64))
538            .collect();
539
540        let selector = MinFillSelector::from(graph);
541        let fc2: FxHashMap<_, _> = vertices.iter().map(|v| (*v, selector.value(*v))).collect();
542
543        for v in fc1.keys() {
544            assert_eq!(fc1.get(v).unwrap(), fc2.get(v).unwrap());
545        }
546    }
547
548    #[test]
549    fn eliminate() {
550        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
551        d.push("tests");
552        d.push("td-validate");
553        d.push("test");
554        d.push("tw-solver-bugs");
555        d.push("dimacs_anna.gr");
556
557        let f = File::open(d).unwrap();
558        let reader = BufReader::new(f);
559        let reader = PaceReader(reader);
560        let mut graph = HashMapGraph::try_from(reader).unwrap();
561        let mut selector = MinFillSelector::from(graph.clone());
562
563        let mut vertices: Vec<_> = graph.vertices().collect();
564
565        while let Some(v) = vertices.pop() {
566            graph.eliminate_vertex(v);
567            selector.eliminate_vertex(v);
568
569            let mut a: Vec<_> = selector.graph.vertices().collect();
570            a.sort_unstable();
571            let mut b: Vec<_> = graph.vertices().collect();
572            b.sort_unstable();
573            assert_eq!(a, b);
574            let fc1: FxHashMap<_, _> = vertices
575                .iter()
576                .map(|v| (*v, graph.fill_in_count(*v) as i64))
577                .collect();
578            let fc2: FxHashMap<_, _> = vertices.iter().map(|v| (*v, selector.value(*v))).collect();
579
580            for v in fc1.keys() {
581                assert_eq!(fc1.get(v).unwrap(), fc2.get(v).unwrap());
582            }
583        }
584    }
585}