ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
TreeDecomposition.h
Go to the documentation of this file.
1/**
2 * @file TreeDecomposition.h
3 * @brief Tree decomposition of a Boolean circuit for knowledge compilation.
4 *
5 * A **tree decomposition** of a graph @f$G=(V,E)@f$ is a tree @f$T@f$
6 * whose nodes are labelled with *bags* (subsets of @f$V@f$) such that:
7 * 1. Every vertex of @f$G@f$ appears in at least one bag.
8 * 2. For every edge @f$(u,v)\in E@f$, some bag contains both @f$u@f$
9 * and @f$v@f$.
10 * 3. For every vertex @f$v@f$, the bags containing @f$v@f$ form a
11 * connected sub-tree of @f$T@f$.
12 *
13 * The **treewidth** is one less than the maximum bag size. ProvSQL
14 * builds a tree decomposition of the primal graph of a @c BooleanCircuit
15 * (using the min-fill elimination heuristic via @c PermutationStrategy)
16 * and then feeds it to @c dDNNFTreeDecompositionBuilder to construct a
17 * d-DNNF. Tractable compilation is guaranteed when the treewidth is
18 * bounded (empirically ≤ @c MAX_TREEWIDTH).
19 *
20 * The decomposition can also be read from an external file in the
21 * standard PACE challenge format (via the streaming @c operator>>).
22 *
23 * @c bag_t is a strongly-typed wrapper around @c size_t, analogous to
24 * @c gate_t, used as a bag identifier within the tree.
25 */
26#ifndef TREE_DECOMPOSITION_H
27#define TREE_DECOMPOSITION_H
28
29#include <iostream>
30#include <string>
31#include <type_traits>
32#include <vector>
33#include <boost/container/static_vector.hpp>
34
35#include "flat_set.hpp"
36#include "BooleanCircuit.h"
37
38// Forward declaration for friend
40
41/**
42 * @brief Strongly-typed bag identifier for a tree decomposition.
43 *
44 * Wraps a @c size_t, analogous to @c gate_t.
45 */
46enum class bag_t : size_t {};
47
48/**
49 * @brief Tree decomposition of a Boolean circuit's primal graph.
50 *
51 * Provides constructors that compute the decomposition from a
52 * @c BooleanCircuit using the min-fill heuristic, or parse it from an
53 * input stream. After construction, @c makeFriendly() restructures the
54 * tree into the "friendly" normal form required by
55 * @c dDNNFTreeDecompositionBuilder.
56 */
58public:
59/** @brief Maximum supported treewidth. Circuits exceeding this cause an error. */
60static constexpr int MAX_TREEWIDTH = 10;
61/** @brief Preferred maximum arity of bags in the friendly form. */
62static constexpr int OPTIMAL_ARITY = 2;
63
64/**
65 * @brief Stack-allocated vector type bounded by @c MAX_TREEWIDTH+1 elements.
66 *
67 * Used for bag contents to avoid heap allocation for small, bounded sets.
68 */
69template<class T>
70using small_vector = boost::container::static_vector<T, MAX_TREEWIDTH+1>;
71
72/** @brief The type of a bag: a small flat set of gate IDs. */
74
75private:
76std::vector<Bag> bags; ///< Bag contents, indexed by @c bag_t
77std::vector<bag_t> parent; ///< Parent of each bag (root points to itself)
78std::vector<std::vector<bag_t> > children; ///< Children of each bag
79bag_t root; ///< Identifier of the root bag
80unsigned treewidth; ///< Treewidth of the decomposition
81
83
84/**
85 * @brief Find the bag whose gate set is closest to gate @p v (for rooting).
86 * @param v Gate to search for.
87 * @return ID of the bag that contains or is nearest to @p v.
88 */
90/**
91 * @brief Re-root the tree so that @p bag becomes the root.
92 * @param bag The bag to use as the new root.
93 */
94void reroot(bag_t bag);
95/**
96 * @brief Insert a new empty bag as a child of @p parent.
97 * @param parent Parent bag of the new empty bag.
98 * @param children Children to transfer from @p parent to the new bag.
99 * @return ID of the newly created bag.
100 */
101bag_t addEmptyBag(bag_t parent, const std::vector<bag_t> &children = std::vector<bag_t>());
102/**
103 * @brief Add gate @p g to the contents of bag @p b.
104 * @param g Gate to add.
105 * @param b Destination bag.
106 */
107void addGateToBag(gate_t g, bag_t b);
108
109/**
110 * @brief Mutable access to bag @p b.
111 * @param b Bag identifier.
112 * @return Reference to the bag's gate set.
113 */
115{
116 return bags[static_cast<std::underlying_type<bag_t>::type>(b)];
117}
118/**
119 * @brief Const access to bag @p b.
120 * @param b Bag identifier.
121 * @return Const reference to the bag's gate set.
122 */
123const Bag &getBag(bag_t b) const
124{
125 return bags[static_cast<std::underlying_type<bag_t>::type>(b)];
126}
127/**
128 * @brief Mutable access to the children of bag @p b.
129 * @param b Bag identifier.
130 * @return Reference to the vector of child bag IDs.
131 */
132std::vector<bag_t> &getChildren(bag_t b)
133{
134 return children[static_cast<std::underlying_type<bag_t>::type>(b)];
135}
136/**
137 * @brief Const access to the children of bag @p b.
138 * @param b Bag identifier.
139 * @return Const reference to the vector of child bag IDs.
140 */
141const std::vector<bag_t> &getChildren(bag_t b) const
142{
143 return children[static_cast<std::underlying_type<bag_t>::type>(b)];
144}
145/**
146 * @brief Return the parent of bag @p b.
147 * @param b Bag identifier.
148 * @return Parent bag identifier.
149 */
151{
152 return parent[static_cast<std::underlying_type<bag_t>::type>(b)];
153}
154/**
155 * @brief Set the parent of bag @p b to @p p.
156 * @param b Bag whose parent is to be set.
157 * @param p New parent bag.
158 */
160{
161 parent[static_cast<std::underlying_type<bag_t>::type>(b)]=p;
162}
163
164public:
165/**
166 * @brief Compute a tree decomposition of the primal graph of @p bc.
167 *
168 * Uses the min-fill elimination ordering heuristic. Throws
169 * @c TreeDecompositionException if the computed treewidth exceeds
170 * @c MAX_TREEWIDTH.
171 *
172 * @param bc The Boolean circuit to decompose.
173 */
175
176/**
177 * @brief Parse a tree decomposition from a stream (PACE challenge format).
178 *
179 * @param in Input stream containing the decomposition.
180 */
181TreeDecomposition(std::istream &in);
182
183/** @brief Copy constructor. @param td Source decomposition. */
185/**
186 * @brief Copy assignment.
187 * @param td Source decomposition.
188 * @return Reference to this decomposition after assignment.
189 */
191
192/**
193 * @brief Return the treewidth of this decomposition.
194 * @return Treewidth (maximum bag size minus one).
195 */
196unsigned getTreewidth() const {
197 return treewidth;
198}
199
200/**
201 * @brief Restructure the tree into the friendly normal form.
202 *
203 * Reroots the tree at the bag that covers the circuit's root gate
204 * and splits/merges bags so that @c dDNNFTreeDecompositionBuilder
205 * can process them efficiently.
206 *
207 * @param root The gate that should be covered by the root bag.
208 */
210
211/**
212 * @brief Render the tree decomposition as a GraphViz DOT string.
213 * @return DOT @c digraph string for visualisation.
214 */
215std::string toDot() const;
216
217friend std::istream& operator>>(std::istream& in, TreeDecomposition &td);
219};
220
221/**
222 * @brief Read a tree decomposition in PACE challenge format.
223 * @param in Input stream.
224 * @param td Tree decomposition to populate.
225 * @return Reference to @p in.
226 */
227std::istream& operator>>(std::istream& in, TreeDecomposition &td);
228
229/**
230 * @brief Pre-increment operator for @c bag_t.
231 * @param b Bag to increment.
232 * @return Reference to the incremented bag.
233 */
234inline bag_t &operator++(bag_t &b) {
235 return b=bag_t{static_cast<std::underlying_type<bag_t>::type>(b)+1};
236}
237/**
238 * @brief Post-increment operator for @c bag_t.
239 * @param b Bag to increment.
240 * @return Original value of @p b before the increment.
241 */
242inline bag_t operator++(bag_t &b, int) {
243 auto temp{b};
244 b=bag_t{static_cast<std::underlying_type<bag_t>::type>(b)+1};
245 return temp;
246}
247
248/**
249 * @brief Compare a @c bag_t against a @c std::vector size type.
250 * @param t Bag identifier.
251 * @param u Size to compare against.
252 * @return @c true if the underlying integer of @p t is less than @p u.
253 */
254inline bool operator<(bag_t t, std::vector<bag_t>::size_type u)
255{
256 return static_cast<std::underlying_type<bag_t>::type>(t)<u;
257}
258
259/**
260 * @brief Convert a @c bag_t to its decimal string representation.
261 * @param b Bag identifier.
262 * @return Decimal string of the underlying integer.
263 */
264inline std::string to_string(bag_t b) {
265 return std::to_string(static_cast<std::underlying_type<bag_t>::type>(b));
266}
267
268/**
269 * @brief Read a @c bag_t from an input stream.
270 * @param i Input stream.
271 * @param b Bag to populate.
272 * @return Reference to @p i.
273 */
274inline std::istream &operator>>(std::istream &i, bag_t &b)
275{
276 std::underlying_type<bag_t>::type u;
277 i >> u;
278 b=bag_t{u};
279 return i;
280}
281
282/**
283 * @brief Exception thrown when a tree decomposition cannot be constructed.
284 *
285 * Raised when the treewidth of the circuit exceeds @c MAX_TREEWIDTH or
286 * when the input stream is malformed.
287 */
288class TreeDecompositionException : public std::exception {};
289
290#endif /* TREE_DECOMPOSITION_H */
Boolean provenance circuit with support for knowledge compilation.
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:48
std::istream & operator>>(std::istream &in, TreeDecomposition &td)
Read a tree decomposition in PACE challenge format.
std::string to_string(bag_t b)
Convert a bag_t to its decimal string representation.
bag_t
Strongly-typed bag identifier for a tree decomposition.
bool operator<(bag_t t, std::vector< bag_t >::size_type u)
Compare a bag_t against a std::vector size type.
bag_t & operator++(bag_t &b)
Pre-increment operator for bag_t.
Boolean circuit for provenance formula evaluation.
Exception thrown when a tree decomposition cannot be constructed.
Tree decomposition of a Boolean circuit's primal graph.
friend std::istream & operator>>(std::istream &in, TreeDecomposition &td)
Read a tree decomposition in PACE challenge format.
bag_t findGateConnection(gate_t v) const
Find the bag whose gate set is closest to gate v (for rooting).
bag_t addEmptyBag(bag_t parent, const std::vector< bag_t > &children=std::vector< bag_t >())
Insert a new empty bag as a child of parent.
const std::vector< bag_t > & getChildren(bag_t b) const
Const access to the children of bag b.
std::vector< Bag > bags
Bag contents, indexed by bag_t.
TreeDecomposition(const TreeDecomposition &td)
Copy constructor.
void addGateToBag(gate_t g, bag_t b)
Add gate g to the contents of bag b.
void setParent(bag_t b, bag_t p)
Set the parent of bag b to p.
unsigned getTreewidth() const
Return the treewidth of this decomposition.
TreeDecomposition()=default
std::string toDot() const
Render the tree decomposition as a GraphViz DOT string.
void reroot(bag_t bag)
Re-root the tree so that bag becomes the root.
static constexpr int OPTIMAL_ARITY
Preferred maximum arity of bags in the friendly form.
const Bag & getBag(bag_t b) const
Const access to bag b.
std::vector< bag_t > parent
Parent of each bag (root points to itself)
boost::container::static_vector< T, MAX_TREEWIDTH+1 > small_vector
Stack-allocated vector type bounded by MAX_TREEWIDTH+1 elements.
unsigned treewidth
Treewidth of the decomposition.
bag_t getParent(bag_t b) const
Return the parent of bag b.
static constexpr int MAX_TREEWIDTH
Maximum supported treewidth.
std::vector< std::vector< bag_t > > children
Children of each bag.
bag_t root
Identifier of the root bag.
Bag & getBag(bag_t b)
Mutable access to bag b.
TreeDecomposition & operator=(const TreeDecomposition &td)
Copy assignment.
void makeFriendly(gate_t root)
Restructure the tree into the friendly normal form.
std::vector< bag_t > & getChildren(bag_t b)
Mutable access to the children of bag b.
Builds a d-DNNF from a Boolean circuit using a tree decomposition.
TreeDecomposition & td
Tree decomposition of the circuit's primal graph.
Flat (unsorted, contiguous-storage) set template.