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>
99 auto it = memo.find(g);
100 if(it != memo.end())
return it->second;
101 CHECK_FOR_INTERRUPTS();
114 for(
gate_t c : gc.
getWires(g)) pn *= (1.0 - mobiusEvalRec(gc, c, memo));
122 r = mobiusEvalRec(gc, w[0], memo) - mobiusEvalRec(gc, w[1], memo);
128 const std::string extra = gc.
getExtra(g);
133 std::map<std::string,long> co;
137 while(i < extra.size()) {
138 while(i < extra.size() && (extra[i]==
' '||extra[i]==
'\t')) ++i;
139 if(i >= extra.size())
break;
141 while(j < extra.size() && extra[j]!=
' ' && extra[j]!=
'\t') ++j;
142 const std::string tok = extra.substr(i, j-i);
143 if(tok.size()>2 && tok[0]==
'L' && tok[1]==
':')
144 lineage = tok.substr(2);
146 const std::size_t colon = tok.rfind(
':');
147 if(colon != std::string::npos)
148 co[tok.substr(0,colon)] =
149 std::strtol(tok.substr(colon+1).c_str(),
nullptr, 10);
155 for(std::size_t i=0;i<w.size();++i) {
159 auto cit = co.find(u);
162 v +=
static_cast<double>(cit->second) * mobiusEvalRec(gc, w[i], memo);
166 constexpr double tol = 1e-9;
167 if(v < -tol || v > 1.0 + tol)
168 provsql_warning(
"mobius: signed combination left [0,1] before clamping "
169 "(value %g) -- possible compiler bug", v);
170 if(v < 0.) v = 0.;
else if(v > 1.) v = 1.;
186std::string mobiusLineageOf(
pg_uuid_t token)
198 const std::size_t p = ex.find(
"L:");
199 if(p != std::string::npos) {
200 const std::size_t e = ex.find(
' ', p);
201 lineage = ex.substr(p + 2,
202 e == std::string::npos ? std::string::npos : e - p - 2);
211double mobiusProbabilityImpl(
pg_uuid_t token)
227 std::map<gate_t,double> memo;
228 r = mobiusEvalRec(gc, root, memo);
243string trim_arg(
const string &s)
245 size_t a = s.find_first_not_of(
" \t");
246 if(a == string::npos)
248 size_t b = s.find_last_not_of(
" \t");
249 return s.substr(a, b - a + 1);
261 map<string, string> kv;
262 vector<string> positional;
263 bool has(
const string &k)
const {
return kv.find(k) != kv.end(); }
264 string get(
const string &k)
const {
265 auto it = kv.find(k);
266 return it == kv.end() ? string() : it->second;
281MethodArgs parse_method_args(
const string &args)
284 stringstream ss(args);
286 while(getline(ss, tok,
',')) {
287 string t = trim_arg(tok);
290 auto eq = t.find(
'=');
291 if(eq == string::npos) {
292 out.positional.push_back(t);
294 string key = trim_arg(t.substr(0, eq));
295 string val = trim_arg(t.substr(eq + 1));
296 transform(key.begin(), key.end(), key.begin(),
297 [](
unsigned char c){ return tolower(c); });
307void reject_unknown_keys(
const MethodArgs &a,
const set<string> &allowed,
310 for(
const auto &p : a.kv)
311 if(allowed.find(p.first) == allowed.end())
313 method, p.first.c_str());
317bool parse_ulong_full(
const string &v,
unsigned long &out)
319 if(v.empty() || v.find_first_not_of(
"0123456789") != string::npos)
323 out = stoul(v, &pos);
324 return pos == v.size();
325 }
catch(
const std::exception &) {
331bool parse_double_full(
const string &v,
double &out)
338 return pos == v.size();
339 }
catch(
const std::exception &) {
353void parse_eps_delta(
const MethodArgs &a,
const char *method,
354 double &eps,
double &delta)
356 if(a.has(
"epsilon") && (!parse_double_full(a.get(
"epsilon"), eps)
357 || eps <= 0. || eps > 1.))
358 provsql_error(
"method '%s': epsilon must be in (0, 1]", method);
359 if(a.has(
"delta") && (!parse_double_full(a.get(
"delta"), delta)
360 || delta < 0. || delta >= 1.))
361 provsql_error(
"method '%s': delta must be in [0, 1)", method);
373 unsigned long samples = 0;
374 double eps = 0.1, delta = 0.05;
375 bool has_max =
false;
376 unsigned long max_samples = 0;
388SampleSpec parse_sample_spec(
const MethodArgs &a,
const char *method)
390 reject_unknown_keys(a, {
"samples",
"epsilon",
"delta",
"max_samples"}, method);
392 const bool has_samples = a.has(
"samples") || !a.positional.empty();
393 const bool has_adaptive = a.has(
"epsilon") || a.has(
"delta");
395 if(a.positional.size() > 1)
396 provsql_error(
"method '%s': too many positional arguments", method);
397 if(a.has(
"samples") && !a.positional.empty())
398 provsql_error(
"method '%s': give either samples= or a bare integer, "
400 if(has_samples && has_adaptive)
401 provsql_error(
"method '%s': samples is mutually exclusive with "
402 "epsilon/delta", method);
403 if(a.has(
"max_samples") && !has_adaptive)
404 provsql_error(
"method '%s': max_samples applies only to the adaptive "
405 "epsilon/delta path", method);
406 if(a.has(
"delta") && !a.has(
"epsilon"))
407 provsql_error(
"method '%s': delta requires epsilon", method);
412 const string v = a.has(
"samples") ? a.get(
"samples") : a.positional[0];
413 if(!parse_ulong_full(v, s.samples) || s.samples == 0)
414 provsql_error(
"method '%s': invalid sample count '%s'", method, v.c_str());
418 parse_eps_delta(a, method, s.eps, s.delta);
422 if(s.delta == 0. &&
string(method) !=
"relative"
423 &&
string(method) !=
"additive")
424 provsql_error(
"method '%s': delta must be in (0, 1); delta = 0 "
425 "(deterministic) is supported only on the 'relative' / "
426 "'additive' paths, which route to the d-tree or an exact "
428 if(a.has(
"max_samples")) {
430 if(!parse_ulong_full(a.get(
"max_samples"), s.max_samples) || s.max_samples == 0)
431 provsql_error(
"method '%s': invalid max_samples '%s'", method,
432 a.get(
"max_samples").c_str());
438unsigned long finalize_adaptive(
double nd,
const SampleSpec &s)
442 unsigned long n = (nd >= 9e18) ?
static_cast<unsigned long>(9e18)
443 :
static_cast<unsigned long>(nd);
446 if(s.has_max && n > s.max_samples)
452string fmt_num(
double x)
455 snprintf(buf,
sizeof(buf),
"%.6g", x);
472void emit_guarantee(
const char *kind,
double eps,
double delta,
473 unsigned long samples,
long clauses,
const char *tool)
477 string msg =
"approximation-guarantee: kind=" + string(kind)
478 +
" eps=" + fmt_num(eps);
480 msg +=
" delta=" + fmt_num(delta);
482 msg +=
" samples=" + std::to_string(samples);
484 msg +=
" clauses=" + std::to_string(clauses);
486 msg +=
" tool=" + string(tool);
491bool is_approx_wmc_tool(
const string &tool)
493 return tool ==
"weightmc" || tool ==
"approxmc";
497double eps_from_wmc_opt(
const string &opt)
499 auto semi = opt.find(
';');
500 if(semi == string::npos)
503 return (parse_double_full(opt.substr(semi + 1), e) && e > 0.) ? e : 0.8;
517unsigned long monte_carlo_samples(
const MethodArgs &a)
519 SampleSpec s = parse_sample_spec(a,
"monte-carlo");
520 unsigned long n = s.fixed
522 : finalize_adaptive(ceil(log(2.0 / s.delta) / (2.0 * s.eps * s.eps)), s);
525 const double eps = s.fixed
526 ? sqrt(log(2.0 / 0.05) / (2.0 *
static_cast<double>(n))) : s.eps;
527 const double delta = s.fixed ? 0.05 : s.delta;
528 emit_guarantee(
"additive", eps, delta, n, -1,
nullptr);
542string wmc_opt_from_args(
const MethodArgs &a,
const char *method)
544 if(!a.positional.empty()) {
545 if(a.has(
"epsilon") || a.has(
"delta"))
546 provsql_error(
"method '%s': give either the legacy 'delta;epsilon' or "
547 "epsilon=/delta=, not both", method);
548 if(a.positional.size() > 1)
549 provsql_error(
"method '%s': too many positional arguments", method);
550 return a.positional[0];
552 reject_unknown_keys(a, {
"epsilon",
"delta"}, method);
555 double eps = 0.8, delta = 0.5;
556 parse_eps_delta(a, method, eps, delta);
557 return (a.has(
"delta") ? a.get(
"delta") :
string()) +
";"
558 + (a.has(
"epsilon") ? a.get(
"epsilon") :
string());
583 const std::vector<gate_t> &clauses,
584 const std::vector<std::set<gate_t> > &supports,
587 const size_t m = clauses.size();
588 const double e = exp(1.0);
589 const double mm = (m == 0) ? 1. : static_cast<double>(m);
590 SampleSpec s = parse_sample_spec(a,
"karp-luby");
593 double r = c.
karpLuby(clauses, supports, s.samples);
595 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(s.samples));
596 emit_guarantee(
"relative", eps, 0.05, s.samples,
static_cast<long>(m),
nullptr);
601 const double Y = 4.0 * (e - 2.0) * log(2.0 / s.delta) / (s.eps * s.eps);
602 const double Y1 = 1.0 + (1.0 + s.eps) * Y;
603 const unsigned long cap =
604 s.has_max ? s.max_samples : finalize_adaptive(ceil(Y1 * mm), s);
605 unsigned long used = 0;
606 bool reached =
false;
609 if(reached || used == 0) {
610 emit_guarantee(
"relative", s.eps, s.delta, used,
static_cast<long>(m),
nullptr);
613 sqrt(4.0 * (e - 2.0) * mm * log(2.0 / 0.05) /
static_cast<double>(used));
615 "%lu-sample cap before the (epsilon=%g, delta=%g) target; "
616 "reporting the relative guarantee achieved at the samples "
617 "spent", cap, s.eps, s.delta);
618 emit_guarantee(
"relative", eps, 0.05, used,
static_cast<long>(m),
nullptr);
629 return mobiusProbabilityImpl(token);
654 if (!proc_exit_inprogress) {
655 InterruptPending =
true;
656 QueryCancelPending =
true;
676 const std::unordered_map<gate_t, gate_t> &gc_to_bc,
678 std::map<gate_t, StructuredDNNFBuilder::InputKey> &out)
680 std::set<gate_t> seen;
681 std::stack<gate_t> st;
683 while (!st.empty()) {
684 gate_t g = st.top(); st.pop();
685 if (!seen.insert(g).second)
continue;
692 auto it = gc_to_bc.find(w[0]);
693 if (it != gc_to_bc.end())
704 std::set<gate_t> bseen;
705 std::stack<gate_t> bst;
707 while (!bst.empty()) {
708 gate_t g = bst.top(); bst.pop();
709 if (!bseen.insert(g).second)
continue;
711 if (out.find(g) == out.end())
735 const std::map<gate_t, StructuredDNNFBuilder::InputKey> &keys)
737 std::vector<std::pair<gate_t, StructuredDNNFBuilder::InputKey>> v(
738 keys.begin(), keys.end());
739 std::sort(v.begin(), v.end(), [](
const auto &a,
const auto &b) {
740 const auto &ka = a.second, &kb = b.second;
741 if (ka.root != kb.root) return ka.root < kb.root;
742 if (ka.sec != kb.sec) return ka.sec < kb.sec;
743 int ga = (ka.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
744 int gb = (kb.factor == StructuredDNNFBuilder::GUARD_FACTOR) ? 0 : 1;
745 if (ga != gb) return ga < gb;
746 if (ka.factor != kb.factor) return ka.factor < kb.factor;
747 return a.first < b.first;
749 std::map<gate_t, int> rank;
751 for (
const auto &p : v) rank[p.first] = r++;
765 std::string ex = gc.
getExtra(gc_root);
768 "carries no inversion-free certificate");
770 std::unordered_map<gate_t, gate_t> gc_to_bc;
772 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
774 throw CircuitException(
"compile 'inversion-free': the certificate's inputs "
775 "lack per-input order markers");
794 const MethodArgs &a,
double &result,
795 std::string &actual_method);
883 return std::ldexp(1.0,
static_cast<int>(std::min<size_t>(k, 60)));
918 c.rewriteMultivaluedGates();
989class IndependentMethod :
public ProbabilityMethod {
991 std::string name()
const override {
return "independent"; }
993 bool inDefaultChain()
const override {
return true; }
996 bool handlesMultivalued()
const override {
return true; }
998 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1001 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1002 double r = ctx.c.independentEvaluation(ctx.gate);
1003 ctx.actual_method =
"independent";
1011 std::string name()
const override {
return "inversion-free"; }
1013 bool inDefaultChain()
const override {
return true; }
1015 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1016 const double N =
static_cast<double>(ctx.n_inputs);
1018 * (
static_cast<double>(ctx.circuit_size) + N * std::log2(N < 2 ? 2. : N));
1020 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1026 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1027 if(ctx.explicitly_named && !ctx.inv_free_cert)
1028 provsql_error(
"method 'inversion-free' requires an inversion-free "
1029 "certificate on the provenance root");
1030 std::map<gate_t, StructuredDNNFBuilder::InputKey> keys;
1033 if(ctx.explicitly_named)
1034 provsql_error(
"method 'inversion-free': the provenance root carries a "
1035 "certificate but its inputs lack per-input order markers");
1037 throw CircuitException(
"inversion-free: inputs lack per-input order "
1042 ctx.actual_method =
"inversion-free";
1059 std::string name()
const override {
return "mobius"; }
1061 bool inDefaultChain()
const override {
return true; }
1064 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1067 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1068 return ctx.gc !=
nullptr
1069 && ctx.gc->getGateType(ctx.gc_root) ==
gate_mobius;
1071 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1072 if(ctx.gc ==
nullptr || ctx.gc->getGateType(ctx.gc_root) !=
gate_mobius)
1073 provsql_error(
"method 'mobius' requires a Möbius-route token (a "
1074 "gate_mobius signed-combination root)");
1075 ctx.actual_method =
"mobius";
1076 return mobiusProbabilityImpl(ctx.token);
1099 std::string name()
const override {
return "interpret-as-dd"; }
1101 bool producesDD()
const override {
return true; }
1108 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1111 dDNNF buildDD(EvalContext &ctx)
const override {
1112 dDNNF dd = ctx.c.interpretAsDD(ctx.gate);
1113 ctx.actual_method =
"interpret-as-dd";
1116 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1127 std::string name()
const override {
return "tree-decomposition"; }
1129 bool inDefaultChain()
const override {
return true; }
1130 bool producesDD()
const override {
return true; }
1138 std::vector<Feature> requiredFeatures()
const override {
1141 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1153 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1157 dDNNF buildDD(EvalContext &ctx)
const override {
1159 TreeDecomposition td(ctx.c);
1167 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1169 *
static_cast<double>(ctx.circuit_size)
1171 if(real_cost > ctx.cost_budget)
1172 throw CircuitException(
1173 "tree-decomposition: discovered treewidth exceeds the budget");
1175 dDNNF dd = dDNNFTreeDecompositionBuilder{ctx.c, ctx.gate, td}.build();
1176 ctx.actual_method =
"tree-decomposition";
1178 }
catch(TreeDecompositionException &) {
1179 if(ctx.explicitly_named)
1183 throw CircuitException(
"tree-decomposition: treewidth above the bound");
1186 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1187 return buildDD(ctx).probabilityEvaluation();
1198 std::string name()
const override {
return "compilation"; }
1200 bool inDefaultChain()
const override {
return true; }
1201 bool producesDD()
const override {
return true; }
1207 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1211 dDNNF buildDD(EvalContext &ctx)
const override {
1219 const std::string compiler =
1220 ctx.explicitly_named ? ctx.args : std::string();
1222 dDNNF dd = ctx.c.
compilation(ctx.gate, compiler, &used);
1225 ctx.actual_method = used.empty() ?
"compilation" :
"compilation:" + used;
1228 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1238 std::string name()
const override {
return "possible-worlds"; }
1240 bool inDefaultChain()
const override {
return true; }
1242 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1246 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1249 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1253 if(ctx.explicitly_named && !ctx.args.empty())
1256 double r = ctx.c.possibleWorlds(ctx.gate);
1257 ctx.actual_method =
"possible-worlds";
1266 std::string name()
const override {
return "monte-carlo"; }
1270 bool inDefaultChain()
const override {
return true; }
1271 bool isDeterministic()
const override {
return false; }
1273 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1274 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1276 * std::log(2.0 / tol.delta)
1277 / (tol.epsilon * tol.epsilon);
1279 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1280 unsigned long samples = monte_carlo_samples(parse_method_args(ctx.args));
1281 double r = ctx.c.monteCarlo(ctx.gate,
static_cast<unsigned>(samples));
1282 ctx.actual_method =
"monte-carlo";
1292 std::string name()
const override {
return "karp-luby"; }
1294 bool inDefaultChain()
const override {
return true; }
1295 bool isDeterministic()
const override {
return false; }
1298 std::vector<Feature> requiredFeatures()
const override {
1301 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1306 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1307 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1308 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1309 ? ctx.dnf_num_clauses_ : 1);
1310 return kCostKarpLuby *
static_cast<double>(ctx.circuit_size) * m
1311 * std::log(2.0 / tol.delta)
1312 / (tol.epsilon * tol.epsilon);
1314 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1315 std::vector<gate_t> clauses;
1316 std::vector<std::set<gate_t> > supports;
1317 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1318 provsql_warning(
"method 'karp-luby' applies only to a DNF-shaped circuit "
1319 "(a monotone OR-of-ANDs over input leaves); negation, "
1320 "comparison, aggregation, random-variable and "
1321 "multivalued-input gates are not supported");
1322 provsql_error(
"method 'karp-luby' requires a DNF-shaped provenance "
1325 double r = evaluate_karp_luby(ctx.c, clauses, supports,
1326 parse_method_args(ctx.args));
1327 ctx.actual_method =
"karp-luby";
1340 std::string name()
const override {
return "stopping-rule"; }
1342 bool inDefaultChain()
const override {
return true; }
1343 bool isDeterministic()
const override {
return false; }
1347 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1348 if(tol.epsilon <= 0.)
return std::numeric_limits<double>::infinity();
1350 * std::log(2.0 / tol.delta)
1351 / (tol.epsilon * tol.epsilon);
1353 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1369 std::string name()
const override {
return "sieve"; }
1371 bool inDefaultChain()
const override {
return true; }
1374 std::vector<Feature> requiredFeatures()
const override {
1377 bool applicable(
const EvalContext &ctx,
const Tolerance &)
const override {
1382 double estimatedCost(
const EvalContext &ctx,
const Tolerance &)
const override {
1384 return std::numeric_limits<double>::infinity();
1385 return kCostSieve *
static_cast<double>(ctx.circuit_size)
1388 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1392 if(ctx.explicitly_named && !ctx.args.empty())
1397 std::vector<gate_t> clauses;
1398 std::vector<std::set<gate_t> > supports;
1399 if(!ctx.c.dnfShape(ctx.gate, clauses, supports)) {
1400 provsql_warning(
"method 'sieve' applies only to a DNF-shaped circuit "
1401 "(a monotone OR-of-ANDs over input leaves); negation, "
1402 "comparison, aggregation, random-variable and "
1403 "multivalued-input gates are not supported");
1404 provsql_error(
"method 'sieve' requires a DNF-shaped provenance circuit");
1406 double r = ctx.c.sieve(clauses, supports);
1407 ctx.actual_method =
"sieve";
1421 std::string name()
const override {
return "d-tree"; }
1423 bool inDefaultChain()
const override {
return true; }
1431 std::vector<Feature> requiredFeatures()
const override {
1438 bool applicable(
const EvalContext &,
const Tolerance &)
const override {
1445 double estimatedCost(
const EvalContext &ctx,
const Tolerance &tol)
const override {
1446 const double S =
static_cast<double>(ctx.circuit_size);
1464 return std::numeric_limits<double>::infinity();
1465 const double m =
static_cast<double>(ctx.dnf_num_clauses_ > 0
1466 ? ctx.dnf_num_clauses_ : 1);
1469 double evaluate(EvalContext &ctx,
const Tolerance &tol)
const override {
1473 std::vector<gate_t> clause_roots;
1474 std::vector<std::set<gate_t> > supports;
1475 const bool is_dnf = ctx.c.dnfShape(ctx.gate, clause_roots, supports);
1476 ctx.actual_method =
"d-tree";
1482 double eps = tol.epsilon;
1484 MethodArgs a = parse_method_args(ctx.args);
1485 if(a.has(
"epsilon")) {
1486 double dummy_delta = 0.;
1487 parse_eps_delta(a,
"d-tree", eps, dummy_delta);
1496 max_width = 2. * eps;
1505 ctx.c.dnfBounds(supports, l0, u0);
1509 max_width = 2. * eps * l0;
1520 unsigned long budget_steps = 0;
1521 if(!ctx.explicitly_named && std::isfinite(ctx.cost_budget)) {
1524 budget_steps =
static_cast<unsigned long>(
1525 std::max(1.0, ctx.cost_budget / ms_per_step));
1529 budget_steps = (budget_steps == 0) ? cap : std::min(budget_steps, cap);
1532 unsigned long steps = 0;
1533 provsql::DTreeInterval iv = is_dnf
1537 provsql_notice(
"calibrate kind=dtree path=%s S=%zu N=%zu steps=%lu budget=%lu",
1538 is_dnf ?
"dnf" :
"circuit", ctx.circuit_size, ctx.n_inputs,
1539 steps, budget_steps);
1540 const double est = 0.5 * (iv.
lower + iv.
upper);
1545 const double half = 0.5 * (iv.upper - iv.lower);
1546 if(kind == ToleranceKind::Relative && est > 0.)
1547 emit_guarantee(
"relative", half / est, 0., 0, -1, nullptr);
1549 emit_guarantee(
"additive", half, 0., 0, -1, nullptr);
1558 std::string name()
const override {
return "weightmc"; }
1560 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1561 std::string opt = wmc_opt_from_args(parse_method_args(ctx.args),
"weightmc");
1562 emit_guarantee(
"relative", eps_from_wmc_opt(opt), -1., 0, -1,
"weightmc");
1563 double r = ctx.c.wmcCount(ctx.gate,
"weightmc", opt);
1564 ctx.actual_method =
"weightmc";
1572 std::string name()
const override {
return "wmc"; }
1574 double evaluate(EvalContext &ctx,
const Tolerance &)
const override {
1575 MethodArgs a = parse_method_args(ctx.args);
1576 std::string tool, tool_args;
1577 if(a.has(
"tool") || a.has(
"epsilon") || a.has(
"delta")) {
1578 reject_unknown_keys(a, {
"tool",
"epsilon",
"delta"},
"wmc");
1579 tool = a.get(
"tool");
1580 if(tool.empty() && !a.positional.empty())
1581 tool = a.positional[0];
1583 provsql_error(
"method 'wmc' requires a tool (tool=<name>)");
1584 if(a.has(
"epsilon") || a.has(
"delta")) {
1585 double eps = 0.8, delta = 0.5;
1586 parse_eps_delta(a,
"wmc", eps, delta);
1587 tool_args = a.get(
"delta") +
";" + a.get(
"epsilon");
1590 auto sep = ctx.args.find(
';');
1591 tool = (sep == std::string::npos) ? ctx.args : ctx.args.substr(0, sep);
1592 tool_args = (sep == std::string::npos) ? std::string()
1593 : ctx.args.substr(sep + 1);
1595 if(is_approx_wmc_tool(tool))
1596 emit_guarantee(
"relative", eps_from_wmc_opt(tool_args), -1., 0, -1,
1598 double r = ctx.c.wmcCount(ctx.gate, tool, tool_args);
1600 ctx.actual_method = tool.empty() ?
"wmc" :
"wmc:" + tool;
1616 +
"' does not construct a d-DNNF");
1624 std::string no_args;
1626 c, g,
nullptr,
false, no_args,
1666template<
class R,
class Run>
1667R runPortfolio(EvalContext &ctx,
const Tolerance &tol,
1668 std::vector<const ProbabilityMethod *> portfolio, Run run)
1678 std::set<Feature> acquired;
1679 std::string last_error;
1680 bool have_last_error =
false;
1685 const ProbabilityMethod *best =
nullptr;
1686 double best_cost = std::numeric_limits<double>::infinity();
1687 double second_cost = std::numeric_limits<double>::infinity();
1688 std::set<Feature> pending;
1689 for(
const ProbabilityMethod *m : portfolio) {
1691 for(
Feature f : m->requiredFeatures())
1692 if(acquired.find(f) == acquired.end()) { ready =
false; pending.insert(f); }
1693 if(!ready || !m->applicable(ctx, tol))
1695 double cost = m->estimatedCost(ctx, tol);
1696 if(cost < best_cost) { second_cost = best_cost; best_cost = cost; best = m; }
1697 else if(cost < second_cost) { second_cost = cost; }
1703 ctx.cost_budget = second_cost;
1706 bool have_pending =
false;
1708 double cheapest_fc = std::numeric_limits<double>::infinity();
1710 double fc = ctx.featureCost(f);
1711 if(fc < cheapest_fc) { cheapest_fc = fc; cheapest_f = f; have_pending =
true; }
1714 if(best !=
nullptr && (!have_pending || best_cost <= cheapest_fc)) {
1720 if(!best->handlesMultivalued())
1721 ctx.ensureMultivaluedRewritten();
1726 auto t0 = std::chrono::steady_clock::now();
1728 double ms = std::chrono::duration<double, std::milli>(
1729 std::chrono::steady_clock::now() - t0).count();
1730 provsql_notice(
"calibrate kind=method which=%s S=%zu N=%zu m=%zu w=%u "
1731 "D=%u cost=%g ms=%g", best->name().c_str(),
1732 ctx.circuit_size, ctx.n_inputs, ctx.dnf_num_clauses_,
1733 ctx.tw_proxy_, ctx.tw_max_degree_, best_cost, ms);
1737 }
catch(CircuitException &e) {
1740 last_error = e.
what();
1741 have_last_error =
true;
1742 portfolio.erase(std::remove(portfolio.begin(), portfolio.end(), best),
1745 }
else if(have_pending) {
1747 auto t0 = std::chrono::steady_clock::now();
1748 ctx.acquireFeature(cheapest_f);
1749 double ms = std::chrono::duration<double, std::milli>(
1750 std::chrono::steady_clock::now() - t0).count();
1751 provsql_notice(
"calibrate kind=feature which=%s S=%zu cost=%g ms=%g",
1754 ctx.circuit_size, cheapest_fc, ms);
1756 ctx.acquireFeature(cheapest_f);
1758 acquired.insert(cheapest_f);
1762 throw CircuitException(last_error);
1763 throw CircuitException(
"no applicable probability method in the portfolio");
1772 std::vector<const ProbabilityMethod *> portfolio;
1779 && (tol.
delta > 0. || m->isDeterministic()))
1780 portfolio.push_back(m.get());
1781 return runPortfolio<double>(ctx, tol, std::move(portfolio),
1792 std::vector<const ProbabilityMethod *> portfolio;
1795 portfolio.push_back(m.get());
1796 return runPortfolio<dDNNF>(ctx, tol, std::move(portfolio),
1860 const MethodArgs &a,
double &result,
1861 std::string &actual_method)
1863 SampleSpec s = parse_sample_spec(a,
"stopping-rule");
1865 provsql_error(
"the relative / stopping-rule estimator is adaptive: give "
1866 "epsilon=E[,delta=D][,max_samples=M], not a fixed sample "
1868 const unsigned long cap = s.has_max ? s.max_samples : 10000000UL;
1869 unsigned long used = 0;
1870 bool reached =
false;
1873 if(reached || used == 0) {
1874 emit_guarantee(
"relative", s.eps, s.delta, used, -1,
"stopping-rule");
1876 const double eps_add = sqrt(log(2.0 / 0.05) / (2.0 * used));
1877 provsql_warning(
"relative estimate: reached the %lu-sample cap before the "
1878 "(epsilon=%g, delta=%g) relative target; reporting the "
1879 "additive guarantee at the samples spent (the event is "
1880 "likely rarer than this budget resolves -- raise "
1881 "max_samples)", cap, s.eps, s.delta);
1882 emit_guarantee(
"additive", eps_add, 0.05, used, -1,
"stopping-rule");
1884 actual_method =
"stopping-rule";
1899 (
pg_uuid_t token,
const string &method,
const string &args,
bool *isnull)
1901 if(isnull !=
nullptr)
1926 const auto &w = gc.
getWires(gc_root);
1928 provsql_error(
"probability_evaluate: this is a conditioned distribution "
1929 "(a random_variable / agg_token X | C), not a Boolean "
1930 "event; query it with expected / variance / moment / "
1931 "support, which report the conditional distribution");
1933 provsql_error(
"probability_evaluate: malformed conditioned gate "
1934 "(expected 3 children [target, evidence, joint], got %zu)",
1938 bool ev_null =
false, jt_null =
false;
1939 double pe = DatumGetFloat8(
1941 if(ev_null || pe == 0.) {
1942 if(isnull !=
nullptr)
1946 double pj = DatumGetFloat8(
1949 if(isnull !=
nullptr)
1954 if(r > 1.) r = 1.;
else if(r < 0.) r = 0.;
1955 PG_RETURN_FLOAT8(r);
1971 const bool is_path =
1972 method.empty() || method ==
"default" || method ==
"exact"
1973 || method ==
"relative" || method ==
"additive";
1974 if(is_path || method ==
"mobius") {
1977 std::unordered_map<gate_t, gate_t> dummymap;
1987 const std::string lineage = mobiusLineageOf(token);
1989 provsql_error(
"method '%s': this Möbius-route token carries no literal "
1990 "lineage (it was built measure-only); only the default / "
1991 "'mobius' method applies", method.c_str());
2004 bool inv_free_cert =
false;
2006 std::string ex = gc.
getExtra(gc_root);
2010 inv_free_cert =
true;
2016 provsql_notice(
"inversion-free certificate read back from circuit "
2017 "root: %d atoms, %d classes, root_class=%d",
2083 auto count_reachable = [&](
gate_t r) {
2084 std::set<gate_t> seen;
2085 std::stack<gate_t> stk;
2087 while (!stk.empty()) {
2088 gate_t g = stk.top(); stk.pop();
2089 if (!seen.insert(g).second)
continue;
2094 size_t gates_before = count_reachable(gc_root);
2109 unsigned count_cmp_resolved = 0;
2110 unsigned minmax_cmp_resolved = 0;
2111 unsigned sum_cmp_resolved = 0;
2112 unsigned agg_marginal_resolved = 0;
2142 if (analytic_resolved + count_cmp_resolved + minmax_cmp_resolved
2143 + sum_cmp_resolved + agg_marginal_resolved + always_true_resolved > 0
2145 size_t gates_after = count_reachable(gc_root);
2146 std::vector<std::string> parts;
2147 if (analytic_resolved > 0)
2148 parts.push_back(std::to_string(analytic_resolved) +
" analytic");
2149 if (count_cmp_resolved > 0)
2150 parts.push_back(std::to_string(count_cmp_resolved) +
" Poisson-binomial");
2151 if (minmax_cmp_resolved > 0)
2152 parts.push_back(std::to_string(minmax_cmp_resolved) +
" min/max");
2153 if (sum_cmp_resolved > 0)
2154 parts.push_back(std::to_string(sum_cmp_resolved) +
" weighted-sum");
2155 if (agg_marginal_resolved > 0)
2156 parts.push_back(std::to_string(agg_marginal_resolved) +
" safe-join aggregate");
2157 if (always_true_resolved > 0)
2158 parts.push_back(std::to_string(always_true_resolved) +
" always-true");
2159 std::string breakdown;
2160 for (
size_t i = 0; i < parts.size(); ++i) {
2161 if (i > 0) breakdown +=
" + ";
2162 breakdown += parts[i];
2165 "gate_cmp expression was shortcut by probability-side pre-pass "
2166 "(%s): provenance circuit reduced from %zu to %zu gates",
2167 breakdown.c_str(), gates_before, gates_after);
2182 if (method !=
"monte-carlo" && method !=
"stopping-rule"
2183 && method !=
"relative" && method !=
"additive"
2187 "probability_evaluate: a comparison over random variables "
2188 "could not be resolved analytically; raise "
2189 "provsql.rv_mc_samples above 0 to enable the Monte Carlo "
2190 "fallback, or call probability_evaluate(..., 'monte-carlo', "
2194 "probability_evaluate: a comparison over random variables "
2195 "could not be resolved analytically and the hybrid evaluator "
2196 "left it unresolved; call probability_evaluate(..., "
2197 "'monte-carlo', <n>) directly for an MC estimate");
2205 string actual_method;
2209 void (*prev_sigint_handler)(int);
2222 if(method ==
"relative" || method ==
"additive") {
2235 SampleSpec s = parse_sample_spec(parse_method_args(args), method.c_str());
2258 const bool sampleable_agg =
2260 bool boolean_built =
false;
2262 std::unordered_map<gate_t, gate_t> gc_to_bc;
2265 && !(sampleable_agg && tol.
delta > 0.)) {
2268 boolean_built =
true;
2270 boolean_built =
false;
2276 inv_free_cert, args,
2282 }
else if(tol.
delta == 0.) {
2285 provsql_error(
"a deterministic (delta = 0) '%s' guarantee is not "
2286 "available for this circuit: it carries random-variable "
2287 "or HAVING-aggregate gates, for which only the (eps,delta) "
2288 "samplers apply -- use delta > 0", method.c_str());
2289 }
else if(method ==
"relative") {
2293 unsigned long samples = monte_carlo_samples(parse_method_args(args));
2295 actual_method =
"monte-carlo";
2297 }
else if(method ==
"stopping-rule") {
2300 }
else if(method ==
"monte-carlo"
2308 unsigned long samples = monte_carlo_samples(parse_method_args(args));
2316 std::unordered_map<gate_t, gate_t> gc_to_bc;
2319 const bool is_path = method.empty() || method ==
"default"
2320 || method ==
"exact";
2322 inv_free_cert, args,
2332 provsql_error(
"Wrong method '%s' for probability evaluation",
2349 CHECK_FOR_INTERRUPTS();
2356 if(!actual_method.empty()) {
2358 if(current.find(actual_method) == string::npos) {
2359 if(!current.empty()) current +=
",";
2360 current += actual_method;
2361 SetConfigOption(
"provsql.last_eval_method", current.c_str(),
2362 PGC_USERSET, PGC_S_SESSION);
2367 signal (SIGINT, prev_sigint_handler);
2375 PG_RETURN_FLOAT8(result);
2383 Datum token = PG_GETARG_DATUM(0);
2390 if(!PG_ARGISNULL(1)) {
2391 text *t = PG_GETARG_TEXT_P(1);
2392 method = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2395 if(!PG_ARGISNULL(2)) {
2396 text *t = PG_GETARG_TEXT_P(2);
2397 args = string(VARDATA(t),VARSIZE(t)-VARHDRSZ);
2400 bool isnull =
false;
2406 }
catch(
const std::exception &e) {
2431 pg_uuid_t token = *DatumGetUUIDP(PG_GETARG_DATUM(0));
2436 std::vector<gate_t> clause_roots;
2437 std::vector<std::set<gate_t> > supports;
2438 if(!c.
dnfShape(root, clause_roots, supports))
2439 provsql_error(
"probability_bounds applies only to a DNF-shaped circuit "
2440 "(a monotone OR-of-ANDs over input leaves); negation, "
2441 "comparison, aggregation, random-variable and "
2442 "multivalued-input gates are not supported");
2444 double lower, upper;
2448 if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2449 provsql_error(
"probability_bounds: expected composite return type");
2450 tupdesc = BlessTupleDesc(tupdesc);
2452 Datum values[2] = { Float8GetDatum(lower), Float8GetDatum(upper) };
2453 bool nulls[2] = {
false,
false };
2454 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
2455 }
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.
@ 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.
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.
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.
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
unsigned runAggMarginalEvaluator(GenericCircuit &gc)
Run the safe-join aggregate marginal-vector pre-pass over gc.
unsigned runCountCmpEvaluator(GenericCircuit &gc)
Run the Poisson-binomial pre-pass over gc.
static const size_t kPossibleWorldsSanityMax
Sanity bound on the reachable-input count for the auto-chosen 2^N possible-worlds enumeration: above ...
static const double kCostKarpLuby
static const double kCostDTreeExact
static const double kCostTwProxyFeature
unsigned runRangeCheck(GenericCircuit &gc)
Run the support-based pruning pass over gc.
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".
unsigned runHybridSimplifier(GenericCircuit &gc)
Run the peephole simplifier over gc.
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...
unsigned runMinMaxCmpEvaluator(GenericCircuit &gc)
Run the MIN / MAX closed-form pre-pass over gc.
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.
unsigned runSumCmpEvaluator(GenericCircuit &gc)
Run the weighted-sum DP pre-pass over gc.
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)
unsigned runHybridDecomposer(GenericCircuit &gc, unsigned samples)
Marginalise unresolved continuous-island gate_cmp gates into Bernoulli gate_input leaves.
unsigned runAnalyticEvaluator(GenericCircuit &gc)
Run the closed-form CDF resolution pass over gc.
unsigned runHavingAlwaysTrueRewriter(GenericCircuit &gc)
Probability-side pre-pass: rewrite HAVING-style gate_cmp gates that are provably TRUE on the agg's va...
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...
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,...
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_cmp_probability_evaluation
Run closed-form / analytic probability evaluators for gate_cmps inside probability_evaluate (currentl...
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-...
bool provsql_hybrid_evaluation
Run the hybrid-evaluator simplifier inside probability_evaluate; controlled by the provsql....
#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 ...
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.