43#include "utils/jsonb.h"
44#include "utils/fmgrprotos.h"
45#include "utils/uuid.h"
88std::pair<double, double>
90 double trunc_lo,
double trunc_hi)
105 if (std::isnan(p))
return std::numeric_limits<double>::quiet_NaN();
107 if (x < t.lo || x > t.
hi)
return 0.0;
110 const double Z = cdf_hi - cdf_lo;
111 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
118 if (std::isnan(c))
return std::numeric_limits<double>::quiet_NaN();
120 if (x < t.
lo)
return 0.0;
121 if (x > t.
hi)
return 1.0;
124 const double Z = cdf_hi - cdf_lo;
125 if (!(Z > 0.0))
return std::numeric_limits<double>::quiet_NaN();
126 return (c - cdf_lo) / Z;
140 return std::visit([&](
const auto &v) ->
double {
141 using T = std::decay_t<
decltype(v)>;
142 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
143 return bare_pdf(v, x);
144 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
147 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
150 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
151 const double pl = shape_pdf(*v.left, x);
152 const double pr = shape_pdf(*v.right, x);
153 if (std::isnan(pl) || std::isnan(pr))
154 return std::numeric_limits<double>::quiet_NaN();
155 return v.p * pl + (1.0 - v.p) * pr;
157 return std::numeric_limits<double>::quiet_NaN();
163 return std::visit([&](
const auto &v) ->
double {
164 using T = std::decay_t<
decltype(v)>;
165 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
166 return bare_cdf(v, x);
167 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
168 return (x >= v.value) ? 1.0 : 0.0;
169 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
171 for (
const auto &pr : v.outcomes)
if (pr.first <= x) sum += pr.second;
173 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
174 const double cl = shape_cdf(*v.left, x);
175 const double cr = shape_cdf(*v.right, x);
176 if (std::isnan(cl) || std::isnan(cr))
177 return std::numeric_limits<double>::quiet_NaN();
178 return v.p * cl + (1.0 - v.p) * cr;
180 return std::numeric_limits<double>::quiet_NaN();
186 return std::visit([](
const auto &v) ->
bool {
187 using T = std::decay_t<
decltype(v)>;
188 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>)
return true;
189 else if constexpr (std::is_same_v<T, provsql::DiracShape>)
return false;
190 else if constexpr (std::is_same_v<T, provsql::CategoricalShape>)
return false;
191 else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>)
192 return shape_has_continuous(*v.left) || shape_has_continuous(*v.right);
204 std::vector<std::pair<double, double>> &out)
206 std::visit([&](
const auto &v) {
207 using T = std::decay_t<
decltype(v)>;
208 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
210 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
211 out.emplace_back(v.value, weight);
212 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
213 for (
const auto &pr : v.outcomes)
214 out.emplace_back(pr.first, weight * pr.second);
215 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
216 shape_stems(*v.left, weight * v.p, out);
217 shape_stems(*v.right, weight * (1.0 - v.p), out);
224 return std::visit([](
const auto &v) -> std::pair<double, double> {
225 using T = std::decay_t<
decltype(v)>;
226 if constexpr (std::is_same_v<T, provsql::TruncatedSingleRv>) {
227 return bare_x_range(v.spec, v.lo, v.hi);
228 }
else if constexpr (std::is_same_v<T, provsql::DiracShape>) {
232 return {v.value - 1.0, v.value + 1.0};
233 }
else if constexpr (std::is_same_v<T, provsql::CategoricalShape>) {
234 double mn = std::numeric_limits<double>::infinity();
235 double mx = -std::numeric_limits<double>::infinity();
236 for (
const auto &pr : v.outcomes) {
237 mn = std::min(mn, pr.first);
238 mx = std::max(mx, pr.first);
240 const double range = mx - mn;
241 const double pad = range > 0.0 ? 0.1 * range : 1.0;
242 return {mn - pad, mx + pad};
243 }
else if constexpr (std::is_same_v<T, provsql::BernoulliMixtureShape>) {
244 const auto L = shape_x_range(*v.left);
245 const auto R = shape_x_range(*v.right);
246 return {std::min(L.first, R.first), std::max(L.second, R.second)};
263std::optional<std::vector<std::tuple<double, double, double>>>
266 if (bins <= 0)
return std::nullopt;
267 const auto [xlo, xhi] = shape_x_range(shape);
268 if (!(xlo < xhi) || !std::isfinite(xlo) || !std::isfinite(xhi))
270 const double w = (xhi - xlo) / bins;
271 std::vector<std::tuple<double, double, double>> out;
273 for (
int i = 0; i < bins; ++i) {
274 const double lo = xlo + i * w;
275 const double hi = (i == bins - 1) ? xhi : lo + w;
276 const double cl = shape_cdf(shape, lo);
277 const double ch = shape_cdf(shape, hi);
278 if (std::isnan(cl) || std::isnan(ch))
return std::nullopt;
279 out.emplace_back(lo, hi, std::max(0.0, ch - cl));
290 int32 samples = PG_GETARG_INT32(1);
295 "rv_analytical_curves: samples must be at least 2 (got %d)",
299 gate_t root_gate, event_gate;
312 std::optional<gate_t> event_opt;
334 gc, root_gate, event_opt);
335 if (!shape) PG_RETURN_NULL();
337 std::vector<std::pair<double, double>> stems;
338 shape_stems(*shape, 1.0, stems);
339 const bool has_cont = shape_has_continuous(*shape);
345 if (!has_cont && stems.empty()) PG_RETURN_NULL();
350 auto [x_lo, x_hi] = shape_x_range(*shape);
351 if (!(x_lo < x_hi)) PG_RETURN_NULL();
353 std::ostringstream out;
357 out << std::setprecision(17);
359 bool first_field =
true;
367 std::ostringstream pdf_out;
368 pdf_out << std::setprecision(17);
369 if (has_cont) pdf_out <<
"\"pdf\":[";
371 for (
int i = 0; i < samples; ++i) {
372 const double t =
static_cast<double>(i) / (samples - 1);
373 const double x = x_lo + t * (x_hi - x_lo);
374 const double cdf_x = shape_cdf(*shape, x);
375 if (std::isnan(cdf_x)) PG_RETURN_NULL();
376 if (i > 0) out <<
',';
377 out <<
"{\"x\":" << x <<
",\"p\":" << cdf_x <<
'}';
379 const double pdf_x = shape_pdf(*shape, x);
380 if (std::isnan(pdf_x)) PG_RETURN_NULL();
381 if (i > 0) pdf_out <<
',';
382 pdf_out <<
"{\"x\":" << x <<
",\"p\":" << pdf_x <<
'}';
388 out <<
',' << pdf_out.str();
392 if (!stems.empty()) {
393 if (!first_field) out <<
',';
394 out <<
"\"stems\":[";
395 for (std::size_t i = 0; i < stems.size(); ++i) {
396 if (i > 0) out <<
',';
397 out <<
"{\"x\":" << stems[i].first
398 <<
",\"p\":" << stems[i].second <<
'}';
404 Datum json = DirectFunctionCall1(
405 jsonb_in, CStringGetDatum(pstrdup(out.str().c_str())));
406 PG_RETURN_DATUM(json);
407 }
catch (
const std::exception &e) {
Closed-form CDF resolution for trivial gate_cmp shapes.
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.
Per-family polymorphic view over a continuous gate_rv distribution (§F.1 class hierarchy).
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.
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...
std::unique_ptr< Distribution > makeDistribution(const DistributionSpec &spec)
Construct the per-family Distribution for a parsed spec.
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 (family + up to two parameters).
Detection result for a closed-form, optionally-truncated single-RV shape.
double lo
Lower bound (-INF if unbounded).
DistributionSpec spec
Parsed family + parameters.
double hi
Upper bound (+INF if unbounded).
bool truncated
True iff the bounds came from a non-trivial event_root.