41#include "utils/jsonb.h"
42#include "utils/fmgrprotos.h"
43#include "utils/uuid.h"
83std::pair<double, double>
85 double trunc_lo,
double trunc_hi)
87 double lo = trunc_lo, hi = trunc_hi;
90 const double mu = spec.
p1, sigma = spec.
p2;
91 if (!std::isfinite(lo)) lo = mu - 4.0 * sigma;
92 if (!std::isfinite(hi)) hi = mu + 4.0 * sigma;
96 const double a = spec.
p1, b = spec.
p2;
97 const double pad = 0.15 * (b - a);
98 if (!std::isfinite(lo)) lo = a - pad;
99 else lo = std::max(lo, a - pad);
100 if (!std::isfinite(hi)) hi = b + pad;
101 else hi = std::min(hi, b + pad);
105 const double lambda = spec.
p1;
106 if (!std::isfinite(lo)) lo = 0.0;
107 if (!std::isfinite(hi)) hi = 6.0 / lambda;
111 const double k = spec.
p1, lambda = spec.
p2;
112 if (!std::isfinite(lo)) lo = 0.0;
113 if (!std::isfinite(hi)) hi = std::max(2.0 * k / lambda, 6.0 / lambda);
130 if (std::isnan(p))
return std::numeric_limits<double>::quiet_NaN();
132 if (x < t.lo || x > t.
hi)
return 0.0;
135 const double Z = cdf_hi - cdf_lo;
136 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
143 if (std::isnan(c))
return std::numeric_limits<double>::quiet_NaN();
145 if (x < t.
lo)
return 0.0;
146 if (x > t.
hi)
return 1.0;
149 const double Z = cdf_hi - cdf_lo;
150 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
151 return (c - cdf_lo) / Z;
165 return std::visit([&](
const auto &v) ->
double {
166 using T = std::decay_t<
decltype(v)>;
167 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
168 return bare_pdf(v, x);
169 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
172 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
175 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
176 const double pl = shape_pdf(*v.left, x);
177 const double pr = shape_pdf(*v.right, x);
178 if (std::isnan(pl) || std::isnan(pr))
179 return std::numeric_limits<double>::quiet_NaN();
180 return v.p * pl + (1.0 - v.p) * pr;
182 return std::numeric_limits<double>::quiet_NaN();
188 return std::visit([&](
const auto &v) ->
double {
189 using T = std::decay_t<
decltype(v)>;
190 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
191 return bare_cdf(v, x);
192 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
193 return (x >= v.value) ? 1.0 : 0.0;
194 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
196 for (
const auto &pr : v.outcomes)
if (pr.first <= x) sum += pr.second;
198 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
199 const double cl = shape_cdf(*v.left, x);
200 const double cr = shape_cdf(*v.right, x);
201 if (std::isnan(cl) || std::isnan(cr))
202 return std::numeric_limits<double>::quiet_NaN();
203 return v.p * cl + (1.0 - v.p) * cr;
205 return std::numeric_limits<double>::quiet_NaN();
211 return std::visit([](
const auto &v) ->
bool {
212 using T = std::decay_t<
decltype(v)>;
213 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>)
return true;
214 else if constexpr (std::is_same_v<T, provsql::DiracShape>)
return false;
215 else if constexpr (std::is_same_v<T, provsql::CategoricalShape>)
return false;
216 else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>)
217 return shape_has_continuous(*v.left) || shape_has_continuous(*v.right);
229 std::vector<std::pair<double, double>> &out)
231 std::visit([&](
const auto &v) {
232 using T = std::decay_t<
decltype(v)>;
233 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
235 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
236 out.emplace_back(v.value, weight);
237 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
238 for (
const auto &pr : v.outcomes)
239 out.emplace_back(pr.first, weight * pr.second);
240 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
241 shape_stems(*v.left, weight * v.p, out);
242 shape_stems(*v.right, weight * (1.0 - v.p), out);
249 return std::visit([](
const auto &v) -> std::pair<double, double> {
250 using T = std::decay_t<
decltype(v)>;
251 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
252 return bare_x_range(v.spec, v.lo, v.hi);
253 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
257 return {v.value - 1.0, v.value + 1.0};
258 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
259 double mn = std::numeric_limits<double>::infinity();
260 double mx = -std::numeric_limits<double>::infinity();
261 for (
const auto &pr : v.outcomes) {
262 mn = std::min(mn, pr.first);
263 mx = std::max(mx, pr.first);
265 const double range = mx - mn;
266 const double pad = range > 0.0 ? 0.1 * range : 1.0;
267 return {mn - pad, mx + pad};
268 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
269 const auto L = shape_x_range(*v.left);
270 const auto R = shape_x_range(*v.right);
271 return {std::min(L.first, R.first), std::max(L.second, R.second)};
283 int32 samples = PG_GETARG_INT32(1);
288 "rv_analytical_curves: samples must be at least 2 (got %d)",
292 gate_t root_gate, event_gate;
318 gc, root_gate, std::optional<gate_t>{event_gate});
319 if (!shape) PG_RETURN_NULL();
321 std::vector<std::pair<double, double>> stems;
322 shape_stems(*shape, 1.0, stems);
323 const bool has_cont = shape_has_continuous(*shape);
329 if (!has_cont && stems.empty()) PG_RETURN_NULL();
334 auto [x_lo, x_hi] = shape_x_range(*shape);
335 if (!(x_lo < x_hi)) PG_RETURN_NULL();
337 std::ostringstream out;
341 out << std::setprecision(17);
343 bool first_field =
true;
351 std::ostringstream pdf_out;
352 pdf_out << std::setprecision(17);
353 if (has_cont) pdf_out <<
"\"pdf\":[";
355 for (
int i = 0; i < samples; ++i) {
356 const double t =
static_cast<double>(i) / (samples - 1);
357 const double x = x_lo + t * (x_hi - x_lo);
358 const double cdf_x = shape_cdf(*shape, x);
359 if (std::isnan(cdf_x)) PG_RETURN_NULL();
360 if (i > 0) out <<
',';
361 out <<
"{\"x\":" << x <<
",\"p\":" << cdf_x <<
'}';
363 const double pdf_x = shape_pdf(*shape, x);
364 if (std::isnan(pdf_x)) PG_RETURN_NULL();
365 if (i > 0) pdf_out <<
',';
366 pdf_out <<
"{\"x\":" << x <<
",\"p\":" << pdf_x <<
'}';
372 out <<
',' << pdf_out.str();
376 if (!stems.empty()) {
377 if (!first_field) out <<
',';
378 out <<
"\"stems\":[";
379 for (std::size_t i = 0; i < stems.size(); ++i) {
380 if (i > 0) out <<
',';
381 out <<
"{\"x\":" << stems[i].first
382 <<
",\"p\":" << stems[i].second <<
'}';
388 Datum json = DirectFunctionCall1(
389 jsonb_in, CStringGetDatum(pstrdup(out.str().c_str())));
390 PG_RETURN_DATUM(json);
391 }
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.
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.
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.
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.
std::variant< TruncatedSingleRv, DiracShape, CategoricalShape, BernoulliMixtureShape > ClosedFormShape
One of the closed-form shapes the analytical-curves payload can render: bare RV (continuous PDF/CDF),...
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.