ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
JointEncoding.cpp
Go to the documentation of this file.
1/**
2 * @file JointEncoding.cpp
3 * @brief The data-graph fast-path encoding.
4 *
5 * See @c JointEncoding.h for the design. This translation unit
6 * implements the §3.5 regime (independent @c gate_input tokens, joint
7 * graph = Gaifman graph of the facts); it does not handle the
8 * correlated regime's circuit slice extraction.
9 */
10#include "JointEncoding.h"
11
12#include <algorithm>
13#include <unordered_map>
14
15#include "TreeDecomposition.h"
16
17JointEncoding JointEncoding::fromFacts(const std::vector<FactRow> &rows)
18{
19 JointEncoding enc;
20
21 // Dedup independent facts by token: the same provenance token over the
22 // same relation and element tuple is one variable serving several
23 // atoms (the standard self-join case). A token over a *different*
24 // tuple is perfectly correlated with the first occurrence -- treating
25 // the two as independent would be silently wrong -- so it routes to
26 // the general path.
27 std::unordered_map<std::string, std::size_t> token_to_fact;
28 // Certain facts (untracked relations) dedup by (relation, elements).
29 std::unordered_map<std::string, std::size_t> certain_to_fact;
30
31 for (const auto &row : rows) {
32 for (unsigned long e : row.elements)
33 enc.n_elements = std::max(enc.n_elements, e + 1);
34
35 if (row.token.empty()) {
36 // Certain fact: always present.
37 std::string key = std::to_string(row.relation_id) + ":";
38 for (unsigned long e : row.elements)
39 key += std::to_string(e) + ",";
40 if (certain_to_fact.count(key))
41 continue;
42 certain_to_fact[key] = enc.facts.size();
43 Fact f;
44 f.relation_id = row.relation_id;
45 f.elements = row.elements;
47 enc.facts.push_back(std::move(f));
48 continue;
49 }
50
51 auto it = token_to_fact.find(row.token);
52 if (it != token_to_fact.end()) {
53 const Fact &existing = enc.facts[it->second];
54 if (existing.relation_id != row.relation_id ||
55 existing.elements != row.elements)
57 "provenance token " + row.token +
58 " gates facts over different element tuples; "
59 "the joint-width data-graph path requires distinct tokens");
60 continue; // duplicate occurrence of the same fact (self-join)
61 }
62
63 if (row.prob < 0. || row.prob > 1.)
64 throw JointCompilerException("fact probability out of [0,1]");
65
66 Fact f;
67 f.relation_id = row.relation_id;
68 f.elements = row.elements;
70 f.event = enc.events.size();
71 token_to_fact[row.token] = enc.facts.size();
72 enc.facts.push_back(std::move(f));
73 enc.events.push_back(Event{row.token, row.prob});
74 }
75
76 // Diagnostics: the data-graph degeneracy lower bound is the joint
77 // screen in this regime; no circuit slice, so its bound is 0.
78 Graph g = enc.buildGraph();
79 unsigned max_degree = 0;
83
84 return enc;
85}
86
88 std::vector<SliceGate> slice,
89 unsigned long n_elements)
90{
91 JointEncoding enc;
92 enc.facts = std::move(facts);
93 enc.slice = std::move(slice);
95 enc.correlated = true;
96
97 // The world variables are the slice's INPUT leaves; mirror them into
98 // events for the statistics count.
99 for (const auto &sg : enc.slice)
100 if (sg.type == SliceGateType::INPUT)
101 enc.events.push_back(Event{sg.token, sg.prob});
102
103 // Diagnostics: the data-only degeneracy lower bound (Gaifman of the
104 // facts, no gate vertices) and the slice-only degeneracy lower bound
105 // (gate cliques only) -- the two "separate screens" of thesis
106 // Prop. 4.2.11, both of which can be small while the joint width is
107 // large.
108 {
109 Graph data;
110 for (const auto &f : enc.facts) {
111 for (std::size_t i = 0; i < f.elements.size(); ++i)
112 for (std::size_t j = i + 1; j < f.elements.size(); ++j)
113 if (f.elements[i] != f.elements[j])
114 data.add_edge(f.elements[i], f.elements[j]);
115 if (!f.elements.empty())
116 data.add_node(f.elements[0]);
117 }
118 unsigned md = 0;
120 }
121 {
122 Graph circ;
123 for (std::size_t i = 0; i < enc.slice.size(); ++i) {
124 const SliceGate &sg = enc.slice[i];
125 std::vector<unsigned long> cl;
126 for (unsigned c : sg.children)
127 cl.push_back(enc.n_elements + c);
128 cl.push_back(enc.n_elements + i);
129 for (std::size_t a = 0; a < cl.size(); ++a)
130 for (std::size_t b = a + 1; b < cl.size(); ++b)
131 if (cl[a] != cl[b])
132 circ.add_edge(cl[a], cl[b]);
133 if (cl.size() == 1)
134 circ.add_node(cl[0]);
135 }
136 unsigned md = 0;
139 }
140
141 return enc;
142}
143
145{
146 Graph g;
147 for (const auto &f : facts) {
148 // Fact clique: {elements} ∪ {fact gate} (the gate vertex is present
149 // only in the correlated regime; an independent/certain fact's clique
150 // is over its elements alone).
151 std::vector<unsigned long> cl = f.elements;
152 if (correlated && f.kind == FactGateKind::GATE)
153 cl.push_back(n_elements + f.gate);
154 for (std::size_t i = 0; i < cl.size(); ++i)
155 for (std::size_t j = i + 1; j < cl.size(); ++j)
156 if (cl[i] != cl[j])
157 g.add_edge(cl[i], cl[j]);
158 if (!cl.empty() && !g.has_node(cl[0]))
159 g.add_node(cl[0]);
160 }
161 // Gate cliques: {gate} ∪ {children} per internal slice gate (the
162 // stronger ternary co-occurrence, so every gate is confirmable in a
163 // single bag).
164 if (correlated)
165 for (std::size_t i = 0; i < slice.size(); ++i) {
166 const SliceGate &sg = slice[i];
167 if (sg.children.empty()) {
168 g.add_node(n_elements + i); // INPUT leaf
169 continue;
170 }
171 std::vector<unsigned long> cl;
172 for (unsigned c : sg.children)
173 cl.push_back(n_elements + c);
174 cl.push_back(n_elements + i);
175 for (std::size_t a = 0; a < cl.size(); ++a)
176 for (std::size_t b = a + 1; b < cl.size(); ++b)
177 if (cl[a] != cl[b])
178 g.add_edge(cl[a], cl[b]);
179 }
180 return g;
181}
Phase A of the joint-width UCQ compiler: assemble the joint graph of the data and its correlation str...
@ GATE
Present iff its slice gate evaluates true (correlated regime).
@ CERTAIN
Always present: an untracked relation, constant-true token.
@ INDEP
Present iff its independent Bernoulli event is drawn (data-graph regime).
Tree decomposition of a Boolean circuit for knowledge compilation.
Mutable adjacency-list graph over unsigned-long node IDs.
Definition Graph.h:33
void add_edge(unsigned long src, unsigned long tgt, bool undirected=true)
Add an edge between src and tgt.
Definition Graph.h:75
bool has_node(unsigned long node) const
Return true if node is present in the graph.
Definition Graph.h:198
void add_node(unsigned long node)
Add node to the graph (no edges).
Definition Graph.h:89
Exception thrown when joint-width compilation cannot proceed.
The joint encoding of an instance: facts, world events, and the joint graph the screen and the DP run...
static JointEncoding fromCorrelated(std::vector< Fact > facts, std::vector< SliceGate > slice, unsigned long n_elements)
Construct the correlated-regime encoding from facts and an already-extracted circuit slice.
Graph buildGraph() const
Construct the joint graph for the screen and the DP.
std::vector< Event > events
Independent world variables (data-graph regime).
bool correlated
true when the slice is present (correlated regime).
unsigned circuit_treewidth_lb
Degeneracy lower bound of the slice-only graph (0 in the data-graph regime).
std::vector< Fact > facts
Deduplicated facts.
std::vector< SliceGate > slice
Circuit slice (correlated regime); gate vertex = n_elements + index.
unsigned data_treewidth_lb
Degeneracy lower bound of the data-only graph (diagnostics).
static JointEncoding fromFacts(const std::vector< FactRow > &rows)
Build the data-graph (§3.5 fast path) encoding from raw rows.
unsigned long n_elements
One past the largest domain element id (vertex ids [0,n_elements)).
static unsigned degeneracyLowerBound(const BooleanCircuit &bc, unsigned &max_degree)
Cheap degeneracy lower bound on the treewidth of bc's primal graph.
One independent Bernoulli world variable.
A deduplicated fact participating in the DP.
std::size_t event
Index into events (when INDEP).
std::vector< unsigned long > elements
Dense element ids (the fact's tuple).
FactGateKind kind
How the fact's presence is gated.
unsigned relation_id
Dense id of the relation symbol.
One node of the extracted circuit slice (correlated regime).
std::vector< unsigned > children
Child slice indices (≤ 2; empty for INPUT).