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 #[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 #[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 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#[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}