28#include "catalog/pg_type.h"
30#include "storage/latch.h"
31#include "utils/uuid.h"
32#include "executor/spi.h"
34#include "access/htup_details.h"
47#include <unordered_map>
101 auto it = memo.find(g);
102 if(it != memo.end())
return it->second;
103 CHECK_FOR_INTERRUPTS();
116 for(
gate_t c : gc.
getWires(g)) pn *= (1.0 - mobiusEvalRec(gc, c, memo));
124 r = mobiusEvalRec(gc, w[0], memo) - mobiusEvalRec(gc, w[1], memo);
130 const std::string extra = gc.
getExtra(g);
135 std::map<std::string,long> co;
139 while(i < extra.size()) {
140 while(i < extra.size() && (extra[i]==
' '||extra[i]==
'\t')) ++i;
141 if(i >= extra.size())
break;
143 while(j < extra.size() && extra[j]!=
' ' && extra[j]!=
'\t') ++j;
144 const std::string tok = extra.substr(i, j-i);
145 if(tok.size()>2 && tok[0]==
'L' && tok[1]==
':')
146 lineage = tok.substr(2);
148 const std::size_t colon = tok.rfind(
':');
149 if(colon != std::string::npos)
150 co[tok.substr(0,colon)] =
151 std::strtol(tok.substr(colon+1).c_str(),
nullptr, 10);
157 for(std::size_t i=0;i<w.size();++i) {
161 auto cit = co.find(u);
164 v +=
static_cast<double>(cit->second) * mobiusEvalRec(gc, w[i], memo);
168 constexpr double tol = 1e-9;
169 if(v < -tol || v > 1.0 + tol)
170 provsql_warning(
"mobius: signed combination left [0,1] before clamping "
171 "(value %g) -- possible compiler bug", v);
172 if(v < 0.) v = 0.;
else if(v > 1.) v = 1.;
188std::string mobiusLineageOf(
pg_uuid_t token)
200 const std::size_t p = ex.find(
"L:");
201 if(p != std::string::npos) {
202 const std::size_t e = ex.find(
' ', p);
203 lineage = ex.substr(p + 2,
204 e == std::string::npos ? std::string::npos : e - p - 2);
213double mobiusProbabilityImpl(
pg_uuid_t token)
229 std::map<gate_t,double> memo;
230 r = mobiusEvalRec(gc, root, memo);
245string trim_arg(
const string &s)
247 size_t a = s.find_first_not_of(
" \t");
248 if(a == string::npos)
250 size_t b = s.find_last_not_of(
" \t");
251 return s.substr(a, b - a + 1);
263 map<string, string> kv;
264 vector<string> positional;
265 bool has(
const string &k)
const {
return kv.find(k) != kv.end(); }
266 string get(
const string &k)
const {
267 auto it = kv.find(k);
268 return it == kv.end() ? string() : it->second;
283MethodArgs parse_method_args(
const string &args)
286 stringstream ss(args);
288 while(getline(ss, tok,
',')) {
289 string t = trim_arg(tok);
292 auto eq = t.find(
'=');
293 if(eq == string::npos) {
294 out.positional.push_back(t);
296 string key = trim_arg(t.substr(0, eq));
297 string val = trim_arg(t.substr(eq + 1));
298 transform(key.begin(), key.end(), key.begin(),
299 [](
unsigned char c){ return tolower(c); });
309void reject_unknown_keys(
const MethodArgs &a,
const set<string> &allowed,
312 for(
const auto &p : a.kv)
313 if(allowed.find(p.first) == allowed.end())
315 method, p.first.c_str());
319bool parse_ulong_full(
const string &v,
unsigned long &out)
321 if(v.empty() || v.find_first_not_of(
"0123456789") != string::npos)
325 out = stoul(v, &pos);
326 return pos == v.size();
327 }
catch(
const std::exception &) {
333bool parse_double_full(
const string &v,
double &out)
340 return pos == v.size();
341 }
catch(
const std::exception &) {
355void parse_eps_delta(
const MethodArgs &a,
const char *method,
356 double &eps,
double &delta)
358 if(a.has(
"epsilon") && (!parse_double_full(a.get(
"epsilon"), eps)
359 || eps <= 0. || eps > 1.))
360 provsql_error(
"method '%s': epsilon must be in (0, 1]", method);
361 if(a.has(
"delta") && (!parse_double_full(a.get(
"delta"), delta)
362 || delta < 0. || delta >= 1.))
363 provsql_error(
"method '%s': delta must be in [0, 1)", method);
375 unsigned long samples = 0;
376 double eps = 0.1, delta = 0.05;
377 bool has_max =
false;
378 unsigned long max_samples = 0;
390SampleSpec parse_sample_spec(
const MethodArgs &a,
const char *method)
392 reject_unknown_keys(a, {
"samples",
"epsilon",
"delta",
"max_samples"}, method);
394 const bool has_samples = a.has(
"samples") || !a.positional.empty();
395 const bool has_adaptive = a.has(
"epsilon") || a.has(
"delta");
397 if(a.positional.size() > 1)
398 provsql_error(
"method '%s': too many positional arguments", method);
399 if(a.has(
"samples") && !a.positional.empty())
400 provsql_error(
"method '%s': give either samples= or a bare integer, "
402 if(has_samples && has_adaptive)
403 provsql_error(
"method '%s': samples is mutually exclusive with "
404 "epsilon/delta", method);
405 if(a.has(
"max_samples") && !has_adaptive)
406 provsql_error(
"method '%s': max_samples applies only to the adaptive "
407 "epsilon/delta path", method);
408 if(a.has(
"delta") && !a.has(
"epsilon"))
409 provsql_error(
"method '%s': delta requires epsilon", method);
414 const string v = a.has(
"samples") ? a.get(
"samples") : a.positional[0];
415 if(!parse_ulong_full(v, s.samples) || s.samples == 0)
416 provsql_error(
"method '%s': invalid sample count '%s'", method, v.c_str());
420 parse_eps_delta(a, method, s.eps, s.delta);
424 if(s.delta == 0. &&
string(method) !=
"relative"
425 &&
string(method) !=
"additive")
426 provsql_error(
"method '%s': delta must be in (0, 1); delta = 0 "
427 "(deterministic) is supported only on the 'relative' / "
428 "'additive' paths, which route to the d-tree or an exact "
430 if(a.has(
"max_samples")) {
432 if(!parse_ulong_full(a.get(
"max_samples"), s.max_samples) || s.max_samples == 0)
433 provsql_error(
"method '%s': invalid max_samples '%s'", method,
434 a.get(
"max_samples").c_str());
440unsigned long finalize_adaptive(
double nd,
const SampleSpec &s)
444 unsigned long n = (nd >= 9e18) ?
static_cast<unsigned long>(9e18)
445 :
static_cast<unsigned long>(nd);
448 if(s.has_max && n > s.max_samples)
454string fmt_num(
double x)
457 snprintf(buf,
sizeof(buf),
"%.6g", x);
474void emit_guarantee(
const char *kind,
double eps,
double delta,
475 unsigned long samples,
long clauses,
const char *tool)
479 string msg =
"approximation-guarantee: kind=" + string(kind)
480 +
" eps=" + fmt_num(eps);
482 msg +=
" delta=" + fmt_num(delta);
484 msg +=
" samples=" + std::to_string(samples);
486 msg +=
" clauses=" + std::to_string(clauses);
488 msg +=
" tool=" + string(tool);
493bool is_approx_wmc_tool(
const string &tool)
495 return tool ==
"weightmc" || tool ==
"approxmc";
499double eps_from_wmc_opt(
const string &opt)
501 auto semi = opt.find(
';');
502 if(semi == string::npos)
505 return (parse_double_full(opt.substr(semi + 1), e) && e > 0.) ? e : 0.8;
519unsigned long monte_carlo_samples(
const MethodArgs &a)
521 SampleSpec s = parse_sample_spec(a,
"monte-carlo");
522 unsigned long n = s.fixed
524 : finalize_adaptive(ceil(log(2.0 / s.delta) / (2.0 * s.eps * s.eps)), s);
527 const double eps = s.fixed
528 ? sqrt(log(2.0 / 0.05) / (2.0 *
static_cast<double>(n))) : s.eps;
529 const double delta = s.fixed ? 0.05 : s.delta;
530 emit_guarantee(
"additive", eps, delta, n, -1,
nullptr);
544string wmc_opt_from_args(
const MethodArgs &a,
const char *method)
546 if(!a.positional.empty()) {
547 if(a.has(
"epsilon") || a.has(
"delta"))
548 provsql_error(
"method '%s': give either the legacy 'delta;epsilon' or "
549 "epsilon=/delta=, not both", method);
550 if(a.positional.size() > 1)
551 provsql_error(
"method '%s': too many positional arguments", method);
552 return a.positional[0];
554 reject_unknown_keys(a, {
"epsilon",
"delta"}, method);
557 double eps = 0.8, delta = 0.5;
558 parse_eps_delta(a, method, eps, delta);
559 return (a.has(
"delta") ? a.get(
"delta") :
string()) +
";"
560 + (a.has(
"epsilon") ? a.get(
"epsilon") :
string());
585 const std::vector<gate_t> &clauses,
586 const std::vector<std::set<gate_t> > &supports,
589 const size_t m = clauses.size();
590 const double e = exp(1.0);
591 const double mm = (m == 0) ? 1. : static_cast<double>(m);
592 SampleSpec s = parse_sample_spec(a,
"karp-luby");
595 double r = c.
karpLuby(clauses, supports, s.samples);
597 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(s.samples));
598 emit_guarantee(
"relative", eps, 0.05, s.samples,
static_cast<long>(m),
nullptr);
603 const double Y = 4.0 * (e - 2.0) * log(2.0 / s.delta) / (s.eps * s.eps);
604 const double Y1 = 1.0 + (1.0 + s.eps) * Y;
605 const unsigned long cap =
606 s.has_max ? s.max_samples : finalize_adaptive(ceil(Y1 * mm), s);
607 unsigned long used = 0;
608 bool reached =
false;
611 if(reached || used == 0) {
612 emit_guarantee(
"relative", s.eps, s.delta, used,
static_cast<long>(m),
nullptr);
615 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(used));
617 "%lu-sample cap before the (epsilon=%g, delta=%g) target; "
618 "reporting the relative guarantee achieved at the samples "
619 "spent", cap, s.eps, s.delta);
620 emit_guarantee(
"relative", eps, 0.05, used,
static_cast<long>(m),
nullptr);
631 return mobiusProbabilityImpl(token);
656 if (!proc_exit_inprogress) {
657 InterruptPending =
true;
658 QueryCancelPending =
true;
678 const std::unordered_map<gate_t, gate_t> &gc_to_bc,
680 std::map<gate_t, StructuredDNNFBuilder::InputKey> &out)
682 std::set<gate_t> seen;
683 std::stack<gate_t> st;
685 while (!st.empty()) {
686 gate_t g = st.top(); st.pop();
687 if (!seen.insert(g).second)
continue;
694 auto it = gc_to_bc.find(w[0]);
695 if (it != gc_to_bc.end())
706 std::set<gate_t> bseen;
707 std::stack<gate_t> bst;
709 while (!bst.empty()) {
710 gate_t g = bst.top(); bst.pop();
711 if (!bseen.insert(g).second)
continue;
713 if (out.find(g) == out.end())
737 const std::map<gate_t, StructuredDNNFBuilder::InputKey> &keys)
739 std::vector<std::pair<gate_t, StructuredDNNFBuilder::InputKey>> v(
740 keys.begin(), keys.end());
741 std::sort(v.begin(), v.end(), [](
const auto &a,
const auto &b) {
742 const auto &ka = a.second, &kb = b.second;
743 if (ka.root != kb.root) return ka.root < kb.root;
744 if (ka.sec != kb.sec) return ka.sec < kb.sec;
745 int ga = (ka.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
746 int gb = (kb.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
747 if (ga != gb) return ga < gb;
748 if (ka.factor != kb.factor) return ka.factor < kb.factor;
749 return a.first < b.first;
751 std::map<gate_t, int> rank;
753 for (
const auto &p : v) rank[p.first] = r++;
767 std::string ex = gc.
getExtra(gc_root);
770 "carries no inversion-free certificate");
772 std::unordered_map<gate_t, gate_t> gc_to_bc;
774 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
776 throw CircuitException(
"compile 'inversion-free': the certificate's inputs "
777 "lack per-input order markers");
796 const MethodArgs &a,
double &result,
797 std::string &actual_method);
885 return std::ldexp(1.0,
static_cast<int>(std::min<size_t>(k, 60)));
920 c.rewriteMultivaluedGates();
1012 for(
unsigned i = 0; i < 8; ++i) {
1015 const std::string text = gc->
getExtra(g);
1016 if(text.rfind(
"route:", 0) == 0) {
1017 const std::string name = text.substr(6);
1030 const auto [info1, info2] = gc->
getInfos(g);
1031 const unsigned tag =
1047 std::string name()
const override {
return "independent"; }
1049 bool inDefaultChain()
const override {
return true; }
1052 bool handlesMultivalued()
const override {
return true; }
1054 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1062 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1065 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1066 double r = ctx.c.independentEvaluation(ctx.gate);
1067 ctx.actual_method =
"independent";
1090 RouteMethod(
provsql_route route, std::string name, std::string requirement)
1091 : route_(route), name_(std::move(name)),
1092 requirement_(std::move(requirement)) {}
1094 std::string name()
const override {
return name_; }
1096 bool inDefaultChain()
const override {
return true; }
1099 bool handlesMultivalued()
const override {
return true; }
1101 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1104 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1105 return rootRoute(ctx.gc, ctx.gc_root) == route_;
1107 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1111 if(ctx.explicitly_named && rootRoute(ctx.gc, ctx.gc_root) != route_)
1113 requirement_.c_str());
1114 double r = ctx.c.independentEvaluation(ctx.gate);
1115 ctx.actual_method = name_;
1121 const std::string name_;
1122 const std::string requirement_;
1128 std::string name()
const override {
return "inversion-free"; }
1130 bool inDefaultChain()
const override {
return true; }
1132 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1133 const double N =
static_cast<double>(ctx.n_inputs);
1135 * (
static_cast<double>(ctx.circuit_size) + N * std::log2(N < 2 ? 2. : N));
1137 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1143 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1144 if(ctx.explicitly_named && !ctx.inv_free_cert)
1145 provsql_error(
"method 'inversion-free' requires an inversion-free "
1146 "certificate on the provenance root (none is attached "
1147 "to the rows of a join that merges no rows and uses no "
1148 "relation twice: 'independent' applies to them)");
1149 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
1152 if(ctx.explicitly_named)
1153 provsql_error(
"method 'inversion-free': the provenance root carries a "
1154 "certificate but its inputs lack per-input order markers");
1156 throw CircuitException(
"inversion-free: inputs lack per-input order "
1161 ctx.actual_method =
"inversion-free";
1178 std::string name()
const override {
return "mobius"; }
1180 bool inDefaultChain()
const override {
return true; }
1183 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1186 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1187 return ctx.gc !=
nullptr
1188 && ctx.gc->getGateType(ctx.gc_root) ==
gate_mobius;
1190 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1191 if(ctx.gc ==
nullptr || ctx.gc->getGateType(ctx.gc_root) !=
gate_mobius)
1192 provsql_error(
"method 'mobius' requires a Möbius-route token (a "
1193 "gate_mobius signed-combination root)");
1194 ctx.actual_method =
"mobius";
1195 return mobiusProbabilityImpl(ctx.token);
1218 std::string name()
const override {
return "interpret-as-dd"; }
1220 bool producesDD()
const override {
return true; }
1227 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1230 dDNNF buildDD(EvalContext &ctx)
const override {
1231 dDNNF dd = ctx.c.interpretAsDD(ctx.gate);
1232 ctx.actual_method =
"interpret-as-dd";
1235 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1246 std::string name()
const override {
return "tree-decomposition"; }
1248 bool inDefaultChain()
const override {
return true; }
1249 bool producesDD()
const override {
return true; }
1257 std::vector<Feature> requiredFeatures()
const override {
1260 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1272 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1276 dDNNF buildDD(EvalContext &ctx)
const override {
1278 TreeDecomposition td(ctx.c);
1286 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1288 *
static_cast<double>(ctx.circuit_size)
1290 if(real_cost > ctx.cost_budget)
1291 throw CircuitException(
1292 "tree-decomposition: discovered treewidth exceeds the budget");
1294 dDNNF dd = dDNNFTreeDecompositionBuilder{ctx.c, ctx.gate, td}.build();
1295 ctx.actual_method =
"tree-decomposition";
1297 }
catch(TreeDecompositionException &) {
1298 if(ctx.explicitly_named)
1302 throw CircuitException(
"tree-decomposition: treewidth above the bound");
1305 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1306 return buildDD(ctx).probabilityEvaluation();
1317 std::string name()
const override {
return "compilation"; }
1319 bool inDefaultChain()
const override {
return true; }
1320 bool producesDD()
const override {
return true; }
1326 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1330 dDNNF buildDD(EvalContext &ctx)
const override {
1338 const std::string compiler =
1339 ctx.explicitly_named ? ctx.args : std::string();
1341 dDNNF dd = ctx.c.
compilation(ctx.gate, compiler, &used);
1344 ctx.actual_method = used.empty() ?
"compilation" :
"compilation:" + used;
1347 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1357 std::string name()
const override {
return "possible-worlds"; }
1359 bool inDefaultChain()
const override {
return true; }
1361 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1365 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1368 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1372 if(ctx.explicitly_named && !ctx.args.empty())
1375 double r = ctx.c.possibleWorlds(ctx.gate);
1376 ctx.actual_method =
"possible-worlds";
1385 std::string name()
const override {
return "monte-carlo"; }
1389 bool inDefaultChain()
const override {
return true; }
1390 bool isDeterministic()
const override {
return false; }
1392 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1393 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1395 * std::log(2.0 / tol.delta)
1396 / (tol.epsilon * tol.epsilon);
1398 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1399 unsigned long samples = monte_carlo_samples(parse_method_args(ctx.args));
1400 double r = ctx.c.monteCarlo(ctx.gate,
static_cast<unsigned>(samples));
1401 ctx.actual_method =
"monte-carlo";
1411 std::string name()
const override {
return "karp-luby"; }
1413 bool inDefaultChain()
const override {
return true; }
1414 bool isDeterministic()
const override {
return false; }
1417 std::vector<Feature> requiredFeatures()
const override {
1420 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1425 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1426 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1427 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1428 ? ctx.dnf_num_clauses_ : 1);
1429 return kCostKarpLuby *
static_cast<double>(ctx.circuit_size) * m
1430 * std::log(2.0 / tol.delta)
1431 / (tol.epsilon * tol.epsilon);
1433 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1434 std::vector<gate_t> clauses;
1435 std::vector<std::set<gate_t> > supports;
1436 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1437 provsql_warning(
"method 'karp-luby' applies only to a DNF-shaped circuit "
1438 "(a monotone OR-of-ANDs over input leaves); negation, "
1439 "comparison, aggregation, random-variable and "
1440 "multivalued-input gates are not supported");
1441 provsql_error(
"method 'karp-luby' requires a DNF-shaped provenance "
1444 double r = evaluate_karp_luby(ctx.c, clauses, supports,
1445 parse_method_args(ctx.args));
1446 ctx.actual_method =
"karp-luby";
1459 std::string name()
const override {
return "stopping-rule"; }
1461 bool inDefaultChain()
const override {
return true; }
1462 bool isDeterministic()
const override {
return false; }
1466 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1467 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1469 * std::log(2.0 / tol.delta)
1470 / (tol.epsilon * tol.epsilon);
1472 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1488 std::string name()
const override {
return "sieve"; }
1490 bool inDefaultChain()
const override {
return true; }
1493 std::vector<Feature> requiredFeatures()
const override {
1496 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1501 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1503 return std::numeric_limits<double>::infinity();
1504 return kCostSieve *
static_cast<double>(ctx.circuit_size)
1507 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1511 if(ctx.explicitly_named && !ctx.args.empty())
1516 std::vector<gate_t> clauses;
1517 std::vector<std::set<gate_t> > supports;
1518 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1519 provsql_warning(
"method 'sieve' applies only to a DNF-shaped circuit "
1520 "(a monotone OR-of-ANDs over input leaves); negation, "
1521 "comparison, aggregation, random-variable and "
1522 "multivalued-input gates are not supported");
1523 provsql_error(
"method 'sieve' requires a DNF-shaped provenance circuit");
1525 double r = ctx.c.sieve(clauses, supports);
1526 ctx.actual_method =
"sieve";
1540 std::string name()
const override {
return "d-tree"; }
1542 bool inDefaultChain()
const override {
return true; }
1550 std::vector<Feature> requiredFeatures()
const override {
1557 bool applicable(
const EvalContext &,
const Tolerance &)
const override {
1564 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1565 const double S =
static_cast<double>(ctx.circuit_size);
1583 return std::numeric_limits<double>::infinity();
1584 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1585 ? ctx.dnf_num_clauses_ : 1);
1588 double evaluate(EvalContext &ctx,
const Tolerance &tol)
const override {
1592 std::vector<gate_t> clause_roots;
1593 std::vector<std::set<gate_t> > supports;
1594 const bool is_dnf = ctx.c.dnfShape(ctx.gate, clause_roots, supports);
1595 ctx.actual_method =
"d-tree";
1601 double eps = tol.epsilon;
1603 MethodArgs a = parse_method_args(ctx.args);
1604 if(a.has(
"epsilon")) {
1605 double dummy_delta = 0.;
1606 parse_eps_delta(a,
"d-tree", eps, dummy_delta);
1615 max_width = 2. * eps;
1624 ctx.c.dnfBounds(supports, l0, u0);
1628 max_width = 2. * eps * l0;
1639 unsigned long budget_steps = 0;
1640 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1643 budget_steps =
static_cast<unsigned long>(
1644 std::max(1.0, ctx.cost_budget / ms_per_step));
1648 budget_steps = (budget_steps == 0) ? cap : std::min(budget_steps, cap);
1651 unsigned long steps = 0;
1652 provsql::DTreeInterval iv = is_dnf
1656 provsql_notice(
"calibrate kind=dtree path=%s S=%zu N=%zu steps=%lu budget=%lu",
1657 is_dnf ?
"dnf" :
"circuit", ctx.circuit_size, ctx.n_inputs,
1658 steps, budget_steps);
1659 const double est = 0.5 * (iv.
lower + iv.
upper);
1664 const double half = 0.5 * (iv.upper - iv.lower);
1665 if(kind == ToleranceKind::Relative && est > 0.)
1666 emit_guarantee(
"relative", half / est, 0., 0, -1, nullptr);
1668 emit_guarantee(
"additive", half, 0., 0, -1, nullptr);
1677 std::string name()
const override {
return "weightmc"; }
1679 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1680 std::string opt = wmc_opt_from_args(parse_method_args(ctx.args),
"weightmc");
1681 emit_guarantee(
"relative", eps_from_wmc_opt(opt), -1., 0, -1,
"weightmc");
1682 double r = ctx.c.wmcCount(ctx.gate,
"weightmc", opt);
1683 ctx.actual_method =
"weightmc";
1691 std::string name()
const override {
return "wmc"; }
1693 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1694 MethodArgs a = parse_method_args(ctx.args);
1695 std::string tool, tool_args;
1696 if(a.has(
"tool") || a.has(
"epsilon") || a.has(
"delta")) {
1697 reject_unknown_keys(a, {
"tool",
"epsilon",
"delta"},
"wmc");
1698 tool = a.get(
"tool");
1699 if(tool.empty() && !a.positional.empty())
1700 tool = a.positional[0];
1702 provsql_error(
"method 'wmc' requires a tool (tool=<name>)");
1703 if(a.has(
"epsilon") || a.has(
"delta")) {
1704 double eps = 0.8, delta = 0.5;
1705 parse_eps_delta(a,
"wmc", eps, delta);
1706 tool_args = a.get(
"delta") +
";" + a.get(
"epsilon");
1709 auto sep = ctx.args.find(
';');
1710 tool = (sep == std::string::npos) ? ctx.args : ctx.args.substr(0, sep);
1711 tool_args = (sep == std::string::npos) ? std::string()
1712 : ctx.args.substr(sep + 1);
1714 if(is_approx_wmc_tool(tool))
1715 emit_guarantee(
"relative", eps_from_wmc_opt(tool_args), -1., 0, -1,
1717 double r = ctx.c.wmcCount(ctx.gate, tool, tool_args);
1719 ctx.actual_method = tool.empty() ?
"wmc" :
"wmc:" + tool;
1735 +
"' does not construct a d-DNNF");
1743 std::string no_args;
1745 c, g,
nullptr,
false, no_args,
1785template<
class R,
class Run>
1786R runPortfolio(EvalContext &ctx,
const Tolerance &tol,
1787 std::vector<const ProbabilityMethod *> portfolio, Run run)
1797 std::set<Feature> acquired;
1798 std::string last_error;
1799 bool have_last_error =
false;
1804 const ProbabilityMethod *best =
nullptr;
1805 double best_cost = std::numeric_limits<double>::infinity();
1806 double second_cost = std::numeric_limits<double>::infinity();
1807 std::set<Feature> pending;
1808 for(
const ProbabilityMethod *m : portfolio) {
1810 for(
Feature f : m->requiredFeatures())
1811 if(acquired.find(f) == acquired.end()) { ready =
false; pending.insert(f); }
1812 if(!ready || !m->applicable(ctx, tol))
1814 double cost = m->estimatedCost(ctx, tol);
1815 if(cost < best_cost) { second_cost = best_cost; best_cost = cost; best = m; }
1816 else if(cost < second_cost) { second_cost = cost; }
1822 ctx.cost_budget = second_cost;
1825 bool have_pending =
false;
1827 double cheapest_fc = std::numeric_limits<double>::infinity();
1829 double fc = ctx.featureCost(f);
1830 if(fc < cheapest_fc) { cheapest_fc = fc; cheapest_f = f; have_pending =
true; }
1833 if(best !=
nullptr && (!have_pending || best_cost <= cheapest_fc)) {
1839 if(!best->handlesMultivalued())
1840 ctx.ensureMultivaluedRewritten();
1845 auto t0 = std::chrono::steady_clock::now();
1847 double ms = std::chrono::duration<double, std::milli>(
1848 std::chrono::steady_clock::now() - t0).count();
1849 provsql_notice(
"calibrate kind=method which=%s S=%zu N=%zu m=%zu w=%u "
1850 "D=%u cost=%g ms=%g", best->name().c_str(),
1851 ctx.circuit_size, ctx.n_inputs, ctx.dnf_num_clauses_,
1852 ctx.tw_proxy_, ctx.tw_max_degree_, best_cost, ms);
1856 }
catch(CircuitException &e) {
1859 last_error = e.
what();
1860 have_last_error =
true;
1861 portfolio.erase(std::remove(portfolio.begin(), portfolio.end(), best),
1864 }
else if(have_pending) {
1866 auto t0 = std::chrono::steady_clock::now();
1867 ctx.acquireFeature(cheapest_f);
1868 double ms = std::chrono::duration<double, std::milli>(
1869 std::chrono::steady_clock::now() - t0).count();
1870 provsql_notice(
"calibrate kind=feature which=%s S=%zu cost=%g ms=%g",
1873 ctx.circuit_size, cheapest_fc, ms);
1875 ctx.acquireFeature(cheapest_f);
1877 acquired.insert(cheapest_f);
1881 throw CircuitException(last_error);
1882 throw CircuitException(
"no applicable probability method in the portfolio");
1891 std::vector<const ProbabilityMethod *> portfolio;
1898 && (tol.
delta > 0. || m->isDeterministic()))
1899 portfolio.push_back(m.get());
1900 return runPortfolio<double>(ctx, tol, std::move(portfolio),
1911 std::vector<const ProbabilityMethod *> portfolio;
1914 portfolio.push_back(m.get());
1915 return runPortfolio<dDNNF>(ctx, tol, std::move(portfolio),
1934 "a provenance root produced by the safe-query "
1935 "(read-once) rewriter"));
1938 "a provenance root produced by the joint-width UCQ "
1942 "a provenance root produced by the reachability "
1969 const std::string &method,
1970 const std::string &args,
1971 bool inv_free_cert,
const Tolerance &tol,
1973 std::string *actual_method_out)
1976 const bool is_path =
1977 method.empty() || method ==
"default" || method ==
"exact";
1991 !(sampleable_agg && tol.
delta > 0.)) {
1994 std::unordered_map<gate_t, gate_t> gc_to_bc;
1996 EvalContext ctx{&gc, root, token, c, gate, &gc_to_bc,
1997 inv_free_cert, args, !is_path,
2008 provsql_error(
"Wrong method '%s' for probability evaluation",
2016 if (actual_method_out !=
nullptr)
2031 "a deterministic (delta = 0) guarantee is not available for this "
2032 "circuit: it carries random-variable or HAVING-aggregate gates, for "
2033 "which only the (eps,delta) samplers apply -- use delta > 0");
2038 if (actual_method_out !=
nullptr) *actual_method_out = am;
2042 if (actual_method_out !=
nullptr) *actual_method_out =
"monte-carlo";
2044 gc, root,
static_cast<int>(monte_carlo_samples(parse_method_args(args))));
2052 "booleanSubcircuitProbability: a random-variable comparator could not "
2053 "be resolved to a Boolean and no Monte Carlo fallback is available "
2054 "(provsql.rv_mc_samples = 0)");
2055 if (actual_method_out !=
nullptr) *actual_method_out =
"monte-carlo";
2086 const MethodArgs &a,
double &result,
2087 std::string &actual_method)
2089 SampleSpec s = parse_sample_spec(a,
"stopping-rule");
2091 provsql_error(
"the relative / stopping-rule estimator is adaptive: give "
2092 "epsilon=E[,delta=D][,max_samples=M], not a fixed sample "
2094 const unsigned long cap = s.has_max ? s.max_samples : 10000000UL;
2095 unsigned long used = 0;
2096 bool reached =
false;
2099 if(reached || used == 0) {
2100 emit_guarantee(
"relative", s.eps, s.delta, used, -1,
"stopping-rule");
2102 const double eps_add = sqrt(log(2.0 / 0.05) / (2.0 * used));
2103 provsql_warning(
"relative estimate: reached the %lu-sample cap before the "
2104 "(epsilon=%g, delta=%g) relative target; reporting the "
2105 "additive guarantee at the samples spent (the event is "
2106 "likely rarer than this budget resolves -- raise "
2107 "max_samples)", cap, s.eps, s.delta);
2108 emit_guarantee(
"additive", eps_add, 0.05, used, -1,
"stopping-rule");
2110 actual_method =
"stopping-rule";
2119 if(actual_method.empty())
2122 if(current.find(actual_method) == std::string::npos) {
2123 if(!current.empty()) current +=
",";
2124 current += actual_method;
2125 SetConfigOption(
"provsql.last_eval_method", current.c_str(),
2126 PGC_USERSET, PGC_S_SESSION);
2153 std::unordered_set<gate_t> seen;
2154 std::stack<gate_t> todo;
2158 while(!todo.empty()) {
2161 if(!seen.insert(g).second)
continue;
2166 most = std::max(most,
static_cast<unsigned>(gc.
getWires(g).size()));
2170 return any ? std::max(most, 1u) : 0;
2174 (
pg_uuid_t token,
const string &method,
const string &args,
bool *isnull)
2176 if(isnull !=
nullptr)
2201 const auto &w = gc.
getWires(gc_root);
2203 provsql_error(
"probability_evaluate: this is a conditioned distribution "
2204 "(a random_variable / agg_token X | C), not a Boolean "
2205 "event; query it with expected / variance / moment / "
2206 "support, which report the conditional distribution");
2208 provsql_error(
"probability_evaluate: malformed conditioned gate "
2209 "(expected 3 children [target, evidence, joint], got %zu)",
2213 bool ev_null =
false, jt_null =
false;
2214 double pe = DatumGetFloat8(
2216 if(ev_null || pe == 0.) {
2217 if(isnull !=
nullptr)
2221 double pj = DatumGetFloat8(
2224 if(isnull !=
nullptr)
2229 if(r > 1.) r = 1.;
else if(r < 0.) r = 0.;
2230 PG_RETURN_FLOAT8(r);
2246 const bool is_path =
2247 method.empty() || method ==
"default" || method ==
"exact"
2248 || method ==
"relative" || method ==
"additive";
2249 if(is_path || method ==
"mobius") {
2252 std::unordered_map<gate_t, gate_t> dummymap;
2261 PG_RETURN_FLOAT8(r);
2265 const std::string lineage = mobiusLineageOf(token);
2267 provsql_error(
"method '%s': this Möbius-route token carries no literal "
2268 "lineage (it was built measure-only); only the default / "
2269 "'mobius' method applies", method.c_str());
2282 bool inv_free_cert =
false;
2284 std::string ex = gc.
getExtra(gc_root);
2288 inv_free_cert =
true;
2294 provsql_notice(
"inversion-free certificate read back from circuit "
2295 "root: %d atoms, %d classes, root_class=%d",
2326 const bool mc_default = method.empty() || method ==
"default";
2327 if (method !=
"monte-carlo" && method !=
"stopping-rule"
2328 && method !=
"relative" && method !=
"additive"
2332 "probability_evaluate: a comparison over random variables "
2333 "could not be resolved analytically; raise "
2334 "provsql.rv_mc_samples above 0 to enable the Monte Carlo "
2335 "fallback, or call probability_evaluate(..., 'monte-carlo', "
2337 }
else if (!mc_default) {
2339 "probability_evaluate: a comparison over random variables "
2340 "could not be resolved analytically and the hybrid evaluator "
2341 "left it unresolved; call probability_evaluate(..., "
2342 "'monte-carlo', <n>) directly for an MC estimate");
2366 const bool named = !(method.empty() || method ==
"default");
2376 const unsigned cap =
2377 (named || nested_agg)
2380 std::optional<double> p;
2389 return Float8GetDatum(*p);
2392 provsql_error(
"probability_evaluate: method %s needs a circuit whose "
2393 "only random sources are at most %u input tuples",
2402 string actual_method;
2406 void (*prev_sigint_handler)(int);
2419 if(method ==
"relative" || method ==
"additive") {
2432 SampleSpec s = parse_sample_spec(parse_method_args(args), method.c_str());
2442 gc, gc_root,
"", args, inv_free_cert, tol,
2443 false, &actual_method);
2444 }
else if(method ==
"stopping-rule") {
2447 }
else if(method ==
"monte-carlo"
2455 unsigned long samples = monte_carlo_samples(parse_method_args(args));
2469 actual_method =
"monte-carlo";
2479 false, &actual_method);
2488 CHECK_FOR_INTERRUPTS();
2497 signal (SIGINT, prev_sigint_handler);
2505 PG_RETURN_FLOAT8(result);
2513 Datum token = PG_GETARG_DATUM(0);
2520 if(!PG_ARGISNULL(1)) {
2521 text *t = PG_GETARG_TEXT_P(1);
2522 method = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2525 if(!PG_ARGISNULL(2)) {
2526 text *t = PG_GETARG_TEXT_P(2);
2527 args = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2530 bool isnull =
false;
2542 "resolve remains in the circuit (%s): supported are a "
2543 "comparison of an aggregate with a constant, between "
2544 "numeric aggregates, and between two array_agg results",
2546 }
catch(
const std::exception &e) {
2571 pg_uuid_t token = *DatumGetUUIDP(PG_GETARG_DATUM(0));
2576 std::vector<gate_t> clause_roots;
2577 std::vector<std::set<gate_t> > supports;
2578 if(!c.
dnfShape(root, clause_roots, supports))
2579 provsql_error(
"probability_bounds applies only to a DNF-shaped circuit "
2580 "(a monotone OR-of-ANDs over input leaves); negation, "
2581 "comparison, aggregation, random-variable and "
2582 "multivalued-input gates are not supported");
2584 double lower, upper;
2588 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2589 provsql_error(
"probability_bounds: expected composite return type");
2590 tupdesc = BlessTupleDesc(tupdesc);
2592 Datum values[2] = { Float8GetDatum(lower), Float8GetDatum(upper) };
2593 bool nulls[2] = {
false,
false };
2594 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
2595 }
catch(
const std::exception &e) {
Exact closed-form HAVING COUNT(*) op C probability over safe-join lineage – the recursive marginal-ve...
Closed-form CDF resolution for trivial gate_cmp shapes.
Boolean-expression (lineage formula) semiring.
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.
@ IN
Input (variable) gate representing a base tuple.
BooleanCircuit getBooleanCircuit(GenericCircuit &gc, pg_uuid_t token, gate_t &gate, std::unordered_map< gate_t, gate_t > &gc_to_bc)
Build a BooleanCircuit from an already-loaded GenericCircuit.
GenericCircuit getGenericCircuit(pg_uuid_t token)
Build a GenericCircuit from the mmap store rooted at token.
Build in-memory circuits from the mmap-backed persistent store.
gate_t
Strongly-typed gate identifier.
The single comparator-resolution pipeline and the single Boolean-subcircuit probability entry point,...
Closed-form Poisson-binomial CDF resolution for HAVING COUNT(*) op C gate_cmps.
Anytime interval-bounds probability for monotone DNFs (d-trees).
Semiring-agnostic in-memory provenance circuit.
Peephole simplifier for continuous gate_arith sub-circuits.
Closed-form probability resolution for HAVING MIN(a) op C and MAX(a) op C gate_cmps.
Monte Carlo sampling over a GenericCircuit, RV-aware.
Catalog of probability-evaluation methods (Strategy + registry).
Support-based bound check for continuous-RV comparators.
Abstract semiring interface for provenance evaluation.
In-process structured-d-DNNF construction over a query-derived variable order, for the inversion-free...
Closed-form probability resolution for HAVING SUM(a) op C gate_cmps via a weighted-sum DP.
Tree decomposition of a Boolean circuit for knowledge compilation.
Fix macro conflicts between PostgreSQL headers and the C++ STL/Boost.
Boolean circuit for provenance formula evaluation.
double karpLubyStopping(const std::vector< gate_t > &clauses, const std::vector< std::set< gate_t > > &supports, double eps, double delta, unsigned long max_samples, unsigned long &samples_used, bool &reached_target) const
Karp-Luby FPRAS with the self-adjusting stopping rule (adaptive sample count for a relative (eps,...
const std::set< gate_t > & getInputs() const
Return the set of input (IN) gate IDs.
double karpLuby(const std::vector< gate_t > &clauses, const std::vector< std::set< gate_t > > &supports, unsigned long samples) const
Karp-Luby FPRAS estimate of a DNF-shaped circuit's probability (fixed sample budget,...
dDNNF compilation(gate_t g, std::string compiler, std::string *resolved=nullptr) const
Compile the sub-circuit rooted at g to a dDNNF via an external tool.
void dnfBounds(const std::vector< std::set< gate_t > > &clauses, double &lower, double &upper) const
Cheap certified probability interval [lower,upper] of a monotone DNF, without compiling it (Olteanu-H...
bool dnfShape(gate_t g, std::vector< gate_t > &clauses, std::vector< std::set< gate_t > > &supports) const
Detect the DNF shape the Karp-Luby FPRAS requires.
Exception type thrown by circuit operations on invalid input.
virtual char const * what() const noexcept
Return the error message as a C-string.
std::vector< gate_t > & getWires(gate_t g)
Return a mutable reference to the child-wire list of gate g.
gateType getGateType(gate_t g) const
Return the type of gate g.
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
gate_t getGate(const uuid &u)
Return (or create) the gate associated with UUID u.
std::vector< gate_t >::size_type getNbGates() const
Return the total number of gates in the circuit.
In-memory provenance circuit with semiring-generic evaluation.
std::string getExtra(gate_t g) const
Return the string extra for gate g.
double getProb(gate_t g) const
Return the probability for gate g.
std::pair< unsigned, unsigned > getInfos(gate_t g) const
Return the integer annotation pair for gate g.
Top-down structured-d-DNNF builder over a query-derived variable order.
const dDNNF & dnnf() const
The constructed d-DNNF (root set, simplified).
static unsigned degeneracyLowerBound(const BooleanCircuit &bc, unsigned &max_degree)
Cheap degeneracy lower bound on the treewidth of bc's primal graph.
static constexpr int MAX_TREEWIDTH
Maximum supported treewidth.
A d-DNNF circuit supporting exact probabilistic and game-theoretic evaluation.
double probabilityEvaluation() const
Compute the exact probability of the d-DNNF being true.
Registry of ProbabilityMethod objects.
const ProbabilityMethod * byName(const std::string &name) const
Exact match on name(); nullptr if absent.
std::vector< std::unique_ptr< ProbabilityMethod > > methods_
double chooseAndRun(EvalContext &ctx, const Tolerance &tol) const
Run the auto-chooser for tol: the portfolio methods admissible for the tolerance and applicable,...
void registerMethod(std::unique_ptr< ProbabilityMethod > m)
static const MethodCatalog & instance()
The process-wide catalog, lazily populated with the built-in methods.
dDNNF chooseAndBuildDD(EvalContext &ctx, const Tolerance &tol) const
The d-DNNF analogue of chooseAndRun: cost-select among the producesDD() portfolio (interpret-as-dd / ...
Strategy interface: one concrete subclass per probability method.
virtual dDNNF buildDD(EvalContext &ctx) const
Build the d-DNNF this method constructs (only when producesDD()).
virtual double evaluate(EvalContext &ctx, const Tolerance &tol) const =0
Run the method, returning the probability.
virtual bool handlesMultivalued() const
True iff the method evaluates the raw circuit, including multivalued (BID / gate_mulinput) gates,...
virtual std::string name() const =0
Stable identifier used for byName lookup and the provsql.last_eval_method report.
Exception thrown when a semiring operation is not supported.
virtual char const * what() const noexcept
Return the error message as a C-string.
A gate kind this semiring does not interpret (a comparison, a semimodule multiplication,...
Constructs a d-DNNF from a Boolean circuit and its tree decomposition.
Provenance evaluation helper for HAVING-clause circuits.
Shared declaration for the Möbius-route probability sweep.
static const double kCostDnfShapeFeature
static const double kCostSieve
static const size_t kPossibleWorldsSanityMax
Sanity bound on the reachable-input count for the auto-chosen 2^N possible-worlds enumeration: above ...
double booleanSubcircuitProbability(GenericCircuit &gc, gate_t root, const std::string &method, const std::string &args, bool inv_free_cert, const Tolerance &tol, bool mc_fallback, std::string *actual_method_out)
Probability of the Boolean function rooted at root in gc – THE single entry point over the method por...
static const double kCostKarpLuby
std::optional< double > enumerateBooleanProbability(const GenericCircuit &gc, gate_t root, unsigned max_inputs)
The probability of root, exactly, over the possible worlds of the circuit as it is: the alternative t...
static const double kCostDTreeExact
static const double kCostTwProxyFeature
bool circuitHasNestedAggValue(const GenericCircuit &gc, gate_t root)
Whether a contribution of an aggregate is itself an aggregate result (a gate_semimod whose M side is ...
static const double kCostDTreeMsPerStepGeneral
static const double kCostCompilationFloor
static bool toleranceAdmits(ToleranceKind request, ToleranceKind method)
Admissibility of a method's guarantee under a requested tolerance.
double monteCarloRVStopping(const GenericCircuit &gc, gate_t root, double eps, double delta, unsigned long max_samples, unsigned long &samples_used, bool &reached_target)
Whole-circuit (eps,delta)-relative probability via the Dagum-Karp-Luby-Ross stopping rule.
static const double kCostStoppingRule
static const double kCostPossibleWorlds
bool circuitHasUnresolvedSampleableAgg(const GenericCircuit &gc, gate_t root)
Whether a surviving gate_agg exists and every one is sample-faithful (SUM / AVG / MIN / MAX / COUNT –...
Feature
A circuit feature a method's cost/applicability estimate depends on, but that is not free to acquire.
static const double kCostDTreeApprox
static const double kCostInversionFree
ToleranceKind
The contract the user grants – the "path".
static const double kCostTreeDecomp
static const double kCostDTreeMsPerStepDnf
static double pow2_clamped(size_t k)
2^k with the exponent clamped to keep the cost finite (a clamped exponent still sorts the method dead...
static const double kCostMonteCarlo
static const double kCostCompilation
double monteCarloRV(const GenericCircuit &gc, gate_t root, unsigned samples)
Run Monte Carlo on a circuit that may contain gate_rv leaves.
bool circuitHasRV(const GenericCircuit &gc, gate_t root)
Walk the circuit reachable from root looking for any gate_rv.
DTreeInterval dtreeBounds(const BooleanCircuit &c, Clauses clauses, double max_width, unsigned long budget, unsigned long *steps_out)
dDNNF makeDDAuto(BooleanCircuit &c, gate_t g)
Cost-select a d-DNNF construction route for gate g of Boolean circuit c and build it – the default ma...
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...
void resolveComparators(GenericCircuit &gc, gate_t root, bool simplify, bool decompose)
Run the comparator-resolution pipeline on gc, rewriting every gate_cmp (RV comparison,...
static const size_t kSieveSanityMaxClauses
Largest clause count for which the auto-chosen sieve (2^m inclusion-exclusion) is admitted (matches B...
static const double kCostIndependent
Datum probability_evaluate(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for probability_evaluate().
static Datum probability_evaluate_internal(pg_uuid_t token, const string &method, const string &args, bool *isnull)
static std::map< gate_t, int > inversion_free_rank(const std::map< gate_t, StructuredDNNFBuilder::InputKey > &keys)
Flatten the per-input order keys into a total rank for the structured builder's order-only constructo...
static const char *const kPossibleWorldsAgg
Core implementation of probability evaluation for a circuit token.
double mobius_probability_of(pg_uuid_t token)
External entry point for the Möbius-route probability sweep (declared in mobius_evaluate....
Datum probability_bounds(PG_FUNCTION_ARGS)
PostgreSQL-callable wrapper for the d-tree leaf bound: probability_bounds(token uuid,...
static const unsigned kPossibleWorldsAggMaxInputs
Most inputs it enumerates the 2^n worlds of.
static void record_last_eval_method(const std::string &actual_method)
Record the method just used in the provsql.last_eval_method GUC (comma-separated, deduplicated across...
dDNNF buildInversionFreeDDNNF(pg_uuid_t token)
Compile a query certified inversion-free to its structured d-DNNF.
static bool collect_inversion_free_keys(const GenericCircuit &gc, gate_t gc_root, const std::unordered_map< gate_t, gate_t > &gc_to_bc, const BooleanCircuit &c, gate_t bc_root, std::map< gate_t, StructuredDNNFBuilder::InputKey > &out)
Collect the inversion-free per-input order keys for the structured builder.
static void run_stopping_rule(GenericCircuit &gc, gate_t gc_root, const MethodArgs &a, double &result, std::string &actual_method)
Whole-circuit (eps,delta)-relative estimate via the stopping rule (shared by the explicit 'stopping-r...
static void provsql_sigint_handler(int)
SIGINT handler that sets the global interrupted flag.
static unsigned maxAggContributions(const GenericCircuit &gc, gate_t root)
The most contributions an aggregation gate below root has, 0 when neither a comparison nor an aggrega...
bool provsql_absorptive_provenance
Derived flag: the session's provenance class is 'absorptive' or 'boolean' – licenses constructions so...
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
bool provsql_simplify_on_load
Run universal cmp-resolution passes when getGenericCircuit returns; controlled by the provsql....
bool provsql_inversion_free
Insert the inversion-free structured-d-DNNF path into the default probability chain (after independen...
char * provsql_last_eval_method
Last probability evaluation method(s) used; exposed via provsql.last_eval_method.
int provsql_rv_mc_samples
Default sample count for analytical-evaluator MC fallbacks; 0 disables fallback (callers raise instea...
int provsql_dtree_max_subproblems
Debug/safety hard cap on d-tree subproblems before it bails (0 = off; the chooser auto-budgets at the...
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
bool provsql_boolean_provenance
Derived flag: the session's provenance class is 'boolean' – enables the Boolean-only machinery (safe-...
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_unsupported(scope, tag, fmt,...)
Refuse a query ProvSQL cannot track, and abort the transaction.
#define provsql_warning(fmt,...)
Emit a ProvSQL warning message (execution continues).
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
Background worker and IPC primitives for mmap-backed circuit storage.
Shared-memory segment and inter-process pipe management.
Core types, constants, and utilities shared across ProvSQL.
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_mobius
Signed Möbius combination: a MEASURE-only gate carrying one integer coefficient per child (in extra,...
@ gate_conditioned
Conditioning marker with two children [target, evidence]: measure-only, probability_evaluate returns ...
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
provsql_route
Tags identifying the planner-time route that produced a circuit.
@ PROVSQL_ROUTE_BOUNDED_JW
Joint-width UCQ compiler (src/UCQJointCompiler.h).
@ PROVSQL_ROUTE_SQ_REWRITE
Hierarchical-CQ read-once rewrite (src/safe_query.c).
@ PROVSQL_ROUTE_NONE
No route rewrite: ordinary lineage.
@ PROVSQL_ROUTE_REACHABILITY
Recursive-reachability compiler (src/reachability_evaluate.cpp).
static const char * provsql_route_name(provsql_route route)
The name of a route, as the probability dispatcher reports it and as the annotation wrapper of a mate...
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.
SafeCert * safe_cert_parse(const char *str)
Parse a C-prefixed recipe string (as produced by safe_cert_serialise and read back from an annotation...
bool safe_cert_key_parse(const char *str, SafeCertKey *out)
Parse a K-prefixed order-key string into out.
Tractability certificate for the inversion-free UCQ(OBDD) path.
@ CERT_INVERSION_FREE
Inversion-free UCQ(OBDD) over TID inputs.
#define SAFE_CERT_EXTRA_PREFIX_RECIPE
Discriminator prefixes for the annotation gate's extra payload.
Query-derived order recipe for the structured-d-DNNF builder.
int nclasses
Number of (compacted) equivalence classes.
int root_class
Compacted id of the root class (touches every atom).
int natoms
Number of atoms (range-table entries).
Per-evaluation circuit state threaded to a method's evaluate().
bool multivalued_rewritten
double cost_budget
Speculative-execution budget: the estimated cost (in the chooser's ms-ish units) of the next-cheapest...
bool hasFeature(Feature f) const
void ensureMultivaluedRewritten()
double featureCost(Feature f) const
Heuristic acquisition cost of f, in the same work units as a method's estimatedCost (so the chooser c...
size_t n_inputs
input count N (O(1) cost feature)
std::unordered_map< gate_t, gate_t > * gc_to_bc
void ensureDnfShape() const
std::string actual_method
void acquireFeature(Feature f)
bool explicitly_named
invoked via byName (vs the default chain)
void ensureTreewidthProxy() const
size_t circuit_size
gate count S, the circuit-size parameter (O(1))
std::size_t dnf_num_clauses_
bool evaluate(const std::vector< long > &values, const std::vector< bool > &mask, long constant, ComparisonOperator op, std::unique_ptr< Aggregator > aggregator)
Evaluate whether the aggregation of values masked by mask satisfies op constant.