ProvSQL C/C++ API
Adding support for provenance and uncertainty management to PostgreSQL databases
Loading...
Searching...
No Matches
CertifiedDDMaterialize.cpp
Go to the documentation of this file.
1/**
2 * @file CertifiedDDMaterialize.cpp
3 * @brief Implementation of certified-d-D materialisation into the store.
4 *
5 * See @c CertifiedDDMaterialize.h. Extracted from the reachability
6 * compiler's SQL glue so the joint-width UCQ compiler shares the same
7 * content-addressed materialisation (and so probability / Shapley /
8 * expectation all go through the standard @c probability_evaluate path on
9 * the materialised token).
10 */
11extern "C" {
12#include "postgres.h"
13#include "miscadmin.h"
14#include "utils/uuid.h"
15
16#include "provsql_utils.h"
17#include "provsql_mmap.h"
18}
19
21#include "BooleanCircuit.h"
22#include "provsql_utils_cpp.h"
23
24#include <algorithm>
25#include <cstdint>
26#include <string>
27#include <unordered_set>
28#include <vector>
29
30namespace {
31
32/**
33 * @brief Minimal SHA-1 (RFC 3174), used for RFC 4122 version-5 UUIDs.
34 *
35 * Self-contained so the content addressing builds on every supported
36 * PostgreSQL version.
37 */
38void sha1(const unsigned char *data, std::size_t len, unsigned char out[20])
39{
40 uint32_t h[5] = {0x67452301u, 0xEFCDAB89u, 0x98BADCFEu, 0x10325476u,
41 0xC3D2E1F0u};
42 const std::size_t total = ((len + 8) / 64 + 1) * 64;
43 std::vector<unsigned char> buf(total, 0);
44 std::copy(data, data + len, buf.begin());
45 buf[len] = 0x80;
46 const uint64_t bits = static_cast<uint64_t>(len) * 8;
47 for (int i = 0; i < 8; ++i)
48 buf[total - 1 - i] = static_cast<unsigned char>(bits >> (8 * i));
49
50 for (std::size_t chunk = 0; chunk < total; chunk += 64) {
51 uint32_t w[80];
52 for (int i = 0; i < 16; ++i)
53 w[i] = (static_cast<uint32_t>(buf[chunk + 4*i]) << 24) |
54 (static_cast<uint32_t>(buf[chunk + 4*i + 1]) << 16) |
55 (static_cast<uint32_t>(buf[chunk + 4*i + 2]) << 8) |
56 static_cast<uint32_t>(buf[chunk + 4*i + 3]);
57 for (int i = 16; i < 80; ++i) {
58 const uint32_t v = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
59 w[i] = (v << 1) | (v >> 31);
60 }
61 uint32_t a = h[0], b = h[1], c = h[2], d = h[3], e = h[4];
62 for (int i = 0; i < 80; ++i) {
63 uint32_t f, k;
64 if (i < 20) {
65 f = (b & c) | (~b & d);
66 k = 0x5A827999u;
67 } else if (i < 40) {
68 f = b ^ c ^ d;
69 k = 0x6ED9EBA1u;
70 } else if (i < 60) {
71 f = (b & c) | (b & d) | (c & d);
72 k = 0x8F1BBCDCu;
73 } else {
74 f = b ^ c ^ d;
75 k = 0xCA62C1D6u;
76 }
77 const uint32_t tmp = ((a << 5) | (a >> 27)) + f + e + k + w[i];
78 e = d;
79 d = c;
80 c = (b << 30) | (b >> 2);
81 b = a;
82 a = tmp;
83 }
84 h[0] += a; h[1] += b; h[2] += c; h[3] += d; h[4] += e;
85 }
86 for (int i = 0; i < 5; ++i) {
87 out[4*i] = static_cast<unsigned char>(h[i] >> 24);
88 out[4*i + 1] = static_cast<unsigned char>(h[i] >> 16);
89 out[4*i + 2] = static_cast<unsigned char>(h[i] >> 8);
90 out[4*i + 3] = static_cast<unsigned char>(h[i]);
91 }
92}
93
94} // namespace
95
96pg_uuid_t provsqlUuidV5(const std::string &name)
97{
98 // uuid_ns_provsql() = 920d4f02-8718-5319-9532-d4ab83a64489
99 static const unsigned char ns[16] = {
100 0x92, 0x0d, 0x4f, 0x02, 0x87, 0x18, 0x53, 0x19,
101 0x95, 0x32, 0xd4, 0xab, 0x83, 0xa6, 0x44, 0x89
102 };
103 std::vector<unsigned char> data(16 + name.size());
104 std::copy(ns, ns + 16, data.begin());
105 std::copy(name.begin(), name.end(), data.begin() + 16);
106 unsigned char digest[20];
107 sha1(data.data(), data.size(), digest);
108 pg_uuid_t u;
109 std::copy(digest, digest + 16, u.data);
110 u.data[6] = static_cast<unsigned char>((u.data[6] & 0x0F) | 0x50);
111 u.data[8] = static_cast<unsigned char>((u.data[8] & 0x3F) | 0x80);
112 return u;
113}
114
115std::unordered_map<gate_t, pg_uuid_t, hash_gate_t> materializeCertifiedDD(
116 const dDNNF &dd, const std::vector<gate_t> &roots)
117{
118 std::unordered_map<gate_t, pg_uuid_t, hash_gate_t> uuid_of;
119 /* Tokens this backend has already materialised, across calls: the
120 * store is append-only, so a create this backend has sent once never
121 * needs re-sending. Per-backend, bounded. */
122 static std::unordered_set<std::string> created;
123 constexpr std::size_t kCreatedCap = 4u << 20;
124 if (created.size() > kCreatedCap)
125 created.clear();
126 pg_uuid_t one_uuid;
127 bool have_one = false;
128
129 const auto ensureOne = [&]() {
130 if (!have_one) {
131 one_uuid = provsqlUuidV5("one");
133 0, NULL);
134 have_one = true;
135 }
136 return one_uuid;
137 };
138 const auto createOnce = [&](const pg_uuid_t &token, gate_type type,
139 const std::vector<pg_uuid_t> &children,
140 bool certified) {
141 const std::string key = uuid2string(token);
142 if (!created.insert(key).second)
143 return;
145 &token, type,
146 static_cast<unsigned>(children.size()),
147 children.empty() ? NULL : children.data());
148 if (certified)
150 DNNF_CERT_INFO, 0);
151 };
152
153 std::vector<gate_t> stack(roots);
154 while (!stack.empty()) {
155 CHECK_FOR_INTERRUPTS();
156 const gate_t g = stack.back();
157 if (uuid_of.find(g) != uuid_of.end()) {
158 stack.pop_back();
159 continue;
160 }
161
162 const auto t = dd.getGateType(g);
163
164 if (t == BooleanGate::IN || t == BooleanGate::MULIN) {
165 const std::string tok = dd.getUUID(g);
166 if (!tok.empty() && tok[0] == '\x01') {
167 // A synthetic stick-breaking coin: the joint-width compiler
168 // introduces these (token "\x01mulsb:N") when it expands a
169 // repair_key / BID exclusion block into shared independent
170 // events; they carry a probability but no store UUID. Materialise
171 // each as a fresh independent gate_input with that probability.
172 // The UUID is content-addressed on the synthetic token, so coins
173 // shared across a block's values (the same "\x01mulsb:N") collapse
174 // to one store gate, preserving the mutual exclusion.
175 const pg_uuid_t token = provsqlUuidV5(tok);
176 if (created.insert(uuid2string(token)).second) {
178 provsql_internal_set_prob(&token, dd.getProb(g));
179 }
180 uuid_of[g] = token;
181 } else {
182 // Existing tokens (the leaf provenance): never re-created.
183 uuid_of[g] = string2uuid(tok);
184 }
185 stack.pop_back();
186 continue;
187 }
188
189 bool ready = true;
190 for (const auto &c : dd.getWires(g))
191 if (uuid_of.find(c) == uuid_of.end()) {
192 stack.push_back(c);
193 ready = false;
194 }
195 if (!ready)
196 continue;
197
198 const bool certified = dd.isDNNFCertified(g);
199 pg_uuid_t token;
200
201 switch (t) {
202 case BooleanGate::NOT:
203 {
204 const pg_uuid_t child = uuid_of[dd.getWires(g)[0]];
205 const pg_uuid_t one = ensureOne();
206 token = provsqlUuidV5("monus" + uuid2string(one) + uuid2string(child));
207 createOnce(token, gate_monus, {one, child}, false);
208 }
209 break;
210
211 case BooleanGate::AND:
212 case BooleanGate::OR:
213 {
214 const auto &wires = dd.getWires(g);
215 if (wires.empty()) {
216 if (t == BooleanGate::AND)
217 token = ensureOne();
218 else {
219 token = provsqlUuidV5("zero");
220 createOnce(token, gate_zero, {}, false);
221 }
222 } else if (wires.size() == 1) {
223 token = uuid_of[wires[0]];
224 } else {
225 std::vector<std::string> texts;
226 texts.reserve(wires.size());
227 for (const auto &c : wires)
228 texts.push_back(uuid2string(uuid_of[c]));
229 std::sort(texts.begin(), texts.end());
230 std::string name = (t == BooleanGate::AND ? "times{" : "plus{");
231 for (std::size_t i = 0; i < texts.size(); ++i) {
232 if (i)
233 name += ",";
234 name += texts[i];
235 }
236 name += "}";
237 token = provsqlUuidV5(name);
238 std::vector<pg_uuid_t> children;
239 children.reserve(wires.size());
240 for (const auto &c : wires)
241 children.push_back(uuid_of[c]);
242 createOnce(token, t == BooleanGate::AND ? gate_times : gate_plus,
243 children, certified);
244 }
245 }
246 break;
247
248 default:
249 provsql_error("materializeCertifiedDD: unsupported gate type");
250 throw std::runtime_error("unreachable");
251 }
252
253 uuid_of[g] = token;
254 stack.pop_back();
255 }
256
257 return uuid_of;
258}
259
261{
262 static std::unordered_set<std::string> created;
263 constexpr std::size_t kCreatedCap = 1u << 20;
264 if (created.size() > kCreatedCap)
265 created.clear();
266
267 const pg_uuid_t token =
268 provsqlUuidV5("assumedabsorptive" + uuid2string(child));
269 if (created.insert(uuid2string(token)).second) {
270 provsql_internal_create_gate(&token, gate_assumed, 1, &child);
271 provsql_internal_set_extra(&token, "absorptive");
272 }
273 return token;
274}
Boolean provenance circuit with support for knowledge compilation.
constexpr unsigned DNNF_CERT_INFO
d-DNNF certificate value for the (gate-type-specific) per-gate info field.
@ NOT
Logical negation of a single child gate.
@ OR
Logical disjunction of child gates.
@ AND
Logical conjunction of child gates.
@ IN
Input (variable) gate representing a base tuple.
@ MULIN
Multivalued-input gate (one of several options).
std::unordered_map< gate_t, pg_uuid_t, hash_gate_t > materializeCertifiedDD(const dDNNF &dd, const std::vector< gate_t > &roots)
Materialise (the reachable part of) a certified d-D into the mmap store.
pg_uuid_t wrapAssumedAbsorptive(const pg_uuid_t &child)
Wrap a materialised root in the 'absorptive' assumption marker and return the wrapper's UUID.
pg_uuid_t provsqlUuidV5(const std::string &name)
RFC 4122 version-5 UUID in the ProvSQL namespace.
Content-addressed materialisation of a certified d-D into the mmap provenance store.
gate_t
Strongly-typed gate identifier.
Definition Circuit.h:49
bool isDNNFCertified(gate_t g) const
Is gate g certified by the d-DNNF per-gate marking?
double getProb(gate_t g) const
Return the probability stored for gate g.
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
Definition Circuit.h:140
gateType getGateType(gate_t g) const
Return the type of gate g.
Definition Circuit.h:130
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
Definition Circuit.hpp:46
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
Definition dDNNF.h:71
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
void provsql_internal_set_extra(const pg_uuid_t *token, const char *str)
Internal entry point behind set_extra(): worker IPC only.
void provsql_internal_create_gate(const pg_uuid_t *token, gate_type type, unsigned nb_children, const pg_uuid_t *children_data)
Internal entry point behind create_gate(): cache + worker IPC.
bool provsql_internal_set_prob(const pg_uuid_t *token, double prob)
Internal entry point behind set_prob(): worker IPC only.
void provsql_internal_set_infos(const pg_uuid_t *token, unsigned info1, unsigned info2)
Internal entry point behind set_infos(): worker IPC only.
Background worker and IPC primitives for mmap-backed circuit storage.
Core types, constants, and utilities shared across ProvSQL.
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
pg_uuid_t string2uuid(const string &source)
Parse a UUID string into a pg_uuid_t.
string uuid2string(pg_uuid_t uuid)
Format a pg_uuid_t as a std::string.
C++ utility functions for UUID manipulation.
UUID structure.