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>
100 auto it = memo.find(g);
101 if(it != memo.end())
return it->second;
102 CHECK_FOR_INTERRUPTS();
115 for(
gate_t c : gc.
getWires(g)) pn *= (1.0 - mobiusEvalRec(gc, c, memo));
123 r = mobiusEvalRec(gc, w[0], memo) - mobiusEvalRec(gc, w[1], memo);
129 const std::string extra = gc.
getExtra(g);
134 std::map<std::string,long> co;
138 while(i < extra.size()) {
139 while(i < extra.size() && (extra[i]==
' '||extra[i]==
'\t')) ++i;
140 if(i >= extra.size())
break;
142 while(j < extra.size() && extra[j]!=
' ' && extra[j]!=
'\t') ++j;
143 const std::string tok = extra.substr(i, j-i);
144 if(tok.size()>2 && tok[0]==
'L' && tok[1]==
':')
145 lineage = tok.substr(2);
147 const std::size_t colon = tok.rfind(
':');
148 if(colon != std::string::npos)
149 co[tok.substr(0,colon)] =
150 std::strtol(tok.substr(colon+1).c_str(),
nullptr, 10);
156 for(std::size_t i=0;i<w.size();++i) {
160 auto cit = co.find(u);
163 v +=
static_cast<double>(cit->second) * mobiusEvalRec(gc, w[i], memo);
167 constexpr double tol = 1e-9;
168 if(v < -tol || v > 1.0 + tol)
169 provsql_warning(
"mobius: signed combination left [0,1] before clamping "
170 "(value %g) -- possible compiler bug", v);
171 if(v < 0.) v = 0.;
else if(v > 1.) v = 1.;
187std::string mobiusLineageOf(
pg_uuid_t token)
199 const std::size_t p = ex.find(
"L:");
200 if(p != std::string::npos) {
201 const std::size_t e = ex.find(
' ', p);
202 lineage = ex.substr(p + 2,
203 e == std::string::npos ? std::string::npos : e - p - 2);
212double mobiusProbabilityImpl(
pg_uuid_t token)
228 std::map<gate_t,double> memo;
229 r = mobiusEvalRec(gc, root, memo);
244string trim_arg(
const string &s)
246 size_t a = s.find_first_not_of(
" \t");
247 if(a == string::npos)
249 size_t b = s.find_last_not_of(
" \t");
250 return s.substr(a, b - a + 1);
262 map<string, string> kv;
263 vector<string> positional;
264 bool has(
const string &k)
const {
return kv.find(k) != kv.end(); }
265 string get(
const string &k)
const {
266 auto it = kv.find(k);
267 return it == kv.end() ? string() : it->second;
282MethodArgs parse_method_args(
const string &args)
285 stringstream ss(args);
287 while(getline(ss, tok,
',')) {
288 string t = trim_arg(tok);
291 auto eq = t.find(
'=');
292 if(eq == string::npos) {
293 out.positional.push_back(t);
295 string key = trim_arg(t.substr(0, eq));
296 string val = trim_arg(t.substr(eq + 1));
297 transform(key.begin(), key.end(), key.begin(),
298 [](
unsigned char c){ return tolower(c); });
308void reject_unknown_keys(
const MethodArgs &a,
const set<string> &allowed,
311 for(
const auto &p : a.kv)
312 if(allowed.find(p.first) == allowed.end())
314 method, p.first.c_str());
318bool parse_ulong_full(
const string &v,
unsigned long &out)
320 if(v.empty() || v.find_first_not_of(
"0123456789") != string::npos)
324 out = stoul(v, &pos);
325 return pos == v.size();
326 }
catch(
const std::exception &) {
332bool parse_double_full(
const string &v,
double &out)
339 return pos == v.size();
340 }
catch(
const std::exception &) {
354void parse_eps_delta(
const MethodArgs &a,
const char *method,
355 double &eps,
double &delta)
357 if(a.has(
"epsilon") && (!parse_double_full(a.get(
"epsilon"), eps)
358 || eps <= 0. || eps > 1.))
359 provsql_error(
"method '%s': epsilon must be in (0, 1]", method);
360 if(a.has(
"delta") && (!parse_double_full(a.get(
"delta"), delta)
361 || delta < 0. || delta >= 1.))
362 provsql_error(
"method '%s': delta must be in [0, 1)", method);
374 unsigned long samples = 0;
375 double eps = 0.1, delta = 0.05;
376 bool has_max =
false;
377 unsigned long max_samples = 0;
389SampleSpec parse_sample_spec(
const MethodArgs &a,
const char *method)
391 reject_unknown_keys(a, {
"samples",
"epsilon",
"delta",
"max_samples"}, method);
393 const bool has_samples = a.has(
"samples") || !a.positional.empty();
394 const bool has_adaptive = a.has(
"epsilon") || a.has(
"delta");
396 if(a.positional.size() > 1)
397 provsql_error(
"method '%s': too many positional arguments", method);
398 if(a.has(
"samples") && !a.positional.empty())
399 provsql_error(
"method '%s': give either samples= or a bare integer, "
401 if(has_samples && has_adaptive)
402 provsql_error(
"method '%s': samples is mutually exclusive with "
403 "epsilon/delta", method);
404 if(a.has(
"max_samples") && !has_adaptive)
405 provsql_error(
"method '%s': max_samples applies only to the adaptive "
406 "epsilon/delta path", method);
407 if(a.has(
"delta") && !a.has(
"epsilon"))
408 provsql_error(
"method '%s': delta requires epsilon", method);
413 const string v = a.has(
"samples") ? a.get(
"samples") : a.positional[0];
414 if(!parse_ulong_full(v, s.samples) || s.samples == 0)
415 provsql_error(
"method '%s': invalid sample count '%s'", method, v.c_str());
419 parse_eps_delta(a, method, s.eps, s.delta);
423 if(s.delta == 0. &&
string(method) !=
"relative"
424 &&
string(method) !=
"additive")
425 provsql_error(
"method '%s': delta must be in (0, 1); delta = 0 "
426 "(deterministic) is supported only on the 'relative' / "
427 "'additive' paths, which route to the d-tree or an exact "
429 if(a.has(
"max_samples")) {
431 if(!parse_ulong_full(a.get(
"max_samples"), s.max_samples) || s.max_samples == 0)
432 provsql_error(
"method '%s': invalid max_samples '%s'", method,
433 a.get(
"max_samples").c_str());
439unsigned long finalize_adaptive(
double nd,
const SampleSpec &s)
443 unsigned long n = (nd >= 9e18) ?
static_cast<unsigned long>(9e18)
444 :
static_cast<unsigned long>(nd);
447 if(s.has_max && n > s.max_samples)
453string fmt_num(
double x)
456 snprintf(buf,
sizeof(buf),
"%.6g", x);
473void emit_guarantee(
const char *kind,
double eps,
double delta,
474 unsigned long samples,
long clauses,
const char *tool)
478 string msg =
"approximation-guarantee: kind=" + string(kind)
479 +
" eps=" + fmt_num(eps);
481 msg +=
" delta=" + fmt_num(delta);
483 msg +=
" samples=" + std::to_string(samples);
485 msg +=
" clauses=" + std::to_string(clauses);
487 msg +=
" tool=" + string(tool);
492bool is_approx_wmc_tool(
const string &tool)
494 return tool ==
"weightmc" || tool ==
"approxmc";
498double eps_from_wmc_opt(
const string &opt)
500 auto semi = opt.find(
';');
501 if(semi == string::npos)
504 return (parse_double_full(opt.substr(semi + 1), e) && e > 0.) ? e : 0.8;
518unsigned long monte_carlo_samples(
const MethodArgs &a)
520 SampleSpec s = parse_sample_spec(a,
"monte-carlo");
521 unsigned long n = s.fixed
523 : finalize_adaptive(ceil(log(2.0 / s.delta) / (2.0 * s.eps * s.eps)), s);
526 const double eps = s.fixed
527 ? sqrt(log(2.0 / 0.05) / (2.0 *
static_cast<double>(n))) : s.eps;
528 const double delta = s.fixed ? 0.05 : s.delta;
529 emit_guarantee(
"additive", eps, delta, n, -1,
nullptr);
543string wmc_opt_from_args(
const MethodArgs &a,
const char *method)
545 if(!a.positional.empty()) {
546 if(a.has(
"epsilon") || a.has(
"delta"))
547 provsql_error(
"method '%s': give either the legacy 'delta;epsilon' or "
548 "epsilon=/delta=, not both", method);
549 if(a.positional.size() > 1)
550 provsql_error(
"method '%s': too many positional arguments", method);
551 return a.positional[0];
553 reject_unknown_keys(a, {
"epsilon",
"delta"}, method);
556 double eps = 0.8, delta = 0.5;
557 parse_eps_delta(a, method, eps, delta);
558 return (a.has(
"delta") ? a.get(
"delta") :
string()) +
";"
559 + (a.has(
"epsilon") ? a.get(
"epsilon") :
string());
584 const std::vector<gate_t> &clauses,
585 const std::vector<std::set<gate_t> > &supports,
588 const size_t m = clauses.size();
589 const double e = exp(1.0);
590 const double mm = (m == 0) ? 1. : static_cast<double>(m);
591 SampleSpec s = parse_sample_spec(a,
"karp-luby");
594 double r = c.
karpLuby(clauses, supports, s.samples);
596 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(s.samples));
597 emit_guarantee(
"relative", eps, 0.05, s.samples,
static_cast<long>(m),
nullptr);
602 const double Y = 4.0 * (e - 2.0) * log(2.0 / s.delta) / (s.eps * s.eps);
603 const double Y1 = 1.0 + (1.0 + s.eps) * Y;
604 const unsigned long cap =
605 s.has_max ? s.max_samples : finalize_adaptive(ceil(Y1 * mm), s);
606 unsigned long used = 0;
607 bool reached =
false;
610 if(reached || used == 0) {
611 emit_guarantee(
"relative", s.eps, s.delta, used,
static_cast<long>(m),
nullptr);
614 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(used));
616 "%lu-sample cap before the (epsilon=%g, delta=%g) target; "
617 "reporting the relative guarantee achieved at the samples "
618 "spent", cap, s.eps, s.delta);
619 emit_guarantee(
"relative", eps, 0.05, used,
static_cast<long>(m),
nullptr);
630 return mobiusProbabilityImpl(token);
655 if (!proc_exit_inprogress) {
656 InterruptPending =
true;
657 QueryCancelPending =
true;
677 const std::unordered_map<gate_t, gate_t> &gc_to_bc,
679 std::map<gate_t, StructuredDNNFBuilder::InputKey> &out)
681 std::set<gate_t> seen;
682 std::stack<gate_t> st;
684 while (!st.empty()) {
685 gate_t g = st.top(); st.pop();
686 if (!seen.insert(g).second)
continue;
693 auto it = gc_to_bc.find(w[0]);
694 if (it != gc_to_bc.end())
705 std::set<gate_t> bseen;
706 std::stack<gate_t> bst;
708 while (!bst.empty()) {
709 gate_t g = bst.top(); bst.pop();
710 if (!bseen.insert(g).second)
continue;
712 if (out.find(g) == out.end())
736 const std::map<gate_t, StructuredDNNFBuilder::InputKey> &keys)
738 std::vector<std::pair<gate_t, StructuredDNNFBuilder::InputKey>> v(
739 keys.begin(), keys.end());
740 std::sort(v.begin(), v.end(), [](
const auto &a,
const auto &b) {
741 const auto &ka = a.second, &kb = b.second;
742 if (ka.root != kb.root) return ka.root < kb.root;
743 if (ka.sec != kb.sec) return ka.sec < kb.sec;
744 int ga = (ka.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
745 int gb = (kb.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
746 if (ga != gb) return ga < gb;
747 if (ka.factor != kb.factor) return ka.factor < kb.factor;
748 return a.first < b.first;
750 std::map<gate_t, int> rank;
752 for (
const auto &p : v) rank[p.first] = r++;
766 std::string ex = gc.
getExtra(gc_root);
769 "carries no inversion-free certificate");
771 std::unordered_map<gate_t, gate_t> gc_to_bc;
773 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
775 throw CircuitException(
"compile 'inversion-free': the certificate's inputs "
776 "lack per-input order markers");
795 const MethodArgs &a,
double &result,
796 std::string &actual_method);
884 return std::ldexp(1.0,
static_cast<int>(std::min<size_t>(k, 60)));
919 c.rewriteMultivaluedGates();
1009 for(
unsigned i = 0; i < 8; ++i) {
1015 const auto [info1, info2] = gc->
getInfos(g);
1016 const unsigned tag =
1032 std::string name()
const override {
return "independent"; }
1034 bool inDefaultChain()
const override {
return true; }
1037 bool handlesMultivalued()
const override {
return true; }
1039 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1047 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1050 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1051 double r = ctx.c.independentEvaluation(ctx.gate);
1052 ctx.actual_method =
"independent";
1075 RouteMethod(
provsql_route route, std::string name, std::string requirement)
1076 : route_(route), name_(std::move(name)),
1077 requirement_(std::move(requirement)) {}
1079 std::string name()
const override {
return name_; }
1081 bool inDefaultChain()
const override {
return true; }
1084 bool handlesMultivalued()
const override {
return true; }
1086 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1089 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1090 return rootRoute(ctx.gc, ctx.gc_root) == route_;
1092 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1096 if(ctx.explicitly_named && rootRoute(ctx.gc, ctx.gc_root) != route_)
1098 requirement_.c_str());
1099 double r = ctx.c.independentEvaluation(ctx.gate);
1100 ctx.actual_method = name_;
1106 const std::string name_;
1107 const std::string requirement_;
1113 std::string name()
const override {
return "inversion-free"; }
1115 bool inDefaultChain()
const override {
return true; }
1117 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1118 const double N =
static_cast<double>(ctx.n_inputs);
1120 * (
static_cast<double>(ctx.circuit_size) + N * std::log2(N < 2 ? 2. : N));
1122 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1128 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1129 if(ctx.explicitly_named && !ctx.inv_free_cert)
1130 provsql_error(
"method 'inversion-free' requires an inversion-free "
1131 "certificate on the provenance root");
1132 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
1135 if(ctx.explicitly_named)
1136 provsql_error(
"method 'inversion-free': the provenance root carries a "
1137 "certificate but its inputs lack per-input order markers");
1139 throw CircuitException(
"inversion-free: inputs lack per-input order "
1144 ctx.actual_method =
"inversion-free";
1161 std::string name()
const override {
return "mobius"; }
1163 bool inDefaultChain()
const override {
return true; }
1166 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1169 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1170 return ctx.gc !=
nullptr
1171 && ctx.gc->getGateType(ctx.gc_root) ==
gate_mobius;
1173 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1174 if(ctx.gc ==
nullptr || ctx.gc->getGateType(ctx.gc_root) !=
gate_mobius)
1175 provsql_error(
"method 'mobius' requires a Möbius-route token (a "
1176 "gate_mobius signed-combination root)");
1177 ctx.actual_method =
"mobius";
1178 return mobiusProbabilityImpl(ctx.token);
1201 std::string name()
const override {
return "interpret-as-dd"; }
1203 bool producesDD()
const override {
return true; }
1210 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1213 dDNNF buildDD(EvalContext &ctx)
const override {
1214 dDNNF dd = ctx.c.interpretAsDD(ctx.gate);
1215 ctx.actual_method =
"interpret-as-dd";
1218 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1229 std::string name()
const override {
return "tree-decomposition"; }
1231 bool inDefaultChain()
const override {
return true; }
1232 bool producesDD()
const override {
return true; }
1240 std::vector<Feature> requiredFeatures()
const override {
1243 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1255 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1259 dDNNF buildDD(EvalContext &ctx)
const override {
1261 TreeDecomposition td(ctx.c);
1269 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1271 *
static_cast<double>(ctx.circuit_size)
1273 if(real_cost > ctx.cost_budget)
1274 throw CircuitException(
1275 "tree-decomposition: discovered treewidth exceeds the budget");
1277 dDNNF dd = dDNNFTreeDecompositionBuilder{ctx.c, ctx.gate, td}.build();
1278 ctx.actual_method =
"tree-decomposition";
1280 }
catch(TreeDecompositionException &) {
1281 if(ctx.explicitly_named)
1285 throw CircuitException(
"tree-decomposition: treewidth above the bound");
1288 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1289 return buildDD(ctx).probabilityEvaluation();
1300 std::string name()
const override {
return "compilation"; }
1302 bool inDefaultChain()
const override {
return true; }
1303 bool producesDD()
const override {
return true; }
1309 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1313 dDNNF buildDD(EvalContext &ctx)
const override {
1321 const std::string compiler =
1322 ctx.explicitly_named ? ctx.args : std::string();
1324 dDNNF dd = ctx.c.
compilation(ctx.gate, compiler, &used);
1327 ctx.actual_method = used.empty() ?
"compilation" :
"compilation:" + used;
1330 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1340 std::string name()
const override {
return "possible-worlds"; }
1342 bool inDefaultChain()
const override {
return true; }
1344 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1348 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1351 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1355 if(ctx.explicitly_named && !ctx.args.empty())
1358 double r = ctx.c.possibleWorlds(ctx.gate);
1359 ctx.actual_method =
"possible-worlds";
1368 std::string name()
const override {
return "monte-carlo"; }
1372 bool inDefaultChain()
const override {
return true; }
1373 bool isDeterministic()
const override {
return false; }
1375 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1376 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1378 * std::log(2.0 / tol.delta)
1379 / (tol.epsilon * tol.epsilon);
1381 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1382 unsigned long samples = monte_carlo_samples(parse_method_args(ctx.args));
1383 double r = ctx.c.monteCarlo(ctx.gate,
static_cast<unsigned>(samples));
1384 ctx.actual_method =
"monte-carlo";
1394 std::string name()
const override {
return "karp-luby"; }
1396 bool inDefaultChain()
const override {
return true; }
1397 bool isDeterministic()
const override {
return false; }
1400 std::vector<Feature> requiredFeatures()
const override {
1403 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1408 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1409 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1410 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1411 ? ctx.dnf_num_clauses_ : 1);
1412 return kCostKarpLuby *
static_cast<double>(ctx.circuit_size) * m
1413 * std::log(2.0 / tol.delta)
1414 / (tol.epsilon * tol.epsilon);
1416 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1417 std::vector<gate_t> clauses;
1418 std::vector<std::set<gate_t> > supports;
1419 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1420 provsql_warning(
"method 'karp-luby' applies only to a DNF-shaped circuit "
1421 "(a monotone OR-of-ANDs over input leaves); negation, "
1422 "comparison, aggregation, random-variable and "
1423 "multivalued-input gates are not supported");
1424 provsql_error(
"method 'karp-luby' requires a DNF-shaped provenance "
1427 double r = evaluate_karp_luby(ctx.c, clauses, supports,
1428 parse_method_args(ctx.args));
1429 ctx.actual_method =
"karp-luby";
1442 std::string name()
const override {
return "stopping-rule"; }
1444 bool inDefaultChain()
const override {
return true; }
1445 bool isDeterministic()
const override {
return false; }
1449 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1450 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1452 * std::log(2.0 / tol.delta)
1453 / (tol.epsilon * tol.epsilon);
1455 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1471 std::string name()
const override {
return "sieve"; }
1473 bool inDefaultChain()
const override {
return true; }
1476 std::vector<Feature> requiredFeatures()
const override {
1479 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1484 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1486 return std::numeric_limits<double>::infinity();
1487 return kCostSieve *
static_cast<double>(ctx.circuit_size)
1490 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1494 if(ctx.explicitly_named && !ctx.args.empty())
1499 std::vector<gate_t> clauses;
1500 std::vector<std::set<gate_t> > supports;
1501 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1502 provsql_warning(
"method 'sieve' applies only to a DNF-shaped circuit "
1503 "(a monotone OR-of-ANDs over input leaves); negation, "
1504 "comparison, aggregation, random-variable and "
1505 "multivalued-input gates are not supported");
1506 provsql_error(
"method 'sieve' requires a DNF-shaped provenance circuit");
1508 double r = ctx.c.sieve(clauses, supports);
1509 ctx.actual_method =
"sieve";
1523 std::string name()
const override {
return "d-tree"; }
1525 bool inDefaultChain()
const override {
return true; }
1533 std::vector<Feature> requiredFeatures()
const override {
1540 bool applicable(
const EvalContext &,
const Tolerance &)
const override {
1547 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1548 const double S =
static_cast<double>(ctx.circuit_size);
1566 return std::numeric_limits<double>::infinity();
1567 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1568 ? ctx.dnf_num_clauses_ : 1);
1571 double evaluate(EvalContext &ctx,
const Tolerance &tol)
const override {
1575 std::vector<gate_t> clause_roots;
1576 std::vector<std::set<gate_t> > supports;
1577 const bool is_dnf = ctx.c.dnfShape(ctx.gate, clause_roots, supports);
1578 ctx.actual_method =
"d-tree";
1584 double eps = tol.epsilon;
1586 MethodArgs a = parse_method_args(ctx.args);
1587 if(a.has(
"epsilon")) {
1588 double dummy_delta = 0.;
1589 parse_eps_delta(a,
"d-tree", eps, dummy_delta);
1598 max_width = 2. * eps;
1607 ctx.c.dnfBounds(supports, l0, u0);
1611 max_width = 2. * eps * l0;
1622 unsigned long budget_steps = 0;
1623 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1626 budget_steps =
static_cast<unsigned long>(
1627 std::max(1.0, ctx.cost_budget / ms_per_step));
1631 budget_steps = (budget_steps == 0) ? cap : std::min(budget_steps, cap);
1634 unsigned long steps = 0;
1635 provsql::DTreeInterval iv = is_dnf
1639 provsql_notice(
"calibrate kind=dtree path=%s S=%zu N=%zu steps=%lu budget=%lu",
1640 is_dnf ?
"dnf" :
"circuit", ctx.circuit_size, ctx.n_inputs,
1641 steps, budget_steps);
1642 const double est = 0.5 * (iv.
lower + iv.
upper);
1647 const double half = 0.5 * (iv.upper - iv.lower);
1648 if(kind == ToleranceKind::Relative && est > 0.)
1649 emit_guarantee(
"relative", half / est, 0., 0, -1, nullptr);
1651 emit_guarantee(
"additive", half, 0., 0, -1, nullptr);
1660 std::string name()
const override {
return "weightmc"; }
1662 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1663 std::string opt = wmc_opt_from_args(parse_method_args(ctx.args),
"weightmc");
1664 emit_guarantee(
"relative", eps_from_wmc_opt(opt), -1., 0, -1,
"weightmc");
1665 double r = ctx.c.wmcCount(ctx.gate,
"weightmc", opt);
1666 ctx.actual_method =
"weightmc";
1674 std::string name()
const override {
return "wmc"; }
1676 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1677 MethodArgs a = parse_method_args(ctx.args);
1678 std::string tool, tool_args;
1679 if(a.has(
"tool") || a.has(
"epsilon") || a.has(
"delta")) {
1680 reject_unknown_keys(a, {
"tool",
"epsilon",
"delta"},
"wmc");
1681 tool = a.get(
"tool");
1682 if(tool.empty() && !a.positional.empty())
1683 tool = a.positional[0];
1685 provsql_error(
"method 'wmc' requires a tool (tool=<name>)");
1686 if(a.has(
"epsilon") || a.has(
"delta")) {
1687 double eps = 0.8, delta = 0.5;
1688 parse_eps_delta(a,
"wmc", eps, delta);
1689 tool_args = a.get(
"delta") +
";" + a.get(
"epsilon");
1692 auto sep = ctx.args.find(
';');
1693 tool = (sep == std::string::npos) ? ctx.args : ctx.args.substr(0, sep);
1694 tool_args = (sep == std::string::npos) ? std::string()
1695 : ctx.args.substr(sep + 1);
1697 if(is_approx_wmc_tool(tool))
1698 emit_guarantee(
"relative", eps_from_wmc_opt(tool_args), -1., 0, -1,
1700 double r = ctx.c.wmcCount(ctx.gate, tool, tool_args);
1702 ctx.actual_method = tool.empty() ?
"wmc" :
"wmc:" + tool;
1718 +
"' does not construct a d-DNNF");
1726 std::string no_args;
1728 c, g,
nullptr,
false, no_args,
1768template<
class R,
class Run>
1769R runPortfolio(EvalContext &ctx,
const Tolerance &tol,
1770 std::vector<const ProbabilityMethod *> portfolio, Run run)
1780 std::set<Feature> acquired;
1781 std::string last_error;
1782 bool have_last_error =
false;
1787 const ProbabilityMethod *best =
nullptr;
1788 double best_cost = std::numeric_limits<double>::infinity();
1789 double second_cost = std::numeric_limits<double>::infinity();
1790 std::set<Feature> pending;
1791 for(
const ProbabilityMethod *m : portfolio) {
1793 for(
Feature f : m->requiredFeatures())
1794 if(acquired.find(f) == acquired.end()) { ready =
false; pending.insert(f); }
1795 if(!ready || !m->applicable(ctx, tol))
1797 double cost = m->estimatedCost(ctx, tol);
1798 if(cost < best_cost) { second_cost = best_cost; best_cost = cost; best = m; }
1799 else if(cost < second_cost) { second_cost = cost; }
1805 ctx.cost_budget = second_cost;
1808 bool have_pending =
false;
1810 double cheapest_fc = std::numeric_limits<double>::infinity();
1812 double fc = ctx.featureCost(f);
1813 if(fc < cheapest_fc) { cheapest_fc = fc; cheapest_f = f; have_pending =
true; }
1816 if(best !=
nullptr && (!have_pending || best_cost <= cheapest_fc)) {
1822 if(!best->handlesMultivalued())
1823 ctx.ensureMultivaluedRewritten();
1828 auto t0 = std::chrono::steady_clock::now();
1830 double ms = std::chrono::duration<double, std::milli>(
1831 std::chrono::steady_clock::now() - t0).count();
1832 provsql_notice(
"calibrate kind=method which=%s S=%zu N=%zu m=%zu w=%u "
1833 "D=%u cost=%g ms=%g", best->name().c_str(),
1834 ctx.circuit_size, ctx.n_inputs, ctx.dnf_num_clauses_,
1835 ctx.tw_proxy_, ctx.tw_max_degree_, best_cost, ms);
1839 }
catch(CircuitException &e) {
1842 last_error = e.
what();
1843 have_last_error =
true;
1844 portfolio.erase(std::remove(portfolio.begin(), portfolio.end(), best),
1847 }
else if(have_pending) {
1849 auto t0 = std::chrono::steady_clock::now();
1850 ctx.acquireFeature(cheapest_f);
1851 double ms = std::chrono::duration<double, std::milli>(
1852 std::chrono::steady_clock::now() - t0).count();
1853 provsql_notice(
"calibrate kind=feature which=%s S=%zu cost=%g ms=%g",
1856 ctx.circuit_size, cheapest_fc, ms);
1858 ctx.acquireFeature(cheapest_f);
1860 acquired.insert(cheapest_f);
1864 throw CircuitException(last_error);
1865 throw CircuitException(
"no applicable probability method in the portfolio");
1874 std::vector<const ProbabilityMethod *> portfolio;
1881 && (tol.
delta > 0. || m->isDeterministic()))
1882 portfolio.push_back(m.get());
1883 return runPortfolio<double>(ctx, tol, std::move(portfolio),
1894 std::vector<const ProbabilityMethod *> portfolio;
1897 portfolio.push_back(m.get());
1898 return runPortfolio<dDNNF>(ctx, tol, std::move(portfolio),
1917 "a provenance root produced by the safe-query "
1918 "(read-once) rewriter"));
1921 "a provenance root produced by the joint-width UCQ "
1925 "a provenance root produced by the reachability "
1952 const std::string &method,
1953 const std::string &args,
1954 bool inv_free_cert,
const Tolerance &tol,
1956 std::string *actual_method_out)
1959 const bool is_path =
1960 method.empty() || method ==
"default" || method ==
"exact";
1971 std::unordered_map<gate_t, gate_t> gc_to_bc;
1973 EvalContext ctx{&gc, root, token, c, gate, &gc_to_bc,
1974 inv_free_cert, args, !is_path,
1985 provsql_error(
"Wrong method '%s' for probability evaluation",
1993 if (actual_method_out !=
nullptr)
2008 "a deterministic (delta = 0) guarantee is not available for this "
2009 "circuit: it carries random-variable or HAVING-aggregate gates, for "
2010 "which only the (eps,delta) samplers apply -- use delta > 0");
2015 if (actual_method_out !=
nullptr) *actual_method_out = am;
2019 if (actual_method_out !=
nullptr) *actual_method_out =
"monte-carlo";
2021 gc, root,
static_cast<int>(monte_carlo_samples(parse_method_args(args))));
2029 "booleanSubcircuitProbability: a random-variable comparator could not "
2030 "be resolved to a Boolean and no Monte Carlo fallback is available "
2031 "(provsql.rv_mc_samples = 0)");
2032 if (actual_method_out !=
nullptr) *actual_method_out =
"monte-carlo";
2063 const MethodArgs &a,
double &result,
2064 std::string &actual_method)
2066 SampleSpec s = parse_sample_spec(a,
"stopping-rule");
2068 provsql_error(
"the relative / stopping-rule estimator is adaptive: give "
2069 "epsilon=E[,delta=D][,max_samples=M], not a fixed sample "
2071 const unsigned long cap = s.has_max ? s.max_samples : 10000000UL;
2072 unsigned long used = 0;
2073 bool reached =
false;
2076 if(reached || used == 0) {
2077 emit_guarantee(
"relative", s.eps, s.delta, used, -1,
"stopping-rule");
2079 const double eps_add = sqrt(log(2.0 / 0.05) / (2.0 * used));
2080 provsql_warning(
"relative estimate: reached the %lu-sample cap before the "
2081 "(epsilon=%g, delta=%g) relative target; reporting the "
2082 "additive guarantee at the samples spent (the event is "
2083 "likely rarer than this budget resolves -- raise "
2084 "max_samples)", cap, s.eps, s.delta);
2085 emit_guarantee(
"additive", eps_add, 0.05, used, -1,
"stopping-rule");
2087 actual_method =
"stopping-rule";
2096 if(actual_method.empty())
2099 if(current.find(actual_method) == std::string::npos) {
2100 if(!current.empty()) current +=
",";
2101 current += actual_method;
2102 SetConfigOption(
"provsql.last_eval_method", current.c_str(),
2103 PGC_USERSET, PGC_S_SESSION);
2119 (
pg_uuid_t token,
const string &method,
const string &args,
bool *isnull)
2121 if(isnull !=
nullptr)
2146 const auto &w = gc.
getWires(gc_root);
2148 provsql_error(
"probability_evaluate: this is a conditioned distribution "
2149 "(a random_variable / agg_token X | C), not a Boolean "
2150 "event; query it with expected / variance / moment / "
2151 "support, which report the conditional distribution");
2153 provsql_error(
"probability_evaluate: malformed conditioned gate "
2154 "(expected 3 children [target, evidence, joint], got %zu)",
2158 bool ev_null =
false, jt_null =
false;
2159 double pe = DatumGetFloat8(
2161 if(ev_null || pe == 0.) {
2162 if(isnull !=
nullptr)
2166 double pj = DatumGetFloat8(
2169 if(isnull !=
nullptr)
2174 if(r > 1.) r = 1.;
else if(r < 0.) r = 0.;
2175 PG_RETURN_FLOAT8(r);
2191 const bool is_path =
2192 method.empty() || method ==
"default" || method ==
"exact"
2193 || method ==
"relative" || method ==
"additive";
2194 if(is_path || method ==
"mobius") {
2197 std::unordered_map<gate_t, gate_t> dummymap;
2206 PG_RETURN_FLOAT8(r);
2210 const std::string lineage = mobiusLineageOf(token);
2212 provsql_error(
"method '%s': this Möbius-route token carries no literal "
2213 "lineage (it was built measure-only); only the default / "
2214 "'mobius' method applies", method.c_str());
2227 bool inv_free_cert =
false;
2229 std::string ex = gc.
getExtra(gc_root);
2233 inv_free_cert =
true;
2239 provsql_notice(
"inversion-free certificate read back from circuit "
2240 "root: %d atoms, %d classes, root_class=%d",
2271 const bool mc_default = method.empty() || method ==
"default";
2272 if (method !=
"monte-carlo" && method !=
"stopping-rule"
2273 && method !=
"relative" && method !=
"additive"
2277 "probability_evaluate: a comparison over random variables "
2278 "could not be resolved analytically; raise "
2279 "provsql.rv_mc_samples above 0 to enable the Monte Carlo "
2280 "fallback, or call probability_evaluate(..., 'monte-carlo', "
2282 }
else if (!mc_default) {
2284 "probability_evaluate: a comparison over random variables "
2285 "could not be resolved analytically and the hybrid evaluator "
2286 "left it unresolved; call probability_evaluate(..., "
2287 "'monte-carlo', <n>) directly for an MC estimate");
2303 string actual_method;
2307 void (*prev_sigint_handler)(int);
2320 if(method ==
"relative" || method ==
"additive") {
2333 SampleSpec s = parse_sample_spec(parse_method_args(args), method.c_str());
2343 gc, gc_root,
"", args, inv_free_cert, tol,
2344 false, &actual_method);
2345 }
else if(method ==
"stopping-rule") {
2348 }
else if(method ==
"monte-carlo"
2356 unsigned long samples = monte_carlo_samples(parse_method_args(args));
2370 actual_method =
"monte-carlo";
2380 false, &actual_method);
2389 CHECK_FOR_INTERRUPTS();
2398 signal (SIGINT, prev_sigint_handler);
2406 PG_RETURN_FLOAT8(result);
2414 Datum token = PG_GETARG_DATUM(0);
2421 if(!PG_ARGISNULL(1)) {
2422 text *t = PG_GETARG_TEXT_P(1);
2423 method = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2426 if(!PG_ARGISNULL(2)) {
2427 text *t = PG_GETARG_TEXT_P(2);
2428 args = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2431 bool isnull =
false;
2437 }
catch(
const std::exception &e) {
2462 pg_uuid_t token = *DatumGetUUIDP(PG_GETARG_DATUM(0));
2467 std::vector<gate_t> clause_roots;
2468 std::vector<std::set<gate_t> > supports;
2469 if(!c.
dnfShape(root, clause_roots, supports))
2470 provsql_error(
"probability_bounds applies only to a DNF-shaped circuit "
2471 "(a monotone OR-of-ANDs over input leaves); negation, "
2472 "comparison, aggregation, random-variable and "
2473 "multivalued-input gates are not supported");
2475 double lower, upper;
2479 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2480 provsql_error(
"probability_bounds: expected composite return type");
2481 tupdesc = BlessTupleDesc(tupdesc);
2483 Datum values[2] = { Float8GetDatum(lower), Float8GetDatum(upper) };
2484 bool nulls[2] = {
false,
false };
2485 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
2486 }
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.
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.
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
static const double kCostDTreeExact
static const double kCostTwProxyFeature
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)
Core implementation of probability evaluation for a circuit token.
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...
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 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.
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_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).
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.