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);
59 "evaluateBooleanProbability: subcircuit could not be evaluated "
60 "independently and provsql.rv_mc_samples = 0 disables the "
61 "Monte Carlo fallback");
71using RvSet = std::set<gate_t>;
91 explicit FootprintCache(
const GenericCircuit &gc) : gc_(gc) {}
93 const RvSet &of(
gate_t g) {
94 auto it = cache_.find(g);
95 if (it != cache_.end())
return it->second;
97 auto type = gc_.getGateType(g);
103 for (
gate_t c : gc_.getWires(g)) {
104 const auto &cs = of(c);
105 s.insert(cs.begin(), cs.end());
108 const auto &wires = gc_.getWires(g);
109 if (gc_.isCategoricalMixture(g)) {
114 for (std::size_t i = 1; i < wires.size(); ++i) {
115 const auto &fm = of(wires[i]);
116 s.insert(fm.begin(), fm.end());
118 }
else if (wires.size() == 3) {
127 const auto &fp = of(wires[0]);
128 s.insert(fp.begin(), fp.end());
129 const auto &fx = of(wires[1]);
130 s.insert(fx.begin(), fx.end());
131 const auto &fy = of(wires[2]);
132 s.insert(fy.begin(), fy.end());
142 const auto &wires = gc_.getWires(g);
143 if (!wires.empty()) {
144 const auto &fk = of(wires[0]);
145 s.insert(fk.begin(), fk.end());
158 for (
gate_t c : gc_.getWires(g)) {
159 const auto &cs = of(c);
160 s.insert(cs.begin(), cs.end());
171 return cache_.emplace(g, std::move(s)).first->second;
175 const GenericCircuit &gc_;
176 std::unordered_map<gate_t, RvSet> cache_;
179bool pairwise_disjoint(FootprintCache &fp,
const std::vector<gate_t> &children)
182 for (
gate_t c : children) {
183 const auto &fpc = fp.of(c);
185 if (!seen.insert(r).second)
return false;
191unsigned mc_samples_or_throw(
const std::string &what)
195 throw CircuitException(
196 what +
" could not be decomposed analytically and "
197 "provsql.rv_mc_samples = 0 disables the Monte Carlo fallback");
199 return static_cast<unsigned>(n);
202double mc_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
203 const std::string &what)
206 if (samples.empty())
return 0.0;
208 for (
double x : samples) total += std::pow(x,
static_cast<double>(k));
209 return total /
static_cast<double>(samples.size());
212double mc_central_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
213 double mu,
const std::string &what)
216 if (samples.empty())
return 0.0;
218 for (
double x : samples) {
219 const double d = x - mu;
220 total += std::pow(d,
static_cast<double>(k));
222 return total /
static_cast<double>(samples.size());
229unsigned min_accepted_floor(
unsigned attempted)
231 unsigned floor = attempted / 1000;
232 return floor < 5 ? 5 : floor;
236 const std::string &what)
238 if (cs.accepted.empty()) {
244 throw CircuitException(
245 what +
": conditioning event is infeasible (0 of " +
246 std::to_string(cs.attempted) +
247 " Monte Carlo samples satisfied it)");
249 const unsigned floor = min_accepted_floor(cs.attempted);
250 if (cs.accepted.size() < floor) {
251 throw CircuitException(
252 what +
": conditional MC accepted only " +
253 std::to_string(cs.accepted.size()) +
" out of " +
254 std::to_string(cs.attempted) +
255 " samples (need >= " + std::to_string(floor) +
256 "); raise provsql.rv_mc_samples or tighten the event.");
260double mc_conditional_raw_moment(
const GenericCircuit &gc,
gate_t g,
261 unsigned k,
gate_t event_root,
262 const std::string &what)
265 gc, g, event_root, mc_samples_or_throw(what));
266 check_acceptance_or_throw(cs, what);
268 for (
double x : cs.accepted) total += std::pow(x,
static_cast<double>(k));
269 return total /
static_cast<double>(cs.accepted.size());
272double mc_conditional_central_moment(
const GenericCircuit &gc,
gate_t g,
273 unsigned k,
double mu,
275 const std::string &what)
278 gc, g, event_root, mc_samples_or_throw(what));
279 check_acceptance_or_throw(cs, what);
281 for (
double x : cs.accepted) {
282 const double d = x - mu;
283 total += std::pow(d,
static_cast<double>(k));
285 return total /
static_cast<double>(cs.accepted.size());
288double binomial(
unsigned n,
unsigned k)
290 if (k > n)
return 0.0;
291 if (k > n - k) k = n - k;
293 for (
unsigned i = 1; i <= k; ++i) {
294 r *=
static_cast<double>(n - i + 1);
295 r /=
static_cast<double>(i);
300double rec_expectation(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp);
301double rec_variance(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp);
302double rec_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
308 static const double INV_SQRT_2PI = 1.0 / std::sqrt(2.0 * M_PI);
309 return INV_SQRT_2PI * std::exp(-0.5 * z * z);
317 static const double SQRT2 = std::sqrt(2.0);
318 return 0.5 * (1.0 + std::erf(z / SQRT2));
336double truncated_normal_raw_moment(
double mu,
double sigma,
double a,
double b,
339 const double alpha = std::isfinite(a) ? (a - mu) / sigma
340 : -std::numeric_limits<double>::infinity();
341 const double beta = std::isfinite(b) ? (b - mu) / sigma
342 : +std::numeric_limits<double>::infinity();
343 const double Phi_alpha = std::isfinite(alpha) ? Phi(alpha) : 0.0;
344 const double Phi_beta = std::isfinite(beta) ? Phi(beta) : 1.0;
345 const double Z = Phi_beta - Phi_alpha;
346 if (Z < 1e-12)
return std::numeric_limits<double>::quiet_NaN();
348 const double phi_alpha = std::isfinite(alpha) ? phi(alpha) : 0.0;
349 const double phi_beta = std::isfinite(beta) ? phi(beta) : 0.0;
352 std::vector<double> M(k + 1, 0.0);
354 if (k >= 1) M[1] = (phi_alpha - phi_beta) / Z;
355 for (
unsigned m = 2; m <= k; ++m) {
358 double end_term = 0.0;
359 if (std::isfinite(alpha))
360 end_term += std::pow(alpha,
static_cast<double>(m - 1)) * phi_alpha;
361 if (std::isfinite(beta))
362 end_term -= std::pow(beta,
static_cast<double>(m - 1)) * phi_beta;
363 M[m] = (m - 1) * M[m - 2] + end_term / Z;
368 for (
unsigned i = 0; i <= k; ++i) {
369 total += binomial(k, i)
370 * std::pow(mu,
static_cast<double>(k - i))
371 * std::pow(sigma,
static_cast<double>(i))
383double truncated_uniform_raw_moment(
double p1,
double p2,
double a,
double b,
386 const double lo = std::max(p1, a);
387 const double hi = std::min(p2, b);
388 if (hi <= lo)
return std::numeric_limits<double>::quiet_NaN();
389 if (k == 0)
return 1.0;
390 return (std::pow(hi,
static_cast<double>(k + 1))
391 - std::pow(lo,
static_cast<double>(k + 1)))
392 / ((k + 1) * (hi - lo));
408double truncated_exponential_raw_moment(
double lambda,
double a,
double b,
411 const double aa = std::max(a, 0.0);
412 if (std::isfinite(b)) {
413 if (b <= aa)
return std::numeric_limits<double>::quiet_NaN();
415 const double e_a = std::exp(-lambda * aa);
416 const double e_b = std::exp(-lambda * b);
417 const double Z = e_a - e_b;
418 if (Z < 1e-12)
return std::numeric_limits<double>::quiet_NaN();
419 if (k == 0)
return 1.0;
427 std::vector<double> J(k + 1, 0.0);
429 for (
unsigned m = 1; m <= k; ++m) {
430 const double endpoint = std::pow(aa,
static_cast<double>(m)) * e_a
431 - std::pow(b,
static_cast<double>(m)) * e_b;
432 J[m] = endpoint / lambda + (m / lambda) * J[m - 1];
434 return lambda * J[k] / Z;
439 for (
unsigned i = 0; i <= k; ++i) {
440 total += binomial(k, i)
441 * std::pow(aa,
static_cast<double>(k - i))
442 * fact_i / std::pow(lambda,
static_cast<double>(i));
463try_truncated_closed_form(
const GenericCircuit &gc,
gate_t root,
464 gate_t event_root,
unsigned k,
bool central)
467 if (!m)
return std::nullopt;
469 const double lo = m->lo, hi = m->hi;
472 auto raw = [&](
unsigned q) -> std::optional<double> {
473 if (q == 0)
return 1.0;
474 double r = std::numeric_limits<double>::quiet_NaN();
477 r = truncated_normal_raw_moment(spec.p1, spec.p2, lo, hi, q);
480 r = truncated_uniform_raw_moment(spec.p1, spec.p2, lo, hi, q);
483 r = truncated_exponential_raw_moment(spec.p1, lo, hi, q);
490 if (std::isnan(r))
return std::nullopt;
494 if (!central)
return raw(k);
497 auto mu_opt = raw(1);
498 if (!mu_opt)
return std::nullopt;
499 const double mu = *mu_opt;
500 if (k == 1)
return 0.0;
502 for (
unsigned i = 0; i <= k; ++i) {
504 if (!m_i)
return std::nullopt;
505 total += binomial(k, i)
506 * std::pow(-mu,
static_cast<double>(k - i)) * (*m_i);
511double rec_expectation(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp)
520 throw CircuitException(
521 "Expectation: malformed gate_rv extra: " + gc.
getExtra(g));
530 for (
gate_t c : wires) s += rec_expectation(gc, c, fp);
534 if (wires.size() != 2)
535 throw CircuitException(
"gate_arith MINUS must be binary");
536 return rec_expectation(gc, wires[0], fp)
537 - rec_expectation(gc, wires[1], fp);
540 if (wires.size() != 1)
541 throw CircuitException(
"gate_arith NEG must be unary");
542 return -rec_expectation(gc, wires[0], fp);
545 if (pairwise_disjoint(fp, wires)) {
547 for (
gate_t c : wires) p *= rec_expectation(gc, c, fp);
550 return mc_raw_moment(gc, g, 1,
551 "Expectation of gate_arith TIMES with shared random variables");
554 if (wires.size() != 2)
555 throw CircuitException(
"gate_arith DIV must be binary");
558 return rec_expectation(gc, wires[0], fp) / divisor;
560 return mc_raw_moment(gc, g, 1,
561 "Expectation of gate_arith DIV with non-constant divisor");
564 throw CircuitException(
565 "Expectation: unknown gate_arith op tag: " +
566 std::to_string(
static_cast<unsigned>(op)));
574 for (std::size_t i = 1; i < wires.size(); ++i) {
584 if (wires.size() != 3)
585 throw CircuitException(
586 "Expectation: gate_mixture must have exactly three children");
587 const double pi = mixturePi(gc, wires[0]);
588 return pi * rec_expectation(gc, wires[1], fp)
589 + (1.0 - pi) * rec_expectation(gc, wires[2], fp);
592 return mc_raw_moment(gc, g, 1,
597double rec_variance(
const GenericCircuit &gc,
gate_t g, FootprintCache &fp)
606 throw CircuitException(
607 "Variance: malformed gate_rv extra: " + gc.
getExtra(g));
613 auto mc_var = [&](
const std::string &what) {
614 const double mu = mc_raw_moment(gc, g, 1, what);
615 return mc_central_moment(gc, g, 2, mu, what);
619 if (pairwise_disjoint(fp, wires)) {
621 for (
gate_t c : wires) s += rec_variance(gc, c, fp);
625 "Variance of gate_arith PLUS with shared random variables");
628 if (wires.size() != 2)
629 throw CircuitException(
"gate_arith MINUS must be binary");
630 if (pairwise_disjoint(fp, wires)) {
631 return rec_variance(gc, wires[0], fp)
632 + rec_variance(gc, wires[1], fp);
635 "Variance of gate_arith MINUS with shared random variables");
638 if (wires.size() != 1)
639 throw CircuitException(
"gate_arith NEG must be unary");
640 return rec_variance(gc, wires[0], fp);
643 if (pairwise_disjoint(fp, wires)) {
646 double prod_e2 = 1.0;
647 double prod_e1 = 1.0;
649 const double mu_c = rec_expectation(gc, c, fp);
650 const double v_c = rec_variance(gc, c, fp);
651 prod_e2 *= (v_c + mu_c * mu_c);
654 return prod_e2 - prod_e1 * prod_e1;
657 "Variance of gate_arith TIMES with shared random variables");
660 if (wires.size() != 2)
661 throw CircuitException(
"gate_arith DIV must be binary");
664 return rec_variance(gc, wires[0], fp) / (divisor * divisor);
667 "Variance of gate_arith DIV with non-constant divisor");
670 throw CircuitException(
671 "Variance: unknown gate_arith op tag: " +
672 std::to_string(
static_cast<unsigned>(op)));
678 double e1 = 0.0, e2 = 0.0;
679 for (std::size_t i = 1; i < wires.size(); ++i) {
680 const double p = gc.
getProb(wires[i]);
689 if (wires.size() != 3)
690 throw CircuitException(
691 "Variance: gate_mixture must have exactly three children");
692 const double pi = mixturePi(gc, wires[0]);
693 const double ex = rec_expectation(gc, wires[1], fp);
694 const double ey = rec_expectation(gc, wires[2], fp);
695 const double vx = rec_variance(gc, wires[1], fp);
696 const double vy = rec_variance(gc, wires[2], fp);
697 const double em = pi * ex + (1.0 - pi) * ey;
698 return pi * (vx + ex * ex)
699 + (1.0 - pi) * (vy + ey * ey)
703 const std::string what =
705 const double mu = mc_raw_moment(gc, g, 1, what);
706 return mc_central_moment(gc, g, 2, mu, what);
711double rec_raw_moment(
const GenericCircuit &gc,
gate_t g,
unsigned k,
714 if (k == 0)
return 1.0;
715 if (k == 1)
return rec_expectation(gc, g, fp);
721 static_cast<double>(k));
725 throw CircuitException(
726 "Moment: malformed gate_rv extra: " + gc.
getExtra(g));
734 if (wires.size() != 1)
735 throw CircuitException(
"gate_arith NEG must be unary");
736 const double v = rec_raw_moment(gc, wires[0], k, fp);
737 return ((k % 2 == 0) ? 1.0 : -1.0) * v;
740 if (pairwise_disjoint(fp, wires)) {
744 std::vector<double> m_acc(k + 1, 0.0);
745 for (
unsigned i = 0; i <= k; ++i)
746 m_acc[i] = rec_raw_moment(gc, wires[0], i, fp);
747 for (
size_t w = 1; w < wires.size(); ++w) {
748 std::vector<double> next(k + 1, 0.0);
749 std::vector<double> moments_y(k + 1, 0.0);
750 for (
unsigned i = 0; i <= k; ++i)
751 moments_y[i] = rec_raw_moment(gc, wires[w], i, fp);
752 for (
unsigned kp = 0; kp <= k; ++kp) {
754 for (
unsigned i = 0; i <= kp; ++i) {
755 total += binomial(kp, i) * m_acc[i] * moments_y[kp - i];
759 m_acc = std::move(next);
763 return mc_raw_moment(gc, g, k,
764 "Raw moment of gate_arith PLUS with shared random variables");
767 if (wires.size() != 2)
768 throw CircuitException(
"gate_arith MINUS must be binary");
769 if (pairwise_disjoint(fp, wires)) {
771 for (
unsigned i = 0; i <= k; ++i) {
772 const double sign = ((k - i) % 2 == 0) ? 1.0 : -1.0;
773 total += binomial(k, i)
774 * rec_raw_moment(gc, wires[0], i, fp)
776 * rec_raw_moment(gc, wires[1], k - i, fp);
780 return mc_raw_moment(gc, g, k,
781 "Raw moment of gate_arith MINUS with shared random variables");
784 if (pairwise_disjoint(fp, wires)) {
787 for (
gate_t c : wires) p *= rec_raw_moment(gc, c, k, fp);
790 return mc_raw_moment(gc, g, k,
791 "Raw moment of gate_arith TIMES with shared random variables");
794 if (wires.size() != 2)
795 throw CircuitException(
"gate_arith DIV must be binary");
798 return rec_raw_moment(gc, wires[0], k, fp)
799 / std::pow(divisor,
static_cast<double>(k));
801 return mc_raw_moment(gc, g, k,
802 "Raw moment of gate_arith DIV with non-constant divisor");
805 throw CircuitException(
806 "Moment: unknown gate_arith op tag: " +
807 std::to_string(
static_cast<unsigned>(op)));
814 for (std::size_t i = 1; i < wires.size(); ++i) {
817 * std::pow(v,
static_cast<double>(k));
822 if (wires.size() != 3)
823 throw CircuitException(
824 "Moment: gate_mixture must have exactly three children");
825 const double pi = mixturePi(gc, wires[0]);
826 return pi * rec_raw_moment(gc, wires[1], k, fp)
827 + (1.0 - pi) * rec_raw_moment(gc, wires[2], k, fp);
830 return mc_raw_moment(gc, g, k,
843[[noreturn]]
void raise_infeasible_event(
const GenericCircuit &gc,
gate_t root)
845 (void)gc; (void)root;
846 throw CircuitException(
847 "conditioning event is infeasible (empty intersection with the "
848 "random variable's support)");
851double conditional_raw_moment(
const GenericCircuit &gc,
gate_t root,
852 unsigned k,
gate_t event_root)
854 if (k == 0)
return 1.0;
855 if (
auto cf = try_truncated_closed_form(gc, root, event_root, k,
false))
858 raise_infeasible_event(gc, root);
859 return mc_conditional_raw_moment(
860 gc, root, k, event_root,
861 "Conditional raw moment of gate type " +
865double conditional_central_moment(
const GenericCircuit &gc,
gate_t root,
866 unsigned k,
gate_t event_root)
868 if (k == 0)
return 1.0;
869 if (k == 1)
return 0.0;
870 if (
auto cf = try_truncated_closed_form(gc, root, event_root, k,
true))
873 raise_infeasible_event(gc, root);
875 const double mu = conditional_raw_moment(gc, root, 1, event_root);
876 return mc_conditional_central_moment(
877 gc, root, k, mu, event_root,
878 "Conditional central moment of gate type " +
885 std::optional<gate_t> event_root)
887 if (event_root.has_value())
888 return conditional_raw_moment(gc, root, 1, *event_root);
889 FootprintCache fp(gc);
890 return rec_expectation(gc, root, fp);
894 std::optional<gate_t> event_root)
896 if (event_root.has_value())
897 return conditional_central_moment(gc, root, 2, *event_root);
898 FootprintCache fp(gc);
899 return rec_variance(gc, root, fp);
903 std::optional<gate_t> event_root)
905 if (event_root.has_value())
906 return conditional_raw_moment(gc, root, k, *event_root);
907 FootprintCache fp(gc);
908 return rec_raw_moment(gc, root, k, fp);
912 std::optional<gate_t> event_root)
914 if (event_root.has_value())
915 return conditional_central_moment(gc, root, k, *event_root);
916 if (k == 0)
return 1.0;
917 if (k == 1)
return 0.0;
918 FootprintCache fp(gc);
919 if (k == 2)
return rec_variance(gc, root, fp);
921 const double mu = rec_expectation(gc, root, fp);
923 for (
unsigned i = 0; i <= k; ++i) {
924 const double mu_pow = std::pow(-mu,
static_cast<double>(k - i));
925 total += binomial(k, i) * mu_pow * rec_raw_moment(gc, root, i, fp);
960 const int32 k_signed = PG_GETARG_INT32(1);
961 const bool central = PG_GETARG_BOOL(2);
965 provsql_error(
"rv_moment: k must be non-negative (got %d)", k_signed);
966 const unsigned k =
static_cast<unsigned>(k_signed);
968 gate_t root_gate, event_gate;
972 std::optional<gate_t> event_opt;
974 event_opt = event_gate;
983 return Float8GetDatum(result);
984 }
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).
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.
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.
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.
double analytical_variance(const DistributionSpec &d)
Closed-form variance Var(X) for a basic distribution.
double compute_variance(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 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_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.
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_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).