11#include <unordered_map>
30using Clauses = std::vector<std::set<gate_t> >;
46 std::size_t operator()(
const Clauses &cls)
const
48 std::size_t h = 1469598103934665603ull;
50 for(
const auto &cl : cls) {
51 std::size_t ch = 1099511628211ull;
53 ch = ch * 31u + gh(v);
56 h ^= ch + 0x9e3779b97f4a7c15ull + (h << 6) + (h >> 2);
80 const BooleanCircuit &c;
81 std::unordered_map<Clauses, double, ClausesHash> memo;
86 unsigned long steps = 0;
87 unsigned long budget = 0;
99void removeSubsumed(Clauses &clauses)
103 std::sort(clauses.begin(), clauses.end(),
104 [](
const std::set<gate_t> &a,
const std::set<gate_t> &b) {
105 return a.size() < b.size();
108 kept.reserve(clauses.size());
109 for(
auto &c : clauses) {
110 bool subsumed =
false;
111 for(
const auto &k : kept)
113 if(std::includes(c.begin(), c.end(), k.begin(), k.end())) {
118 kept.push_back(std::move(c));
120 clauses = std::move(kept);
132std::vector<Clauses> components(
const Clauses &clauses)
134 const size_t m = clauses.size();
135 std::vector<size_t> parent(m);
136 for(
size_t i = 0; i < m; ++i)
138 std::function<size_t(
size_t)> find = [&](
size_t x) {
139 while(parent[x] != x) {
140 parent[x] = parent[parent[x]];
145 std::unordered_map<gate_t, size_t> owner;
146 for(
size_t i = 0; i < m; ++i)
147 for(
gate_t v : clauses[i]) {
148 auto it = owner.find(v);
149 if(it == owner.end())
152 parent[find(i)] = find(it->second);
155 std::map<size_t, Clauses> groups;
156 for(
size_t i = 0; i < m; ++i)
157 groups[find(i)].push_back(clauses[i]);
158 std::vector<Clauses> out;
159 out.reserve(groups.size());
160 for(
auto &kv : groups)
161 out.push_back(std::move(kv.second));
167gate_t mostFrequentVar(
const Clauses &clauses)
169 std::map<gate_t, size_t> freq;
170 for(
const auto &c : clauses)
174 size_t best_count = 0;
176 for(
const auto &kv : freq)
177 if(!found || kv.second > best_count) {
179 best_count = kv.second;
185DTreeInterval recurse(DTreeContext &ctx, Clauses clauses,
double max_width)
189 throw CircuitException(
"Interrupted");
191 if(ctx.budget && ctx.steps > ctx.budget)
192 throw CircuitException(
"d-tree: cost budget exceeded");
196 for(
const auto &cl : clauses)
202 removeSubsumed(clauses);
203 std::sort(clauses.begin(), clauses.end());
208 const bool exact = (max_width <= 0.);
210 const auto it = ctx.memo.find(clauses);
211 if(it != ctx.memo.end())
212 return {it->second, it->second};
217 ctx.c.dnfBounds(clauses, L, U);
218 if(U - L <= max_width) {
220 ctx.memo.emplace(clauses, L);
227 std::vector<Clauses> comps = components(clauses);
228 if(comps.size() > 1) {
229 double prod_lower = 1., prod_upper = 1.;
230 const double sub_width = max_width /
static_cast<double>(comps.size());
231 for(
auto &comp : comps) {
233 prod_lower *= (1. - r.lower);
234 prod_upper *= (1. - r.upper);
236 res = {1. - prod_lower, 1. - prod_upper};
244 const gate_t x = mostFrequentVar(clauses);
245 const double px = ctx.c.getProb(x);
247 pos.reserve(clauses.size());
248 neg.reserve(clauses.size());
249 for(
const auto &cl : clauses) {
251 std::set<gate_t> reduced = cl;
253 pos.push_back(std::move(reduced));
259 const DTreeInterval rp = recurse(ctx, std::move(pos), max_width);
260 const DTreeInterval rn = recurse(ctx, std::move(neg), max_width);
261 res = {px * rp.lower + (1. - px) * rn.lower,
262 px * rp.upper + (1. - px) * rn.upper};
266 ctx.memo.emplace(clauses, res.lower);
273 double max_width,
unsigned long budget,
274 unsigned long *steps_out)
276 DTreeContext ctx{c, {}};
278 DTreeInterval r = recurse(ctx, std::move(clauses), max_width);
280 *steps_out = ctx.steps;
291using Assignment = std::unordered_map<gate_t, bool>;
297 std::unordered_map<gate_t, std::set<gate_t> > footprint;
298 std::unordered_map<std::string, double> exactMemo;
300 unsigned long steps = 0;
301 unsigned long budget = 0;
304inline unsigned long gid(
gate_t g)
306 return static_cast<unsigned long>(
307 static_cast<std::underlying_type<gate_t>::type
>(g));
318const std::set<gate_t> &footprintOf(GenContext &ctx,
gate_t g)
320 auto it = ctx.footprint.find(g);
321 if(it != ctx.footprint.end())
324 switch(ctx.c.getGateType(g)) {
326 const double p = ctx.c.getProb(g);
327 if(p > 0.0 && p < 1.0)
334 for(
gate_t ch : ctx.c.getWires(g)) {
335 const auto &cs = footprintOf(ctx, ch);
336 s.insert(cs.begin(), cs.end());
340 throw CircuitException(
341 "d-tree: multivalued / undetermined gate not supported on the general "
344 return ctx.footprint.emplace(g, std::move(s)).first->second;
349bool determined(GenContext &ctx,
gate_t g,
const Assignment &A)
351 for(
gate_t v : footprintOf(ctx, g))
352 if(A.find(v) == A.end())
358bool evalDet(GenContext &ctx,
gate_t g,
const Assignment &A)
360 switch(ctx.c.getGateType(g)) {
365 return ctx.c.getProb(g) >= 1.0;
368 return !evalDet(ctx, ctx.c.getWires(g)[0], A);
370 for(
gate_t ch : ctx.c.getWires(g))
371 if(!evalDet(ctx, ch, A))
375 for(
gate_t ch : ctx.c.getWires(g))
376 if(evalDet(ctx, ch, A))
380 throw CircuitException(
"d-tree: unsupported gate in evalDet");
386std::vector<std::vector<gate_t> > genComponents(
387 GenContext &ctx,
const std::vector<gate_t> &live,
const Assignment &A)
389 const size_t m = live.size();
390 std::vector<size_t> parent(m);
391 for(
size_t i = 0; i < m; ++i)
393 std::function<size_t(
size_t)> find = [&](
size_t x) {
394 while(parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
397 std::unordered_map<gate_t, size_t> owner;
398 for(
size_t i = 0; i < m; ++i)
399 for(
gate_t v : footprintOf(ctx, live[i])) {
400 if(A.find(v) != A.end())
402 auto it = owner.find(v);
403 if(it == owner.end())
406 parent[find(i)] = find(it->second);
408 std::map<size_t, std::vector<gate_t> > groups;
409 for(
size_t i = 0; i < m; ++i)
410 groups[find(i)].push_back(live[i]);
411 std::vector<std::vector<gate_t> > out;
412 out.reserve(groups.size());
413 for(
auto &kv : groups)
414 out.push_back(std::move(kv.second));
420gate_t genPivot(GenContext &ctx,
const std::vector<gate_t> &live,
423 std::map<gate_t, size_t> freq;
425 for(
gate_t v : footprintOf(ctx, c))
426 if(A.find(v) == A.end())
429 size_t best_count = 0;
431 for(
const auto &kv : freq)
432 if(!found || kv.second > best_count) {
434 best_count = kv.second;
446 const std::vector<gate_t> &children,
449 auto comps = genComponents(ctx, children, A);
452 for(
const auto &comp : comps) {
454 if(comp.size() == 1) {
464 gU = std::min(gU, b.upper);
466 gL = sumL - (
static_cast<double>(comp.size()) - 1.0);
467 if(gL < 0.0) gL = 0.0;
474 gL = std::max(gL, b.lower);
476 gU = (sumU > 1.0) ? 1.0 : sumU;
479 else { L *= (1.0 - gL); U *= (1.0 - gU); }
483 return {1.0 - L, 1.0 - U};
488 switch(ctx.c.getGateType(g)) {
492 return {it->second ? 1.0 : 0.0, it->second ? 1.0 : 0.0};
493 double p = ctx.c.getProb(g);
500 return {1.0 - b.upper, 1.0 - b.lower};
504 return genBoundGroup(ctx, ctx.c.getGateType(g), ctx.c.getWires(g), A);
506 throw CircuitException(
"d-tree: unsupported gate in genBound");
513std::string exactKey(GenContext &ctx,
char tag,
BooleanGate op,
514 const std::vector<gate_t> &gates,
const Assignment &A)
516 std::string k(1, tag);
518 std::set<gate_t> footunion;
521 k += std::to_string(gid(g));
522 const auto &fp = footprintOf(ctx, g);
523 footunion.insert(fp.begin(), fp.end());
526 for(
gate_t v : footunion) {
529 k += std::to_string(gid(v));
530 k += it->second ?
'=' :
'#';
537 const std::vector<gate_t> &children,
538 Assignment &A,
double w);
544 throw CircuitException(
"Interrupted");
546 if(ctx.budget && ctx.steps > ctx.budget)
547 throw CircuitException(
"d-tree: cost budget exceeded");
549 if(determined(ctx, g, A)) {
550 double v = evalDet(ctx, g, A) ? 1.0 : 0.0;
555 if(b.upper - b.lower <= w)
558 switch(ctx.c.getGateType(g)) {
560 DTreeInterval r = genRefine(ctx, ctx.c.getWires(g)[0], A, w);
561 return {1.0 - r.upper, 1.0 - r.lower};
564 const double p = ctx.c.getProb(g);
569 return genRefineGroup(ctx, ctx.c.getGateType(g), ctx.c.getWires(g), A, w);
571 throw CircuitException(
"d-tree: unsupported gate in genRefine");
576 const std::vector<gate_t> &children,
577 Assignment &A,
double w)
581 throw CircuitException(
"Interrupted");
583 if(ctx.budget && ctx.steps > ctx.budget)
584 throw CircuitException(
"d-tree: cost budget exceeded");
587 std::vector<gate_t> live;
588 live.reserve(children.size());
589 for(
gate_t c : children) {
590 if(determined(ctx, c, A)) {
591 bool v = evalDet(ctx, c, A);
603 const bool exact = (w <= 0.0);
604 std::sort(live.begin(), live.end());
607 key = exactKey(ctx,
'G', op, live, A);
608 auto it = ctx.exactMemo.find(key);
609 if(it != ctx.exactMemo.end())
610 return {it->second, it->second};
614 auto comps = genComponents(ctx, live, A);
615 if(comps.size() > 1) {
616 const double w_sub = w /
static_cast<double>(comps.size());
617 double L = 1.0, U = 1.0;
618 for(
auto &comp : comps) {
621 else { L *= (1.0 - r.lower); U *= (1.0 - r.upper); }
625 }
else if(live.size() == 1) {
626 res = genRefine(ctx, live[0], A, w);
630 if(b.upper - b.lower <= w)
633 const gate_t x = genPivot(ctx, live, A);
634 const double px = ctx.c.getProb(x);
640 res = {px * r1.lower + (1.0 - px) * r0.lower,
641 px * r1.upper + (1.0 - px) * r0.upper};
645 ctx.exactMemo.emplace(key, res.lower);
652 double max_width,
unsigned long budget,
653 unsigned long *steps_out)
655 GenContext ctx{c, {}, {}};
660 *steps_out = ctx.steps;
Boolean provenance circuit with support for knowledge compilation.
BooleanGate
Gate types for a Boolean provenance circuit.
@ 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.
gate_t
Strongly-typed gate identifier.
Anytime interval-bounds probability for monotone DNFs (d-trees).
Boolean circuit for provenance formula evaluation.
DTreeInterval dtreeBounds(const BooleanCircuit &c, Clauses clauses, double max_width, unsigned long budget, unsigned long *steps_out)
DTreeInterval dtreeBoundsCircuit(const BooleanCircuit &c, gate_t root, double max_width, unsigned long budget, unsigned long *steps_out)
Certified probability interval of an arbitrary Boolean circuit, refined to a target width (the d-tree...
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
Core types, constants, and utilities shared across ProvSQL.
A certified probability interval lower <= Pr <= upper.