31#include "utils/uuid.h"
53void collectStochasticLeaves(
const GenericCircuit &gc,
gate_t g,
54 std::set<gate_t> &out, std::set<gate_t> &seen)
56 if (!seen.insert(g).second)
return;
61 collectStochasticLeaves(gc, c, out, seen);
64std::set<gate_t> stochasticLeaves(
const GenericCircuit &gc,
gate_t g)
66 std::set<gate_t> out, seen;
67 collectStochasticLeaves(gc, g, out, seen);
71bool disjointFrom(
const std::set<gate_t> &a,
const std::set<gate_t> &b)
74 if (b.count(l))
return false;
81struct McDisabledGuard {
102unsigned mc_samples_or_throw(
const std::string &what)
106 throw CircuitException(
107 what +
" could not be decomposed analytically and "
108 "provsql.rv_mc_samples = 0 disables the Monte Carlo fallback");
112 "%s: no closed form found; estimating by Monte Carlo over %d samples "
113 "(an approximation, not an exact moment) -- set provsql.rv_mc_samples = 0 "
114 "to require an exact result instead", what.c_str(), n);
115 return static_cast<unsigned>(n);
121void check_acceptance_or_throw(std::size_t accepted,
unsigned attempted,
122 const std::string &what)
125 throw CircuitException(
126 what +
": conditioning event is infeasible (0 of " +
127 std::to_string(attempted) +
" Monte Carlo samples satisfied it)");
129 unsigned floor = attempted / 1000;
130 if (floor < 5) floor = 5;
131 if (accepted < floor) {
132 throw CircuitException(
133 what +
": conditional MC accepted only " + std::to_string(accepted) +
134 " out of " + std::to_string(attempted) +
135 " samples (need >= " + std::to_string(floor) +
136 "); raise provsql.rv_mc_samples or tighten the event.");
151CovStats mcCovStats(
const GenericCircuit &gc,
gate_t x,
gate_t y,
152 std::optional<gate_t> event,
const std::string &what)
154 const unsigned n = mc_samples_or_throw(what);
156 std::vector<double> xs, ys;
159 check_acceptance_or_throw(cs.xs.size(), cs.attempted, what);
160 xs = std::move(cs.xs);
161 ys = std::move(cs.ys);
166 double sum_x = 0.0, sum_y = 0.0;
168 for (std::size_t i = 0; i < xs.size(); ++i) {
169 if (std::isnan(xs[i]) || std::isnan(ys[i]))
continue;
175 const double nan = std::numeric_limits<double>::quiet_NaN();
176 return {nan, nan, nan};
178 const double mean_x = sum_x /
static_cast<double>(m);
179 const double mean_y = sum_y /
static_cast<double>(m);
181 double cov = 0.0, var_x = 0.0, var_y = 0.0;
182 for (std::size_t i = 0; i < xs.size(); ++i) {
183 if (std::isnan(xs[i]) || std::isnan(ys[i]))
continue;
184 const double dx = xs[i] - mean_x;
185 const double dy = ys[i] - mean_y;
190 const double dm =
static_cast<double>(m);
191 return {cov / dm, var_x / dm, var_y / dm};
198std::optional<CovStats> tryAnalyticCovStats(GenericCircuit &gc,
gate_t x,
200 std::optional<gate_t> event,
203 McDisabledGuard guard;
205 CovStats s{0.0, 0.0, 0.0};
208 const gate_t prod = makeProductGate(gc, x, y);
210 s.cov = exy - ex * ey;
216 }
catch (
const CircuitException &) {
234 std::optional<gate_t> event)
249 auto lx = stochasticLeaves(gc, x);
250 auto ly = stochasticLeaves(gc, y);
252 if (event) le = stochasticLeaves(gc, *event);
253 const bool x_indep = disjointFrom(lx, ly) && disjointFrom(lx, le);
254 const bool y_indep = disjointFrom(ly, lx) && disjointFrom(ly, le);
255 if (x_indep || y_indep)
259 if (
auto s = tryAnalyticCovStats(gc, x, y, event,
false))
262 return mcCovStats(gc, x, y, event,
"covariance").cov;
277 std::optional<gate_t> event)
284 if (std::isnan(var) || var <= 0.0)
return std::nullopt;
289 auto lx = stochasticLeaves(gc, x);
290 auto ly = stochasticLeaves(gc, y);
292 if (event) le = stochasticLeaves(gc, *event);
293 const bool x_indep = disjointFrom(lx, ly) && disjointFrom(lx, le);
294 const bool y_indep = disjointFrom(ly, lx) && disjointFrom(ly, le);
295 if (x_indep || y_indep) {
298 if (std::isnan(var_x) || std::isnan(var_y) ||
299 var_x <= 0.0 || var_y <= 0.0)
306 if (
auto exact = tryAnalyticCovStats(gc, x, y, event,
true))
309 s = mcCovStats(gc, x, y, event,
"correlation");
311 if (std::isnan(s.var_x) || std::isnan(s.var_y) ||
312 s.var_x <= 0.0 || s.var_y <= 0.0)
314 return s.cov / std::sqrt(s.var_x * s.var_y);
333 std::vector<gate_t> gates;
337 std::optional<gate_t> event_opt;
339 event_opt = gates[2];
341 return Float8GetDatum(
343 }
catch (
const std::exception &e) {
364 std::vector<gate_t> gates;
367 std::optional<gate_t> event_opt;
369 event_opt = gates[2];
375 return Float8GetDatum(*rho);
376 }
catch (
const std::exception &e) {
GenericCircuit getJointCircuit(const std::vector< pg_uuid_t > &tokens, std::vector< gate_t > &gates)
Multi-root variant of getJointCircuit.
Build in-memory circuits from the mmap-backed persistent store.
gate_t
Strongly-typed gate identifier.
Analytical expectation / variance / moment evaluator over RV circuits.
Monte Carlo sampling over a GenericCircuit, RV-aware.
Datum rv_covariance(PG_FUNCTION_ARGS)
SQL: rv_covariance(x uuid, y uuid, prov uuid) -> float8.
Datum rv_correlation(PG_FUNCTION_ARGS)
SQL: rv_correlation(x uuid, y uuid, prov uuid) -> float8.
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.
In-memory provenance circuit with semiring-generic evaluation.
void setInfos(gate_t g, unsigned info1, unsigned info2)
Set the integer annotation pair for gate g.
gate_t setGate(gate_type type) override
Allocate a new gate with type type and no UUID.
gate_t lift_conditioning(GenericCircuit &gc, gate_t root, std::optional< gate_t > &event_opt)
Lift conditioning out of a scalar arithmetic expression.
std::pair< std::vector< double >, std::vector< double > > monteCarloScalarPairSamples(const GenericCircuit &gc, gate_t root_a, gate_t root_b, unsigned samples)
Coupled per-iteration draws of two scalar roots.
double computeCovariance(GenericCircuit &gc, gate_t x, gate_t y, std::optional< gate_t > event)
(or ) over a joint circuit.
std::optional< double > computeCorrelation(GenericCircuit &gc, gate_t x, gate_t y, std::optional< gate_t > event)
Pearson (conditioned on event when set).
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).
ConditionalScalarPairSamples monteCarloConditionalScalarPairSamples(const GenericCircuit &gc, gate_t root_a, gate_t root_b, gate_t event_root, unsigned samples)
Rejection-sample the PAIR (root_a, root_b) conditioned on event_root.
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.
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).
Core types, constants, and utilities shared across ProvSQL.
@ PROVSQL_ARITH_TIMES
n-ary, product of children
@ gate_rv
Continuous random-variable leaf (extra encodes distribution).
@ gate_arith
n-ary arithmetic gate over scalar-valued children (info1 holds operator tag)
C++ utility functions for UUID manipulation.