41#include "utils/jsonb.h"
42#include "utils/fmgrprotos.h"
43#include "utils/uuid.h"
85std::pair<double, double>
87 double trunc_lo,
double trunc_hi)
89 double lo = trunc_lo, hi = trunc_hi;
92 const double mu = spec.
p1, sigma = spec.
p2;
93 if (!std::isfinite(lo)) lo = mu - 4.0 * sigma;
94 if (!std::isfinite(hi)) hi = mu + 4.0 * sigma;
98 const double a = spec.
p1, b = spec.
p2;
99 const double pad = 0.15 * (b - a);
100 if (!std::isfinite(lo)) lo = a - pad;
101 else lo = std::max(lo, a - pad);
102 if (!std::isfinite(hi)) hi = b + pad;
103 else hi = std::min(hi, b + pad);
107 const double lambda = spec.
p1;
108 if (!std::isfinite(lo)) lo = 0.0;
109 if (!std::isfinite(hi)) hi = 6.0 / lambda;
113 const double k = spec.
p1, lambda = spec.
p2;
114 if (!std::isfinite(lo)) lo = 0.0;
115 if (!std::isfinite(hi)) hi = std::max(2.0 * k / lambda, 6.0 / lambda);
132 if (std::isnan(p))
return std::numeric_limits<double>::quiet_NaN();
134 if (x < t.lo || x > t.
hi)
return 0.0;
137 const double Z = cdf_hi - cdf_lo;
138 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
145 if (std::isnan(c))
return std::numeric_limits<double>::quiet_NaN();
147 if (x < t.
lo)
return 0.0;
148 if (x > t.
hi)
return 1.0;
151 const double Z = cdf_hi - cdf_lo;
152 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
153 return (c - cdf_lo) / Z;
167 return std::visit([&](
const auto &v) ->
double {
168 using T = std::decay_t<
decltype(v)>;
169 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
170 return bare_pdf(v, x);
171 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
174 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
177 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
178 const double pl = shape_pdf(*v.left, x);
179 const double pr = shape_pdf(*v.right, x);
180 if (std::isnan(pl) || std::isnan(pr))
181 return std::numeric_limits<double>::quiet_NaN();
182 return v.p * pl + (1.0 - v.p) * pr;
184 return std::numeric_limits<double>::quiet_NaN();
190 return std::visit([&](
const auto &v) ->
double {
191 using T = std::decay_t<
decltype(v)>;
192 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
193 return bare_cdf(v, x);
194 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
195 return (x >= v.value) ? 1.0 : 0.0;
196 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
198 for (
const auto &pr : v.outcomes)
if (pr.first <= x) sum += pr.second;
200 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
201 const double cl = shape_cdf(*v.left, x);
202 const double cr = shape_cdf(*v.right, x);
203 if (std::isnan(cl) || std::isnan(cr))
204 return std::numeric_limits<double>::quiet_NaN();
205 return v.p * cl + (1.0 - v.p) * cr;
207 return std::numeric_limits<double>::quiet_NaN();
213 return std::visit([](
const auto &v) ->
bool {
214 using T = std::decay_t<
decltype(v)>;
215 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>)
return true;
216 else if constexpr (std::is_same_v<T, provsql::DiracShape>)
return false;
217 else if constexpr (std::is_same_v<T, provsql::CategoricalShape>)
return false;
218 else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>)
219 return shape_has_continuous(*v.left) || shape_has_continuous(*v.right);
231 std::vector<std::pair<double, double>> &out)
233 std::visit([&](
const auto &v) {
234 using T = std::decay_t<
decltype(v)>;
235 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
237 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
238 out.emplace_back(v.value, weight);
239 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
240 for (
const auto &pr : v.outcomes)
241 out.emplace_back(pr.first, weight * pr.second);
242 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
243 shape_stems(*v.left, weight * v.p, out);
244 shape_stems(*v.right, weight * (1.0 - v.p), out);
251 return std::visit([](
const auto &v) -> std::pair<double, double> {
252 using T = std::decay_t<
decltype(v)>;
253 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
254 return bare_x_range(v.spec, v.lo, v.hi);
255 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
259 return {v.value - 1.0, v.value + 1.0};
260 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
261 double mn = std::numeric_limits<double>::infinity();
262 double mx = -std::numeric_limits<double>::infinity();
263 for (
const auto &pr : v.outcomes) {
264 mn = std::min(mn, pr.first);
265 mx = std::max(mx, pr.first);
267 const double range = mx - mn;
268 const double pad = range > 0.0 ? 0.1 * range : 1.0;
269 return {mn - pad, mx + pad};
270 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
271 const auto L = shape_x_range(*v.left);
272 const auto R = shape_x_range(*v.right);
273 return {std::min(L.first, R.first), std::max(L.second, R.second)};
290std::optional<std::vector<std::tuple<double, double, double>>>
293 if (bins <= 0)
return std::nullopt;
294 const auto [xlo, xhi] = shape_x_range(shape);
295 if (!(xlo < xhi) || !std::isfinite(xlo) || !std::isfinite(xhi))
297 const double w = (xhi - xlo) / bins;
298 std::vector<std::tuple<double, double, double>> out;
300 for (
int i = 0; i < bins; ++i) {
301 const double lo = xlo + i * w;
302 const double hi = (i == bins - 1) ? xhi : lo + w;
303 const double cl = shape_cdf(shape, lo);
304 const double ch = shape_cdf(shape, hi);
305 if (std::isnan(cl) || std::isnan(ch))
return std::nullopt;
306 out.emplace_back(lo, hi, std::max(0.0, ch - cl));
317 int32 samples = PG_GETARG_INT32(1);
322 "rv_analytical_curves: samples must be at least 2 (got %d)",
326 gate_t root_gate, event_gate;
339 std::optional<gate_t> event_opt;
361 gc, root_gate, event_opt);
362 if (!shape) PG_RETURN_NULL();
364 std::vector<std::pair<double, double>> stems;
365 shape_stems(*shape, 1.0, stems);
366 const bool has_cont = shape_has_continuous(*shape);
372 if (!has_cont && stems.empty()) PG_RETURN_NULL();
377 auto [x_lo, x_hi] = shape_x_range(*shape);
378 if (!(x_lo < x_hi)) PG_RETURN_NULL();
380 std::ostringstream out;
384 out << std::setprecision(17);
386 bool first_field =
true;
394 std::ostringstream pdf_out;
395 pdf_out << std::setprecision(17);
396 if (has_cont) pdf_out <<
"\"pdf\":[";
398 for (
int i = 0; i < samples; ++i) {
399 const double t =
static_cast<double>(i) / (samples - 1);
400 const double x = x_lo + t * (x_hi - x_lo);
401 const double cdf_x = shape_cdf(*shape, x);
402 if (std::isnan(cdf_x)) PG_RETURN_NULL();
403 if (i > 0) out <<
',';
404 out <<
"{\"x\":" << x <<
",\"p\":" << cdf_x <<
'}';
406 const double pdf_x = shape_pdf(*shape, x);
407 if (std::isnan(pdf_x)) PG_RETURN_NULL();
408 if (i > 0) pdf_out <<
',';
409 pdf_out <<
"{\"x\":" << x <<
",\"p\":" << pdf_x <<
'}';
415 out <<
',' << pdf_out.str();
419 if (!stems.empty()) {
420 if (!first_field) out <<
',';
421 out <<
"\"stems\":[";
422 for (std::size_t i = 0; i < stems.size(); ++i) {
423 if (i > 0) out <<
',';
424 out <<
"{\"x\":" << stems[i].first
425 <<
",\"p\":" << stems[i].second <<
'}';
431 Datum json = DirectFunctionCall1(
432 jsonb_in, CStringGetDatum(pstrdup(out.str().c_str())));
433 PG_RETURN_DATUM(json);
434 }
catch (
const std::exception &e) {
Closed-form CDF resolution for trivial gate_cmp shapes.
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.
gate_t
Strongly-typed gate identifier.
Analytical expectation / variance / moment evaluator over RV circuits.
Semiring-agnostic in-memory provenance circuit.
Peephole simplifier for continuous gate_arith sub-circuits.
Continuous random-variable helpers (distribution parsing, moments).
Support-based bound check for continuous-RV comparators.
Datum rv_analytical_curves(PG_FUNCTION_ARGS)
Exception type thrown by circuit operations on invalid input.
gateType getGateType(gate_t g) const
Return the type of gate g.
In-memory provenance circuit with semiring-generic evaluation.
@ 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=λ.
std::optional< ClosedFormShape > matchClosedFormDistribution(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
Detect any of the closed-form shapes supported by rv_analytical_curves.
std::variant< TruncatedSingleRv, DiracShape, CategoricalShape, BernoulliMixtureShape > ClosedFormShape
One of the closed-form shapes the analytical-curves payload can render: bare RV (continuous PDF/CDF),...
gate_t lift_conditioning(GenericCircuit &gc, gate_t root, std::optional< gate_t > &event_opt)
Lift conditioning out of a scalar arithmetic expression.
std::optional< std::vector< std::tuple< double, double, double > > > analyticalHistogram(const ClosedFormShape &shape, int bins)
Exact histogram (bin_lo, bin_hi, probability mass) of a closed-form shape, in bins equal-width bins o...
unsigned runHybridSimplifier(GenericCircuit &gc)
Run the peephole simplifier over gc.
double pdfAt(const DistributionSpec &d, double c)
Closed-form probability density for a basic distribution.
double cdfAt(const DistributionSpec &d, double c)
Closed-form CDF for a basic continuous distribution.
bool provsql_hybrid_evaluation
Run the hybrid-evaluator simplifier inside probability_evaluate; controlled by the provsql....
Uniform error-reporting macros for ProvSQL.
#define provsql_error(fmt,...)
Report a fatal ProvSQL error and abort the current transaction.
Core types, constants, and utilities shared across ProvSQL.
C++ utility functions for UUID manipulation.
Parsed distribution spec (kind + up to two parameters).
double p2
Second parameter (σ or b; unused for Exponential).
double p1
First parameter (μ, a, or λ).
Detection result for a closed-form, optionally-truncated single-RV shape.
double lo
Lower bound (-INF if unbounded).
DistributionSpec spec
Parsed kind + parameters.
double hi
Upper bound (+INF if unbounded).
bool truncated
True iff the bounds came from a non-trivial event_root.