ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
ReachabilityCompiler.h
Go to the documentation of this file.
1/**
2 * @file ReachabilityCompiler.h
3 * @brief Decomposition-aligned compilation of two-terminal reachability
4 * over bounded-treewidth data into a d-D.
5 *
6 * Implements the data-side counterpart of ProvSQL's circuit-side
7 * treewidth exploitation: instead of building a provenance circuit
8 * along the relational-algebra plan (whose treewidth can grow with the
9 * instance size) and decomposing it afterwards, the provenance of
10 * *s-t reachability* is built **along a tree decomposition of the data
11 * graph itself**, in the spirit of the provenance refinement of
12 * Courcelle's theorem (Amarilli, Bourhis & Senellart, ICALP 2015 /
13 * ICDT 2017).
14 *
15 * The construction is a bag-by-bag dynamic program whose state at a
16 * decomposition node is the transitively-closed reachability relation
17 * over the bag's vertices augmented with the two terminals @c s and
18 * @c t (equivalently, the standard DP over the decomposition obtained
19 * by adding @c s and @c t to every bag). Each transition emits d-D
20 * gates directly:
21 *
22 * - states at a node are **mutually exclusive and exhaustive** over the
23 * valuations of the edge variables introduced in its subtree, so
24 * every OR gate is deterministic *by construction*;
25 * - each edge variable is introduced at exactly one node, so the
26 * children of every AND gate mention disjoint variable sets and the
27 * circuit is decomposable *by construction*.
28 *
29 * No knowledge-compilation step is therefore needed: the result is fed
30 * straight to @c dDNNF::probabilityEvaluation(). For data of treewidth
31 * @f$k@f$ the d-D has size linear in the number of edges (times a
32 * function of @f$k@f$ only), which yields linear-time exact computation
33 * of two-terminal network reliability -- a @c \#P-hard problem in
34 * general -- on bounded-treewidth probabilistic graphs, including
35 * **cyclic** graphs that the recursive-query fixpoint cannot handle.
36 *
37 * Directed reachability and undirected connectivity are both
38 * supported: the DP state is a reachability *relation* (not a
39 * partition), so the undirected case is simply the directed case with
40 * each edge contributing both arcs.
41 */
42#ifndef REACHABILITY_COMPILER_H
43#define REACHABILITY_COMPILER_H
44
45#include <cstddef>
46#include <stdexcept>
47#include <string>
48#include <vector>
49
50#include "dDNNF.h"
51#include "TreeDecomposition.h"
52
53/**
54 * @brief Exception thrown when reachability compilation fails.
55 *
56 * Raised on unsupported input shapes (a provenance token shared by
57 * edges with different endpoints) or when the per-node state space
58 * exceeds the configured bound. Data treewidth above
59 * @c TreeDecomposition::MAX_TREEWIDTH surfaces as the usual
60 * @c TreeDecompositionException instead.
61 */
62class ReachabilityCompilerException : public std::runtime_error {
63public:
64/** @brief Construct with a human-readable message. @param what Message. */
65explicit ReachabilityCompilerException(const std::string &what)
66 : std::runtime_error(what) {
67}
68};
69
70/**
71 * @brief Compiles s-t reachability over a probabilistic edge relation
72 * into a d-D, along a tree decomposition of the data graph.
73 */
75public:
76/**
77 * @brief One row of the edge relation.
78 *
79 * Two rows may share a @c token only if they are mutual reverses
80 * (@c (u,v) and @c (v,u)), the natural encoding of an undirected edge
81 * in a directed edge relation; the pair is then treated as a single
82 * bidirectional edge variable.
83 */
84struct EdgeRow {
85 unsigned long src; ///< Source vertex ID.
86 unsigned long dst; ///< Destination vertex ID.
87 std::string token; ///< Provenance token (UUID) of the edge tuple.
88 double prob; ///< Probability of the edge tuple.
89 std::string block_key; ///< Block-independent (BID) key variable (UUID) when the tuple is a @c mulinput alternative (e.g. from @c repair_key); empty for an independent tuple.
90 unsigned block_index = 0; ///< Outcome index within the block (the @c mulinput gate's info).
91};
92
93/** @brief Structural statistics of a compilation, for diagnostics and tests. */
94struct Stats {
95 unsigned data_treewidth = 0; ///< Treewidth of the min-fill decomposition of the *data* graph.
96 std::size_t nb_bags = 0; ///< Number of bags of the decomposition.
97 std::size_t max_states = 0; ///< Maximum number of DP states at any node.
98 std::size_t nb_gates = 0; ///< Number of gates of the emitted d-D.
99 std::size_t nb_variables = 0; ///< Number of edge variables (provenance tokens).
100};
101
102/** @brief A compiled reachability query: the d-D and its statistics. */
103struct Result {
104 dDNNF dd; ///< d-D whose root computes "t is reachable from s"; input gates carry the edge tokens (UUID) and probabilities.
105 Stats stats; ///< Compilation statistics.
106};
107
108/** @brief One vertex's reachability circuit in an all-targets compilation. */
110 unsigned long vertex; ///< Vertex ID.
111 gate_t root; ///< Root of "vertex is reachable from the source" in the shared d-D.
112};
113
114/** @brief An all-targets compilation: one shared d-D, one root per reachable vertex. */
115struct AllResult {
116 dDNNF dd; ///< Shared circuit (gates are reused across vertices).
117 std::vector<VertexRoot> roots; ///< One entry per vertex reachable in the all-edges-present world (including the source itself, with a constant-true root).
118 Stats stats; ///< Compilation statistics.
119};
120
121/** @brief One (vertex, walk length) circuit of a bounded-hop compilation. */
123 unsigned long vertex; ///< Vertex ID.
124 unsigned hops; ///< Exact walk length (number of edges).
125 gate_t root; ///< Root of "some walk of exactly this many edges connects the source to the vertex".
126};
127
128/**
129 * @brief A bounded-hop all-targets compilation.
130 *
131 * One root per (vertex, walk length) pair achievable in the
132 * all-edges-present world -- matching the rows the generic recursive
133 * fixpoint derives for a hop-counting CTE, whose row @c (v,h)
134 * provenance is "some *walk* of exactly @c h edges" (walks, not paths:
135 * a cycle on the way pumps achievable lengths) -- plus, per vertex, the
136 * root of "some walk of at most the bound", which a hop-counting query
137 * deduplicating away the hop column computes as the OR of the
138 * per-length roots.
139 */
141 dDNNF dd; ///< Shared circuit.
142 std::vector<VertexHopRoot> roots; ///< Per (vertex, exact length) roots.
143 std::vector<VertexRoot> within_roots; ///< Per-vertex "within the bound" roots.
144 Stats stats; ///< Compilation statistics.
145};
146
147/** @brief A multi-set any-reach compilation: one shared circuit, one root per target set. */
149 dDNNF dd; ///< Shared circuit (consed: identical subcircuits are the same gate).
150 std::vector<gate_t> roots; ///< One root per input set, in input order.
151 Stats stats; ///< Compilation statistics (max_states maxed over the sweeps).
152};
153
154/**
155 * @brief One source of a multi-source compilation.
156 *
157 * Modelled as an arc from a virtual super-source to @c vertex: gated by
158 * the source tuple's provenance @c token when the source relation is
159 * tracked (a *probabilistic source set*), always present when
160 * @c certain (untracked sources, or the constant base arm of the
161 * recursive shape).
162 */
163struct SourceArc {
164 unsigned long vertex; ///< Source vertex.
165 std::string token; ///< Provenance token of the source tuple (unused when certain).
166 double prob; ///< Source-tuple probability (unused when certain).
167 bool certain; ///< Always-present source (no gating variable).
168};
169
170/**
171 * @brief Default bound on the number of DP states at a single
172 * decomposition node.
173 *
174 * The state space at a node is the set of reachable transitively-closed
175 * relations over at most @c MAX_TREEWIDTH+3 elements; it is bounded for
176 * fixed treewidth but can still be large near the treewidth cap, so a
177 * guard keeps compilation from exhausting memory on adversarial data.
178 */
179static constexpr std::size_t DEFAULT_MAX_STATES = 100000;
180
181/**
182 * @brief Maximum supported hop bound for @c compileAllHops().
183 *
184 * Length sets are bitmasks over walk lengths @c 0..bound in a 64-bit
185 * word; the driver falls back to the generic fixpoint above this.
186 */
187static constexpr unsigned MAX_HOP_BOUND = 62;
188
189/**
190 * @brief Compile s-t reachability over @p rows into a d-D.
191 *
192 * @param rows Edge tuples (vertex IDs, provenance token, probability).
193 * @param source Source vertex @c s.
194 * @param target Target vertex @c t.
195 * @param directed If @c false, every edge contributes both arcs
196 * (undirected connectivity).
197 * @param max_states Bound on the DP state count per node.
198 * @return The compiled d-D and statistics.
199 * @throws TreeDecompositionException if the data treewidth exceeds
200 * @c TreeDecomposition::MAX_TREEWIDTH.
201 * @throws ReachabilityCompilerException on unsupported input shapes or
202 * when @p max_states is exceeded.
203 */
204static Result compile(const std::vector<EdgeRow> &rows,
205 unsigned long source,
206 unsigned long target,
207 bool directed,
208 std::size_t max_states = DEFAULT_MAX_STATES);
209
210/**
211 * @brief Compile the reachability circuits of **every** vertex in one pass.
212 *
213 * Two sweeps over the tree decomposition -- bottom-up per-subtree state
214 * tables, then a top-down "rest of the graph" pass -- yield, for each
215 * vertex read at its elimination bag, the deterministic OR over
216 * compatible (below, above) state pairs whose closure connects the
217 * source to it. Total circuit size stays linear in the number of edges
218 * for fixed data treewidth: gates are shared across the per-vertex
219 * roots. This matches the semantics of the recursive-query relation
220 * @c reach: one root per vertex reachable in the all-edges-present
221 * world (vertices certainly unreachable are omitted).
222 *
223 * @param rows Edge tuples (vertex IDs, provenance token, probability).
224 * @param source Source vertex @c s.
225 * @param directed If @c false, every edge contributes both arcs.
226 * @param max_states Bound on the DP state count per node.
227 * @return The shared d-D, per-vertex roots, statistics.
228 * @throws TreeDecompositionException / ReachabilityCompilerException as
229 * for @c compile().
230 */
231static AllResult compileAll(const std::vector<EdgeRow> &rows,
232 unsigned long source,
233 bool directed,
234 std::size_t max_states = DEFAULT_MAX_STATES);
235
236/**
237 * @brief Multi-source variant of @c compileAll().
238 *
239 * Reachability is from a virtual super-source whose arcs to the
240 * @p sources are gated by the source tuples' tokens (or always present
241 * for certain sources); a vertex's circuit therefore computes "some
242 * present source reaches it". Everything else is as in
243 * @c compileAll(); the super-source itself is not reported in the
244 * roots.
245 *
246 * @param rows Edge tuples.
247 * @param sources Source arcs (at least one).
248 * @param directed If @c false, every edge contributes both arcs.
249 * @param max_states Bound on the DP state count per node.
250 * @return The shared d-D, per-vertex roots, statistics.
251 */
252static AllResult compileAll(const std::vector<EdgeRow> &rows,
253 const std::vector<SourceArc> &sources,
254 bool directed,
255 std::size_t max_states = DEFAULT_MAX_STATES);
256
257/**
258 * @brief Bounded-hop variant of @c compileAll(): per-(vertex, exact
259 * walk length) circuits for every length up to @p hop_bound.
260 *
261 * Same DP, richer state: the relation entries are *sets of achievable
262 * walk lengths* (bitmasks capped at @p hop_bound) instead of single
263 * reachability bits, composed in the capped min-plus-set semiring
264 * (Kleene closure with diagonal star). States still partition the
265 * worlds of the introduced edge variables, so determinism and
266 * decomposability hold by the same argument; the price is the larger
267 * state space, guarded by @p max_states as before.
268 *
269 * @param rows Edge tuples.
270 * @param source Source vertex.
271 * @param directed If @c false, every edge contributes both arcs.
272 * @param hop_bound Maximum walk length (at most @c MAX_HOP_BOUND).
273 * @param max_states Bound on the DP state count per node.
274 * @return The shared d-D, per-(vertex, length) and
275 * per-vertex within-bound roots, statistics.
276 */
277static AllHopsResult compileAllHops(const std::vector<EdgeRow> &rows,
278 unsigned long source,
279 bool directed,
280 unsigned hop_bound,
281 std::size_t max_states = DEFAULT_MAX_STATES);
282
283/**
284 * @brief Multi-source bounded-hop compilation.
285 *
286 * The virtual super-source's arcs contribute walk length zero, so the
287 * reported lengths count edges of the underlying graph only.
288 *
289 * @param rows Edge tuples.
290 * @param sources Source arcs (at least one).
291 * @param directed If @c false, every edge contributes both arcs.
292 * @param hop_bound Maximum walk length (at most @c MAX_HOP_BOUND).
293 * @param max_states Bound on the DP state count per node.
294 * @return As the single-source overload.
295 */
296static AllHopsResult compileAllHops(const std::vector<EdgeRow> &rows,
297 const std::vector<SourceArc> &sources,
298 bool directed,
299 unsigned hop_bound,
300 std::size_t max_states = DEFAULT_MAX_STATES);
301
302/**
303 * @brief Compile "some vertex of @p set is reachable" into one
304 * certified circuit.
305 *
306 * Same DP, with the state extended by one bit per domain position --
307 * "this position reaches some @p set vertex within the processed
308 * part" -- folded under closure and projection, so the single
309 * acceptance bit (at the source's position, over the root's table) is
310 * read in one bottom-up sweep. This is the deterministic circuit for
311 * the OR of the per-vertex reachability roots over @p set -- which,
312 * as an OR of *correlated* events (shared edges), could not otherwise
313 * be marked: it serves cross-vertex aggregations ("is some vertex of
314 * this region reachable") the per-vertex roots cannot.
315 *
316 * @param rows Edge tuples.
317 * @param sources Source arcs (at least one).
318 * @param set The target vertex set (need not intersect the
319 * graph; an absent vertex contributes nothing).
320 * @param directed If @c false, every edge contributes both arcs.
321 * @param max_states Bound on the DP state count per node.
322 * @return The circuit, its root computing the disjunction,
323 * and statistics.
324 */
325static Result compileAnyReach(const std::vector<EdgeRow> &rows,
326 const std::vector<SourceArc> &sources,
327 const std::vector<unsigned long> &set,
328 bool directed,
329 std::size_t max_states = DEFAULT_MAX_STATES);
330
331/**
332 * @brief Multi-set variant of @c compileAnyReach(): one shared
333 * circuit, one root per target set.
334 *
335 * The shared prelude -- variable grouping, tree decomposition of the
336 * data graph, bag assignments, literal gates -- is built once; one
337 * bottom-up sweep then runs per set, with content-deduplicated gate
338 * emission, so the parts of the circuit a set's seeds do not touch
339 * are literally shared across sets (the per-set sweeps re-derive the
340 * same gates). This is the engine behind cross-vertex aggregation
341 * planting, where one query yields many groups over the same graph.
342 *
343 * @param rows Edge tuples.
344 * @param sources Source arcs (at least one).
345 * @param sets The target vertex sets (at least one; an absent
346 * vertex contributes nothing).
347 * @param directed If @c false, every edge contributes both arcs.
348 * @param max_states Bound on the DP state count per node.
349 * @return The shared circuit, one root per set (in input
350 * order), and statistics.
351 */
353 const std::vector<EdgeRow> &rows,
354 const std::vector<SourceArc> &sources,
355 const std::vector<std::vector<unsigned long> > &sets,
356 bool directed,
357 std::size_t max_states = DEFAULT_MAX_STATES);
358
359/**
360 * @brief Compile "every vertex of @p set is reachable" (k-terminal /
361 * coverage reachability) into one certified circuit.
362 *
363 * Same DP, with the state extended by the *pending rescuer-set
364 * antichain*: a forgotten target vertex not yet reached by the source
365 * pends on the boundary positions that reach it (its rescuers), the
366 * sets staying closed under the relation, shrinking losslessly at
367 * forgets, discharged when the source reaches them and absorbed by
368 * the empty (reject) set; acceptance, after a final collapse onto the
369 * source domain, is the empty antichain. Worlds map to one state
370 * each, so the circuit is a certified d-D like the rest of the
371 * family: probability evaluation gives k-terminal reliability, and
372 * absorptive-semiring evaluation is exact too -- nonnegative min-plus
373 * gives the cost of the cheapest covering subgraph (directed Steiner
374 * cost).
375 *
376 * @param rows Edge tuples.
377 * @param sources Source arcs (at least one).
378 * @param set The target vertex set; a vertex absent from the
379 * graph is unreachable, so the result is constant
380 * false.
381 * @param directed If @c false, every edge contributes both arcs.
382 * @param max_states Bound on the DP state count per node.
383 * @return The circuit, its root computing the conjunction,
384 * and statistics.
385 */
386static Result compileCoverReach(const std::vector<EdgeRow> &rows,
387 const std::vector<SourceArc> &sources,
388 const std::vector<unsigned long> &set,
389 bool directed,
390 std::size_t max_states = DEFAULT_MAX_STATES);
391
392/**
393 * @brief Multi-set variant of @c compileCoverReach(): one shared
394 * (content-deduplicated) circuit, one root per set, as
395 * @c compileAnyReachAll().
396 */
398 const std::vector<EdgeRow> &rows,
399 const std::vector<SourceArc> &sources,
400 const std::vector<std::vector<unsigned long> > &sets,
401 bool directed,
402 std::size_t max_states = DEFAULT_MAX_STATES);
403};
404
405#endif /* REACHABILITY_COMPILER_H */
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
Tree decomposition of a Boolean circuit for knowledge compilation.
ReachabilityCompilerException(const std::string &what)
Construct with a human-readable message.
Compiles s-t reachability over a probabilistic edge relation into a d-D, along a tree decomposition o...
static AllHopsResult compileAllHops(const std::vector< EdgeRow > &rows, unsigned long source, bool directed, unsigned hop_bound, std::size_t max_states=DEFAULT_MAX_STATES)
Bounded-hop variant of compileAll(): per-(vertex, exact walk length) circuits for every length up to ...
static Result compile(const std::vector< EdgeRow > &rows, unsigned long source, unsigned long target, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile s-t reachability over rows into a d-D.
static constexpr unsigned MAX_HOP_BOUND
Maximum supported hop bound for compileAllHops().
static Result compileCoverReach(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< unsigned long > &set, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile "every vertex of @p set is reachable" (k-terminal / coverage reachability) into one certified...
static Result compileAnyReach(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< unsigned long > &set, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile "some vertex of @p set is reachable" into one certified circuit.
static constexpr std::size_t DEFAULT_MAX_STATES
Default bound on the number of DP states at a single decomposition node.
static AnyReachAllResult compileAnyReachAll(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< std::vector< unsigned long > > &sets, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Multi-set variant of compileAnyReach(): one shared circuit, one root per target set.
static AnyReachAllResult compileCoverReachAll(const std::vector< EdgeRow > &rows, const std::vector< SourceArc > &sources, const std::vector< std::vector< unsigned long > > &sets, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Multi-set variant of compileCoverReach(): one shared (content-deduplicated) circuit,...
static AllResult compileAll(const std::vector< EdgeRow > &rows, unsigned long source, bool directed, std::size_t max_states=DEFAULT_MAX_STATES)
Compile the reachability circuits of every vertex in one pass.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
Definition dDNNF.h:71
Decomposable Deterministic Negation Normal Form circuit.
A bounded-hop all-targets compilation.
std::vector< VertexHopRoot > roots
Per (vertex, exact length) roots.
std::vector< VertexRoot > within_roots
Per-vertex "within the bound" roots.
An all-targets compilation: one shared d-D, one root per reachable vertex.
std::vector< VertexRoot > roots
One entry per vertex reachable in the all-edges-present world (including the source itself,...
Stats stats
Compilation statistics.
dDNNF dd
Shared circuit (gates are reused across vertices).
A multi-set any-reach compilation: one shared circuit, one root per target set.
dDNNF dd
Shared circuit (consed: identical subcircuits are the same gate).
Stats stats
Compilation statistics (max_states maxed over the sweeps).
std::vector< gate_t > roots
One root per input set, in input order.
One row of the edge relation.
std::string token
Provenance token (UUID) of the edge tuple.
std::string block_key
Block-independent (BID) key variable (UUID) when the tuple is a mulinput alternative (e....
unsigned long src
Source vertex ID.
unsigned long dst
Destination vertex ID.
double prob
Probability of the edge tuple.
unsigned block_index
Outcome index within the block (the mulinput gate's info).
A compiled reachability query: the d-D and its statistics.
Stats stats
Compilation statistics.
dDNNF dd
d-D whose root computes "t is reachable from s"; input gates carry the edge tokens (UUID) and probabi...
One source of a multi-source compilation.
bool certain
Always-present source (no gating variable).
std::string token
Provenance token of the source tuple (unused when certain).
double prob
Source-tuple probability (unused when certain).
unsigned long vertex
Source vertex.
Structural statistics of a compilation, for diagnostics and tests.
std::size_t nb_gates
Number of gates of the emitted d-D.
std::size_t nb_bags
Number of bags of the decomposition.
std::size_t max_states
Maximum number of DP states at any node.
std::size_t nb_variables
Number of edge variables (provenance tokens).
unsigned data_treewidth
Treewidth of the min-fill decomposition of the data graph.
One (vertex, walk length) circuit of a bounded-hop compilation.
unsigned hops
Exact walk length (number of edges).
gate_t root
Root of "some walk of exactly this many edges connects the source to the vertex".
One vertex's reachability circuit in an all-targets compilation.
gate_t root
Root of "vertex is reachable from the source" in the shared d-D.