ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
PermutationStrategy.h
Go to the documentation of this file.
1/**
2 * @file PermutationStrategy.h
3 * @brief Priority-queue-based node-elimination ordering for tree decomposition.
4 *
5 * Originally taken and adapted from https://github.com/smaniu/treewidth
6 *
7 * Computing an optimal tree decomposition is NP-hard in general.
8 * @c PermutationStrategy implements a greedy heuristic that eliminates
9 * nodes from a graph one at a time in the order of a **priority** determined
10 * by a node statistic (by default, current degree). The sequence of
11 * eliminated nodes defines an **elimination ordering**, and the cliques
12 * formed during elimination define the bags of the resulting tree
13 * decomposition.
14 *
15 * The base class implements the **min-degree** heuristic. Subclasses
16 * can override @c compute_statistic() to implement other heuristics
17 * (e.g., min-fill, which counts the number of edges that would be added
18 * to the graph when the node is eliminated).
19 *
20 * A Fibonacci heap is used so that @c recompute() (decrease-key after
21 * neighbours are updated) runs in amortised O(1) time.
22 */
23#ifndef PermutationStrategy_h
24#define PermutationStrategy_h
25
26#include <boost/heap/fibonacci_heap.hpp>
27
28#include "Graph.h"
29
30/**
31 * @brief Node-elimination ordering strategy using a priority queue.
32 *
33 * Maintains a Fibonacci heap of nodes prioritised by a user-defined
34 * statistic. The main consumer is @c TreeDecomposition, which calls
35 * @c init_permutation() once and then repeatedly calls @c get_next() to
36 * obtain the next node to eliminate.
37 */
39protected:
40 /**
41 * @brief Entry in the priority queue.
42 *
43 * Nodes with a smaller @c val are eliminated first. Ties are broken
44 * by node ID (smaller ID first) to ensure a deterministic ordering.
45 */
46 struct node_type{
47 unsigned long id; ///< Node identifier
48 unsigned long val; ///< Priority statistic (smaller ⇒ higher priority)
49 /**
50 * @brief Compare two entries; lower @c val (or lower @c id on tie) wins.
51 * @param a Other entry to compare against.
52 * @return @c true if this entry has higher priority (smaller val or id).
53 */
54 bool operator<(const node_type &a) const{
55 return val>a.val?true:(val<a.val?false:id>a.id);
56 }
57 };
58 /** @brief The priority queue holding all not-yet-eliminated nodes. */
59 boost::heap::fibonacci_heap<node_type> queue;
60 /** @brief Maps node IDs to their heap handles for O(1) key updates. */
61 std::unordered_map<unsigned long,
62 boost::heap::fibonacci_heap<node_type>::handle_type> queue_nodes;
63
64public:
65 /**
66 * @brief Populate the priority queue with all nodes in @p graph.
67 *
68 * Must be called once before any calls to @c get_next().
69 *
70 * @param graph The graph whose nodes should be enqueued.
71 */
72 void init_permutation(Graph& graph){
73 for(auto node:graph.get_nodes()){
74 node_type nstruct;
75 nstruct.id = node;
76 nstruct.val = compute_statistic(node, graph);
77 queue_nodes[node]=queue.push(nstruct);
78 }
79 }
80
81 /**
82 * @brief Pop and discard the top node, returning the new queue size.
83 *
84 * @return Number of remaining nodes after the pop.
85 */
86 unsigned long emptyQ(){
87 node_type nstruct_temp = queue.top();
88 queue.pop();
89 unsigned long node_id = nstruct_temp.id;
90 queue_nodes.erase(node_id);
91 return queue.size();
92 }
93
94 /**
95 * @brief Recompute statistics for a subset of nodes and update the queue.
96 *
97 * Called after a node is eliminated to refresh the priorities of its
98 * former neighbours.
99 *
100 * @param nodes Set of node IDs whose statistics need updating.
101 * @param graph The current (modified) graph.
102 */
103 virtual void recompute(const std::unordered_set<unsigned long> &nodes, Graph& graph){
104 for(auto node:nodes){
105 node_type nstruct;
106 nstruct.id = node;
107 nstruct.val = compute_statistic(node, graph);
108 queue.update(queue_nodes[node], nstruct);
109 }
110 }
111
112 /**
113 * @brief Pop and return the node with the smallest statistic.
114 * @return ID of the eliminated node.
115 */
116 unsigned long get_next(){
117 node_type nstruct = queue.top();
118 unsigned long node_id = nstruct.id;
119 queue.pop();
120 queue_nodes.erase(node_id);
121 return node_id;
122 }
123
124 /**
125 * @brief Peek at the node with the smallest statistic (without removing it).
126 * @return ID of the top node.
127 */
128 unsigned long get_next_wo_delete() {
129 node_type nstruct = queue.top();
130 unsigned long node_id = nstruct.id;
131 return node_id;
132 }
133
134 /**
135 * @brief Pop the top node, then pop and return the new top node.
136 *
137 * The first (smallest) node is temporarily removed, the second-smallest
138 * is popped and returned, and the first is re-inserted.
139 *
140 * @return ID of the second-smallest node.
141 */
142 unsigned long get_second_next_delete() {
143 node_type nstruct_temp = queue.top();
144 queue.pop();
145 node_type nstruct = queue.top();
146 unsigned long node_id = nstruct.id;
147 queue.pop();
148 queue_nodes.erase(node_id);
149 queue.push(nstruct_temp);
150 return node_id;
151 }
152
153 /**
154 * @brief Peek at the node with the second-smallest statistic (no removal).
155 *
156 * Temporarily pops the top, peeks at the new top, then re-inserts.
157 *
158 * @return ID of the second-smallest node.
159 */
160 unsigned long get_second_next() {
161 node_type nstruct_temp = queue.top();
162 queue.pop();
163 node_type nstruct = queue.top();
164 unsigned long node_id = nstruct.id;
165 queue.push(nstruct_temp);
166 return node_id;
167 }
168
169 /**
170 * @brief Return the current number of nodes in the queue.
171 * @return Number of nodes remaining in the priority queue.
172 */
173 unsigned long Q_siz(){return queue.size();}
174 /**
175 * @brief Return @c true if the queue contains exactly one node.
176 * @return @c true iff exactly one node remains.
177 */
178 bool empty_but1() { return !(queue.size()>1); }
179 /**
180 * @brief Return @c true if the queue is empty.
181 * @return @c true iff no nodes remain.
182 */
183 bool empty() {return !(queue.size()>0);}
184
185protected:
186 /**
187 * @brief Compute the priority statistic for @p node in @p graph.
188 *
189 * The default implementation returns the degree (number of neighbours)
190 * of the node. Override in subclasses to implement other heuristics.
191 *
192 * @param node Node whose statistic is needed.
193 * @param graph Current graph state.
194 * @return Non-negative statistic value (smaller ⇒ eliminate sooner).
195 */
196 unsigned long compute_statistic(unsigned long node, Graph& graph)
197 {
198 if(graph.has_neighbours(node)){
199 return graph.get_neighbours(node).size();
200 }
201 else{
202 return 0;
203 }
204 }
205};
206
207#endif /* PermutationStrategy_h */
Undirected graph used in tree-decomposition computations.
Mutable adjacency-list graph over unsigned-long node IDs.
Definition Graph.h:33
const std::unordered_set< unsigned long > & get_nodes() const
Return the set of all node IDs in the graph.
Definition Graph.h:232
bool has_neighbours(unsigned long node) const
Return true if node has any adjacent edges.
Definition Graph.h:186
const std::unordered_set< unsigned long > & get_neighbours(unsigned long node) const
Return the neighbour set of node.
Definition Graph.h:222
Node-elimination ordering strategy using a priority queue.
unsigned long get_next()
Pop and return the node with the smallest statistic.
virtual void recompute(const std::unordered_set< unsigned long > &nodes, Graph &graph)
Recompute statistics for a subset of nodes and update the queue.
unsigned long get_second_next_delete()
Pop the top node, then pop and return the new top node.
boost::heap::fibonacci_heap< node_type > queue
The priority queue holding all not-yet-eliminated nodes.
unsigned long emptyQ()
Pop and discard the top node, returning the new queue size.
std::unordered_map< unsigned long, boost::heap::fibonacci_heap< node_type >::handle_type > queue_nodes
Maps node IDs to their heap handles for O(1) key updates.
unsigned long Q_siz()
Return the current number of nodes in the queue.
bool empty_but1()
Return true if the queue contains exactly one node.
unsigned long compute_statistic(unsigned long node, Graph &graph)
Compute the priority statistic for node in graph.
bool empty()
Return true if the queue is empty.
unsigned long get_second_next()
Peek at the node with the second-smallest statistic (no removal).
unsigned long get_next_wo_delete()
Peek at the node with the smallest statistic (without removing it).
void init_permutation(Graph &graph)
Populate the priority queue with all nodes in graph.
Entry in the priority queue.
unsigned long val
Priority statistic (smaller ⇒ higher priority)
bool operator<(const node_type &a) const
Compare two entries; lower val (or lower id on tie) wins.
unsigned long id
Node identifier.