21#include "utils/uuid.h"
31#include <unordered_map>
39 std::unordered_map<gate_t, gate_t> gc_to_bc;
44 auto u =
static_cast<gate_t>(i);
60 "evaluateBooleanProbability: subcircuit could not be evaluated "
61 "independently and provsql.rv_mc_samples = 0 disables the "
62 "Monte Carlo fallback");
72using RvSet = std::set<gate_t>;
92 explicit FootprintCache(
const GenericCircuit &gc) : gc_(gc) {}
94 const RvSet &of(
gate_t g) {
95 auto it = cache_.find(g);
96 if (it != cache_.end())
return it->second;
98 auto type = gc_.getGateType(g);
104 for (
gate_t c : gc_.getWires(g)) {
105 const auto &cs = of(c);
106 s.insert(cs.begin(), cs.end());
109 const auto &wires = gc_.getWires(g);
110 if (gc_.isCategoricalMixture(g)) {
115 for (std::size_t i = 1; i < wires.size(); ++i) {
116 const auto &fm = of(wires[i]);
117 s.insert(fm.begin(), fm.end());
119 }
else if (wires.size() == 3) {
128 const auto &fp = of(wires[0]);
129 s.insert(fp.begin(), fp.end());
130 const auto &fx = of(wires[1]);
131 s.insert(fx.begin(), fx.end());
132 const auto &fy = of(wires[2]);
133 s.insert(fy.begin(), fy.end());
143 const auto &wires = gc_.getWires(g);
144 if (!wires.empty()) {
145 const auto &fk = of(wires[0]);
146 s.insert(fk.begin(), fk.end());
160 for (
gate_t c : gc_.getWires(g)) {
161 const auto &cs = of(c);
162 s.insert(cs.begin(), cs.end());
173 return cache_.emplace(g, std::move(s)).first->second;
177 const GenericCircuit &gc_;
178 std::unordered_map<gate_t, RvSet> cache_;
181bool pairwise_disjoint(FootprintCache &fp,
const std::vector<gate_t> &children)
184 for (
gate_t c : children) {
185 const auto &fpc = fp.of(c);
187 if (!seen.insert(r).second)
return false;
193unsigned mc_samples_or_throw(
const std::string &what)
197 throw CircuitException(
198 what +
" could not be decomposed analytically and "
199 "provsql.rv_mc_samples = 0 disables the Monte Carlo fallback");
209 "%s: no closed form found; estimating by Monte Carlo over %d samples "
210 "(an approximation, not an exact moment) -- set provsql.rv_mc_samples = 0 "
211 "to require an exact result instead", what.c_str(), n);
212 return static_cast<unsigned>(n);
215double mc_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
216 const std::string &what)
219 if (samples.empty())
return 0.0;
225 std::size_t finite_count = 0;
226 for (
double x : samples) {
227 if (std::isnan(x))
continue;
228 total += std::pow(x,
static_cast<double>(k));
231 if (finite_count == 0)
return std::numeric_limits<double>::quiet_NaN();
232 return total /
static_cast<double>(finite_count);
235double mc_central_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
236 double mu,
const std::string &what)
239 if (samples.empty())
return 0.0;
241 std::size_t finite_count = 0;
242 for (
double x : samples) {
243 if (std::isnan(x))
continue;
244 const double d = x - mu;
245 total += std::pow(d,
static_cast<double>(k));
248 if (finite_count == 0)
return std::numeric_limits<double>::quiet_NaN();
249 return total /
static_cast<double>(finite_count);
256unsigned min_accepted_floor(
unsigned attempted)
258 unsigned floor = attempted / 1000;
259 return floor < 5 ? 5 : floor;
263 const std::string &what)
265 if (cs.accepted.empty()) {
271 throw CircuitException(
272 what +
": conditioning event is infeasible (0 of " +
273 std::to_string(cs.attempted) +
274 " Monte Carlo samples satisfied it)");
276 const unsigned floor = min_accepted_floor(cs.attempted);
277 if (cs.accepted.size() < floor) {
278 throw CircuitException(
279 what +
": conditional MC accepted only " +
280 std::to_string(cs.accepted.size()) +
" out of " +
281 std::to_string(cs.attempted) +
282 " samples (need >= " + std::to_string(floor) +
283 "); raise provsql.rv_mc_samples or tighten the event.");
287double mc_conditional_raw_moment(
const GenericCircuit &gc,
gate_t g,
288 unsigned k,
gate_t event_root,
289 const std::string &what)
292 gc, g, event_root, mc_samples_or_throw(what));
293 check_acceptance_or_throw(cs, what);
298 std::size_t finite_count = 0;
299 for (
double x : cs.accepted) {
300 if (std::isnan(x))
continue;
301 total += std::pow(x,
static_cast<double>(k));
304 if (finite_count == 0)
return std::numeric_limits<double>::quiet_NaN();
305 return total /
static_cast<double>(finite_count);
308double mc_conditional_central_moment(
const GenericCircuit &gc,
gate_t g,
309 unsigned k,
double mu,
311 const std::string &what)
314 gc, g, event_root, mc_samples_or_throw(what));
315 check_acceptance_or_throw(cs, what);
317 std::size_t finite_count = 0;
318 for (
double x : cs.accepted) {
319 if (std::isnan(x))
continue;
320 const double d = x - mu;
321 total += std::pow(d,
static_cast<double>(k));
324 if (finite_count == 0)
return std::numeric_limits<double>::quiet_NaN();
325 return total /
static_cast<double>(finite_count);
328double binomial(
unsigned n,
unsigned k)
330 if (k > n)
return 0.0;
331 if (k > n - k) k = n - k;
333 for (
unsigned i = 1; i <= k; ++i) {
334 r *=
static_cast<double>(n - i + 1);
335 r /=
static_cast<double>(i);
340double rec_expectation(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp);
341double rec_variance(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp);
342double rec_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
348 static const double INV_SQRT_2PI = 1.0 / std::sqrt(2.0 * M_PI);
349 return INV_SQRT_2PI * std::exp(-0.5 * z * z);
357 static const double SQRT2 = std::sqrt(2.0);
358 return 0.5 * (1.0 + std::erf(z / SQRT2));
376double truncated_normal_raw_moment(
double mu,
double sigma,
double a,
double b,
379 const double alpha = std::isfinite(a) ? (a - mu) / sigma
380 : -std::numeric_limits<double>::infinity();
381 const double beta = std::isfinite(b) ? (b - mu) / sigma
382 : +std::numeric_limits<double>::infinity();
383 const double Phi_alpha = std::isfinite(alpha) ? Phi(alpha) : 0.0;
384 const double Phi_beta = std::isfinite(beta) ? Phi(beta) : 1.0;
385 const double Z = Phi_beta - Phi_alpha;
386 if (Z < 1e-12)
return std::numeric_limits<double>::quiet_NaN();
388 const double phi_alpha = std::isfinite(alpha) ? phi(alpha) : 0.0;
389 const double phi_beta = std::isfinite(beta) ? phi(beta) : 0.0;
392 std::vector<double> M(k + 1, 0.0);
394 if (k >= 1) M[1] = (phi_alpha - phi_beta) / Z;
395 for (
unsigned m = 2; m <= k; ++m) {
398 double end_term = 0.0;
399 if (std::isfinite(alpha))
400 end_term += std::pow(alpha,
static_cast<double>(m - 1)) * phi_alpha;
401 if (std::isfinite(beta))
402 end_term -= std::pow(beta,
static_cast<double>(m - 1)) * phi_beta;
403 M[m] = (m - 1) * M[m - 2] + end_term / Z;
408 for (
unsigned i = 0; i <= k; ++i) {
409 total += binomial(k, i)
410 * std::pow(mu,
static_cast<double>(k - i))
411 * std::pow(sigma,
static_cast<double>(i))
423double truncated_uniform_raw_moment(
double p1,
double p2,
double a,
double b,
426 const double lo = std::max(p1, a);
427 const double hi = std::min(p2, b);
428 if (hi <= lo)
return std::numeric_limits<double>::quiet_NaN();
429 if (k == 0)
return 1.0;
430 return (std::pow(hi,
static_cast<double>(k + 1))
431 - std::pow(lo,
static_cast<double>(k + 1)))
432 / ((k + 1) * (hi - lo));
448double truncated_exponential_raw_moment(
double lambda,
double a,
double b,
451 const double aa = std::max(a, 0.0);
452 if (std::isfinite(b)) {
453 if (b <= aa)
return std::numeric_limits<double>::quiet_NaN();
455 const double e_a = std::exp(-lambda * aa);
456 const double e_b = std::exp(-lambda * b);
457 const double Z = e_a - e_b;
458 if (Z < 1e-12)
return std::numeric_limits<double>::quiet_NaN();
459 if (k == 0)
return 1.0;
467 std::vector<double> J(k + 1, 0.0);
469 for (
unsigned m = 1; m <= k; ++m) {
470 const double endpoint = std::pow(aa,
static_cast<double>(m)) * e_a
471 - std::pow(b,
static_cast<double>(m)) * e_b;
472 J[m] = endpoint / lambda + (m / lambda) * J[m - 1];
474 return lambda * J[k] / Z;
479 for (
unsigned i = 0; i <= k; ++i) {
480 total += binomial(k, i)
481 * std::pow(aa,
static_cast<double>(k - i))
482 * fact_i / std::pow(lambda,
static_cast<double>(i));
503try_truncated_closed_form(
const GenericCircuit &gc,
gate_t root,
504 gate_t event_root,
unsigned k,
bool central)
507 if (!m)
return std::nullopt;
509 const double lo = m->lo, hi = m->hi;
512 auto raw = [&](
unsigned q) -> std::optional<double> {
513 if (q == 0)
return 1.0;
514 double r = std::numeric_limits<double>::quiet_NaN();
517 r = truncated_normal_raw_moment(spec.p1, spec.p2, lo, hi, q);
520 r = truncated_uniform_raw_moment(spec.p1, spec.p2, lo, hi, q);
523 r = truncated_exponential_raw_moment(spec.p1, lo, hi, q);
530 if (std::isnan(r))
return std::nullopt;
534 if (!central)
return raw(k);
537 auto mu_opt = raw(1);
538 if (!mu_opt)
return std::nullopt;
539 const double mu = *mu_opt;
540 if (k == 1)
return 0.0;
542 for (
unsigned i = 0; i <= k; ++i) {
544 if (!m_i)
return std::nullopt;
545 total += binomial(k, i)
546 * std::pow(-mu,
static_cast<double>(k - i)) * (*m_i);
551double rec_expectation(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp)
560 throw CircuitException(
561 "Expectation: malformed gate_rv extra: " + gc.
getExtra(g));
570 for (
gate_t c : wires) s += rec_expectation(gc, c, fp);
574 if (wires.size() != 2)
575 throw CircuitException(
"gate_arith MINUS must be binary");
576 return rec_expectation(gc, wires[0], fp)
577 - rec_expectation(gc, wires[1], fp);
580 if (wires.size() != 1)
581 throw CircuitException(
"gate_arith NEG must be unary");
582 return -rec_expectation(gc, wires[0], fp);
585 if (pairwise_disjoint(fp, wires)) {
587 for (
gate_t c : wires) p *= rec_expectation(gc, c, fp);
590 return mc_raw_moment(gc, g, 1,
591 "Expectation of gate_arith TIMES with shared random variables");
594 if (wires.size() != 2)
595 throw CircuitException(
"gate_arith DIV must be binary");
598 return rec_expectation(gc, wires[0], fp) / divisor;
600 return mc_raw_moment(gc, g, 1,
601 "Expectation of gate_arith DIV with non-constant divisor");
604 throw CircuitException(
605 "Expectation: unknown gate_arith op tag: " +
606 std::to_string(
static_cast<unsigned>(op)));
614 for (std::size_t i = 1; i < wires.size(); ++i) {
624 if (wires.size() != 3)
625 throw CircuitException(
626 "Expectation: gate_mixture must have exactly three children");
627 const double pi = mixturePi(gc, wires[0]);
628 return pi * rec_expectation(gc, wires[1], fp)
629 + (1.0 - pi) * rec_expectation(gc, wires[2], fp);
632 return mc_raw_moment(gc, g, 1,
637double rec_variance(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp)
646 throw CircuitException(
647 "Variance: malformed gate_rv extra: " + gc.
getExtra(g));
653 auto mc_var = [&](
const std::string &what) {
654 const double mu = mc_raw_moment(gc, g, 1, what);
655 return mc_central_moment(gc, g, 2, mu, what);
659 if (pairwise_disjoint(fp, wires)) {
661 for (
gate_t c : wires) s += rec_variance(gc, c, fp);
665 "Variance of gate_arith PLUS with shared random variables");
668 if (wires.size() != 2)
669 throw CircuitException(
"gate_arith MINUS must be binary");
670 if (pairwise_disjoint(fp, wires)) {
671 return rec_variance(gc, wires[0], fp)
672 + rec_variance(gc, wires[1], fp);
675 "Variance of gate_arith MINUS with shared random variables");
678 if (wires.size() != 1)
679 throw CircuitException(
"gate_arith NEG must be unary");
680 return rec_variance(gc, wires[0], fp);
683 if (pairwise_disjoint(fp, wires)) {
686 double prod_e2 = 1.0;
687 double prod_e1 = 1.0;
689 const double mu_c = rec_expectation(gc, c, fp);
690 const double v_c = rec_variance(gc, c, fp);
691 prod_e2 *= (v_c + mu_c * mu_c);
694 return prod_e2 - prod_e1 * prod_e1;
697 "Variance of gate_arith TIMES with shared random variables");
700 if (wires.size() != 2)
701 throw CircuitException(
"gate_arith DIV must be binary");
704 return rec_variance(gc, wires[0], fp) / (divisor * divisor);
707 "Variance of gate_arith DIV with non-constant divisor");
710 throw CircuitException(
711 "Variance: unknown gate_arith op tag: " +
712 std::to_string(
static_cast<unsigned>(op)));
718 double e1 = 0.0, e2 = 0.0;
719 for (std::size_t i = 1; i < wires.size(); ++i) {
720 const double p = gc.
getProb(wires[i]);
729 if (wires.size() != 3)
730 throw CircuitException(
731 "Variance: gate_mixture must have exactly three children");
732 const double pi = mixturePi(gc, wires[0]);
733 const double ex = rec_expectation(gc, wires[1], fp);
734 const double ey = rec_expectation(gc, wires[2], fp);
735 const double vx = rec_variance(gc, wires[1], fp);
736 const double vy = rec_variance(gc, wires[2], fp);
737 const double em = pi * ex + (1.0 - pi) * ey;
738 return pi * (vx + ex * ex)
739 + (1.0 - pi) * (vy + ey * ey)
743 const std::string what =
745 const double mu = mc_raw_moment(gc, g, 1, what);
746 return mc_central_moment(gc, g, 2, mu, what);
751double rec_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
754 if (k == 0)
return 1.0;
755 if (k == 1)
return rec_expectation(gc, g, fp);
761 static_cast<double>(k));
765 throw CircuitException(
766 "Moment: malformed gate_rv extra: " + gc.
getExtra(g));
774 if (wires.size() != 1)
775 throw CircuitException(
"gate_arith NEG must be unary");
776 const double v = rec_raw_moment(gc, wires[0], k, fp);
777 return ((k % 2 == 0) ? 1.0 : -1.0) * v;
780 if (pairwise_disjoint(fp, wires)) {
784 std::vector<double> m_acc(k + 1, 0.0);
785 for (
unsigned i = 0; i <= k; ++i)
786 m_acc[i] = rec_raw_moment(gc, wires[0], i, fp);
787 for (
size_t w = 1; w < wires.size(); ++w) {
788 std::vector<double> next(k + 1, 0.0);
789 std::vector<double> moments_y(k + 1, 0.0);
790 for (
unsigned i = 0; i <= k; ++i)
791 moments_y[i] = rec_raw_moment(gc, wires[w], i, fp);
792 for (
unsigned kp = 0; kp <= k; ++kp) {
794 for (
unsigned i = 0; i <= kp; ++i) {
795 total += binomial(kp, i) * m_acc[i] * moments_y[kp - i];
799 m_acc = std::move(next);
803 return mc_raw_moment(gc, g, k,
804 "Raw moment of gate_arith PLUS with shared random variables");
807 if (wires.size() != 2)
808 throw CircuitException(
"gate_arith MINUS must be binary");
809 if (pairwise_disjoint(fp, wires)) {
811 for (
unsigned i = 0; i <= k; ++i) {
812 const double sign = ((k - i) % 2 == 0) ? 1.0 : -1.0;
813 total += binomial(k, i)
814 * rec_raw_moment(gc, wires[0], i, fp)
816 * rec_raw_moment(gc, wires[1], k - i, fp);
820 return mc_raw_moment(gc, g, k,
821 "Raw moment of gate_arith MINUS with shared random variables");
824 if (pairwise_disjoint(fp, wires)) {
827 for (
gate_t c : wires) p *= rec_raw_moment(gc, c, k, fp);
830 return mc_raw_moment(gc, g, k,
831 "Raw moment of gate_arith TIMES with shared random variables");
834 if (wires.size() != 2)
835 throw CircuitException(
"gate_arith DIV must be binary");
838 return rec_raw_moment(gc, wires[0], k, fp)
839 / std::pow(divisor,
static_cast<double>(k));
841 return mc_raw_moment(gc, g, k,
842 "Raw moment of gate_arith DIV with non-constant divisor");
845 throw CircuitException(
846 "Moment: unknown gate_arith op tag: " +
847 std::to_string(
static_cast<unsigned>(op)));
854 for (std::size_t i = 1; i < wires.size(); ++i) {
857 * std::pow(v,
static_cast<double>(k));
862 if (wires.size() != 3)
863 throw CircuitException(
864 "Moment: gate_mixture must have exactly three children");
865 const double pi = mixturePi(gc, wires[0]);
866 return pi * rec_raw_moment(gc, wires[1], k, fp)
867 + (1.0 - pi) * rec_raw_moment(gc, wires[2], k, fp);
870 return mc_raw_moment(gc, g, k,
883[[noreturn]]
void raise_infeasible_event(
const GenericCircuit &gc,
gate_t root)
885 (void)gc; (void)root;
886 throw CircuitException(
887 "conditioning event is infeasible (empty intersection with the "
888 "random variable's support)");
891double conditional_raw_moment(
const GenericCircuit &gc,
gate_t root,
892 unsigned k,
gate_t event_root)
894 if (k == 0)
return 1.0;
895 if (
auto cf = try_truncated_closed_form(gc, root, event_root, k,
false))
898 raise_infeasible_event(gc, root);
899 return mc_conditional_raw_moment(
900 gc, root, k, event_root,
901 "Conditional raw moment of gate type " +
905double conditional_central_moment(
const GenericCircuit &gc,
gate_t root,
906 unsigned k,
gate_t event_root)
908 if (k == 0)
return 1.0;
909 if (k == 1)
return 0.0;
910 if (
auto cf = try_truncated_closed_form(gc, root, event_root, k,
true))
913 raise_infeasible_event(gc, root);
915 const double mu = conditional_raw_moment(gc, root, 1, event_root);
916 return mc_conditional_central_moment(
917 gc, root, k, mu, event_root,
918 "Conditional central moment of gate type " +
925 std::optional<gate_t> event_root)
927 if (event_root.has_value())
928 return conditional_raw_moment(gc, root, 1, *event_root);
929 FootprintCache fp(gc);
930 return rec_expectation(gc, root, fp);
934 std::optional<gate_t> event_root)
936 if (event_root.has_value())
937 return conditional_raw_moment(gc, root, k, *event_root);
938 FootprintCache fp(gc);
939 return rec_raw_moment(gc, root, k, fp);
943 std::optional<gate_t> event_root)
945 if (event_root.has_value())
946 return conditional_central_moment(gc, root, k, *event_root);
947 if (k == 0)
return 1.0;
948 if (k == 1)
return 0.0;
949 FootprintCache fp(gc);
950 if (k == 2)
return rec_variance(gc, root, fp);
952 const double mu = rec_expectation(gc, root, fp);
954 for (
unsigned i = 0; i <= k; ++i) {
955 const double mu_pow = std::pow(-mu,
static_cast<double>(k - i));
956 total += binomial(k, i) * mu_pow * rec_raw_moment(gc, root, i, fp);
979 std::optional<gate_t> &event_opt)
981 std::vector<gate_t> evidences;
994 evidences.push_back(w[1]);
1000 std::set<gate_t> seen;
1001 std::vector<gate_t> stack{root};
1002 while (!stack.empty()) {
1005 if (!seen.insert(g).second)
continue;
1011 evidences.push_back(w[1]);
1013 stack.push_back(target);
1019 if (evidences.empty())
1021 if (event_opt.has_value())
1022 evidences.push_back(*event_opt);
1024 if (evidences.size() == 1)
1025 cond = evidences[0];
1029 for (
gate_t e : evidences)
1066 const int32 k_signed = PG_GETARG_INT32(1);
1067 const bool central = PG_GETARG_BOOL(2);
1071 provsql_error(
"rv_moment: k must be non-negative (got %d)", k_signed);
1072 const unsigned k =
static_cast<unsigned>(k_signed);
1074 gate_t root_gate, event_gate;
1078 std::optional<gate_t> event_opt;
1080 event_opt = event_gate;
1098 return Float8GetDatum(result);
1099 }
catch (
const std::exception &e) {
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.
@ MULIN
Multivalued-input gate (one of several options).
void propagateDNNFCertificate(const GenericCircuit &gc, const std::unordered_map< gate_t, gate_t > &gc_to_bc, BooleanCircuit &c)
Propagate the per-gate d-DNNF certificate from gc to c.
GenericCircuit getJointCircuit(pg_uuid_t root_token, pg_uuid_t event_token, gate_t &root_gate, gate_t &event_gate)
Build a GenericCircuit containing the closures of two roots, with shared subgraphs unified.
Build in-memory circuits from the mmap-backed persistent store.
Generic directed-acyclic-graph circuit template and gate identifier.
gate_t
Strongly-typed gate identifier.
Datum rv_moment(PG_FUNCTION_ARGS)
SQL: rv_moment(token uuid, k integer, central boolean, prov uuid DEFAULT gate_on...
Analytical expectation / variance / moment evaluator over RV circuits.
Monte Carlo sampling over a GenericCircuit, RV-aware.
Continuous random-variable helpers (distribution parsing, moments).
Support-based bound check for continuous-RV comparators.
Boolean circuit for provenance formula evaluation.
gate_t setGate(BooleanGate type) override
Allocate a new gate with type type and no UUID.
double monteCarlo(gate_t g, unsigned samples) const
Estimate the probability via Monte Carlo sampling.
void rewriteMultivaluedGates()
Rewrite all MULVAR/MULIN gate clusters into standard AND/OR/NOT circuits.
void setInfo(gate_t g, unsigned info)
Store an integer annotation on gate g.
double independentEvaluation(gate_t g) const
Compute the probability exactly when inputs are independent.
Exception type thrown by circuit operations on invalid input.
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.
void addWire(gate_t f, gate_t t)
Add a directed wire from gate f (parent) to gate t (child).
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
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.
S::value_type evaluate(gate_t g, std::unordered_map< gate_t, typename S::value_type > &provenance_mapping, S semiring) const
Evaluate the sub-circuit rooted at gate g over semiring semiring.
bool isCategoricalMixture(gate_t g) const
Test whether g is a categorical-form gate_mixture (the explicit provsql.categorical output).
std::string getExtra(gate_t g) const
Return the string extra for gate g.
gate_t setGate(gate_type type) override
Allocate a new gate with type type and no UUID.
double getProb(gate_t g) const
Return the probability for gate g.
const std::set< gate_t > & getInputs() const
Return the set of input (leaf) gates.
std::pair< unsigned, unsigned > getInfos(gate_t g) const
Return the integer annotation pair for gate g.
void liftConditionedToTarget(gate_t g, gate_t target)
Replace a gate_conditioned g by a transparent passthrough to its target child (a single-child gate_ar...
Provenance-as-Boolean-circuit semiring.
@ Normal
Normal (Gaussian): p1=μ, p2=σ
@ Exponential
Exponential: p1=λ, p2 unused.
@ Uniform
Uniform on [a,b]: p1=a, p2=b.
@ Erlang
Erlang: p1=k (positive integer), p2=λ.
double compute_raw_moment(const GenericCircuit &gc, gate_t root, unsigned k, std::optional< gate_t > event_root)
Compute the raw moment (or if event_root is set) for k >= 0.
gate_t lift_conditioning(GenericCircuit &gc, gate_t root, std::optional< gate_t > &event_opt)
Lift conditioning out of a scalar arithmetic expression.
double analytical_variance(const DistributionSpec &d)
Closed-form variance Var(X) for a basic distribution.
double parseDoubleStrict(const std::string &s)
Strictly parse s as a double.
bool eventIsProvablyInfeasible(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
True iff the conditioning event is provably infeasible for a bare gate_rv root.
double compute_central_moment(const GenericCircuit &gc, gate_t root, unsigned k, std::optional< gate_t > event_root)
Compute the central moment (or if event_root is set).
ConditionalScalarSamples monteCarloConditionalScalarSamples(const GenericCircuit &gc, gate_t root, gate_t event_root, unsigned samples)
Rejection-sample root conditioned on event_root.
double evaluateBooleanProbability(const GenericCircuit &gc, gate_t boolRoot)
Probability that the Boolean subcircuit rooted at boolRoot evaluates to true under the tuple-independ...
std::vector< double > monteCarloScalarSamples(const GenericCircuit &gc, gate_t root, unsigned samples)
Sample a scalar sub-circuit samples times and return the draws.
std::optional< DistributionSpec > parse_distribution_spec(const std::string &s)
Parse the on-disk text encoding of a gate_rv distribution.
double analytical_mean(const DistributionSpec &d)
Closed-form expectation E[X] for a basic distribution.
std::optional< TruncatedSingleRv > matchTruncatedSingleRv(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
Detect a closed-form, optionally-truncated single-RV shape.
double compute_expectation(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
Compute (or if event_root is set) over the scalar sub-circuit rooted at root.
double analytical_raw_moment(const DistributionSpec &d, unsigned k)
Closed-form raw moment for a basic distribution.
int provsql_verbose
Verbosity level; controlled by the provsql.verbose_level GUC.
int provsql_rv_mc_samples
Default sample count for analytical-evaluator MC fallbacks; 0 disables fallback (callers raise instea...
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
#define provsql_notice(fmt,...)
Emit a ProvSQL informational notice (execution continues).
const char * gate_type_name[]
Names of gate types.
Core types, constants, and utilities shared across ProvSQL.
provsql_arith_op
Arithmetic operator tags used by gate_arith.
@ PROVSQL_ARITH_DIV
binary, child0 / child1
@ PROVSQL_ARITH_PLUS
n-ary, sum of children
@ PROVSQL_ARITH_NEG
unary, -child0
@ PROVSQL_ARITH_MINUS
binary, child0 - child1
@ PROVSQL_ARITH_TIMES
n-ary, product of children
@ gate_rv
Continuous random-variable leaf (extra encodes distribution).
@ gate_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ gate_conditioned
Conditioning marker with two children [target, evidence]: measure-only, probability_evaluate returns ...
@ gate_mixture
Probabilistic mixture: three wires [p_token (gate_input Bernoulli), x_token, y_token]; samples x when...
@ gate_arith
n-ary arithmetic gate over scalar-valued children (info1 holds operator tag)
C++ utility functions for UUID manipulation.
Outcome of a conditional Monte Carlo sampling pass.
Parsed distribution spec (kind + up to two parameters).