12#include <unordered_map>
13#include <unordered_set>
28#include "access/htup_details.h"
31#include "utils/uuid.h"
54 static Interval point(
double v) {
return {v, v}; }
55 static Interval all() {
return {-std::numeric_limits<double>::infinity(),
56 +std::numeric_limits<double>::infinity()}; }
58 return std::isinf(lo) && lo < 0 && std::isinf(hi) && hi > 0;
62Interval add(Interval a, Interval b) {
return {a.lo + b.lo, a.hi + b.hi}; }
63Interval sub(Interval a, Interval b) {
return {a.lo - b.hi, a.hi - b.lo}; }
64Interval neg(Interval a) {
return {-a.hi, -a.lo}; }
68Interval mul(Interval a, Interval b)
70 double p1 = a.lo * b.lo, p2 = a.lo * b.hi;
71 double p3 = a.hi * b.lo, p4 = a.hi * b.hi;
72 return {std::min({p1, p2, p3, p4}), std::max({p1, p2, p3, p4})};
80Interval divInt(Interval a, Interval b)
82 if (b.lo <= 0.0 && b.hi >= 0.0)
83 return Interval::all();
84 Interval inv = {1.0 / b.hi, 1.0 / b.lo};
102Interval intervalOf(
const GenericCircuit &gc,
gate_t g,
103 std::unordered_map<gate_t, Interval> &
cache)
105 auto it =
cache.find(g);
106 if (it !=
cache.
end())
return it->second;
108 Interval result = Interval::all();
118 switch (spec->kind) {
123 result = {spec->p1, spec->p2};
127 result = {0.0, std::numeric_limits<double>::infinity()};
135 if (wires.empty())
break;
136 Interval first = intervalOf(gc, wires[0],
cache);
140 for (std::size_t i = 1; i < wires.size(); ++i)
141 result = add(result, intervalOf(gc, wires[i],
cache));
145 for (std::size_t i = 1; i < wires.size(); ++i)
146 result = mul(result, intervalOf(gc, wires[i],
cache));
149 if (wires.size() != 2)
break;
150 result = sub(first, intervalOf(gc, wires[1],
cache));
153 if (wires.size() != 2)
break;
154 result = divInt(first, intervalOf(gc, wires[1],
cache));
157 if (wires.size() != 1)
break;
172 result = intervalOf(gc, wires[1],
cache);
186 double lo = std::numeric_limits<double>::infinity();
187 double hi = -std::numeric_limits<double>::infinity();
189 for (std::size_t i = 1; i < wires.size(); ++i) {
192 catch (
const CircuitException &) { any =
false;
break; }
193 lo = std::min(lo, v);
194 hi = std::max(hi, v);
197 if (any) result = {lo, hi};
198 }
else if (wires.size() == 3) {
199 Interval ix = intervalOf(gc, wires[1],
cache);
200 Interval iy = intervalOf(gc, wires[2],
cache);
201 result = {std::min(ix.lo, iy.lo), std::max(ix.hi, iy.hi)};
232 if (diff.hi < 0.0)
return 1.0;
233 if (diff.lo >= 0.0)
return 0.0;
236 if (diff.hi <= 0.0)
return 1.0;
237 if (diff.lo > 0.0)
return 0.0;
240 if (diff.lo > 0.0)
return 1.0;
241 if (diff.hi <= 0.0)
return 0.0;
244 if (diff.lo >= 0.0)
return 1.0;
245 if (diff.hi < 0.0)
return 0.0;
253 if (diff.hi < 0.0 || diff.lo > 0.0)
return 0.0;
256 if (diff.hi < 0.0 || diff.lo > 0.0)
return 1.0;
259 return std::numeric_limits<double>::quiet_NaN();
293double decideAggVsConstCmp(
const GenericCircuit &gc,
gate_t agg_gate,
300 std::vector<double> values;
303 return std::numeric_limits<double>::quiet_NaN();
304 const auto &sw = gc.
getWires(child);
306 return std::numeric_limits<double>::quiet_NaN();
307 gate_t value_gate = sw[1];
309 return std::numeric_limits<double>::quiet_NaN();
312 }
catch (
const CircuitException &) {
313 return std::numeric_limits<double>::quiet_NaN();
317 Interval val_interval = Interval::all();
321 val_interval = {0.0,
static_cast<double>(values.size())};
324 double sum_neg = 0.0, sum_pos = 0.0;
325 for (
double v : values) {
326 if (v < 0.0) sum_neg += v;
329 val_interval = {std::min(0.0, sum_neg), std::max(0.0, sum_pos)};
335 return std::numeric_limits<double>::quiet_NaN();
336 val_interval = {*std::min_element(values.begin(), values.end()),
337 *std::max_element(values.begin(), values.end())};
342 return std::numeric_limits<double>::quiet_NaN();
345 Interval lhs = agg_on_lhs ? val_interval : Interval::point(const_val);
346 Interval rhs = agg_on_lhs ? Interval::point(const_val) : val_interval;
347 Interval diff = sub(lhs, rhs);
348 double p = decideCmp(diff, op);
354 if (p == 0.0)
return 0.0;
355 return std::numeric_limits<double>::quiet_NaN();
368double extractScalarConst(
const GenericCircuit &gc,
gate_t g)
373 catch (
const CircuitException &) {
374 return std::numeric_limits<double>::quiet_NaN();
379 if (w.size() != 2)
return std::numeric_limits<double>::quiet_NaN();
381 return std::numeric_limits<double>::quiet_NaN();
383 return std::numeric_limits<double>::quiet_NaN();
385 catch (
const CircuitException &) {
386 return std::numeric_limits<double>::quiet_NaN();
389 return std::numeric_limits<double>::quiet_NaN();
424bool asRvVsConstCmp(
const GenericCircuit &gc,
gate_t cmp_gate,
430 if (!ok)
return false;
431 const auto &wires = gc.
getWires(cmp_gate);
432 if (wires.size() != 2)
return false;
449 catch (
const CircuitException &) {
return false; }
456 catch (
const CircuitException &) {
return false; }
458 op_out = flipCmpOp(op);
480 current.hi = std::min(current.hi, c);
484 current.lo = std::max(current.lo, c);
487 current.lo = std::max(current.lo, c);
488 current.hi = std::min(current.hi, c);
498bool intervalEmpty(Interval i) {
return i.lo > i.hi; }
524void walkAndConjunctIntervals(
525 const GenericCircuit &gc,
gate_t root,
526 std::unordered_map<gate_t, Interval> &rv_intervals,
527 std::unordered_map<gate_t, Interval> &support_cache,
530 std::unordered_set<gate_t> seen;
531 std::stack<gate_t> stk;
535 while (!stk.empty()) {
536 gate_t g = stk.top(); stk.pop();
537 if (!seen.insert(g).second)
continue;
544 if (!asRvVsConstCmp(gc, g, rv, op, c)) {
552 auto it = rv_intervals.find(rv);
553 Interval current = (it == rv_intervals.end())
554 ? intervalOf(gc, rv, support_cache)
556 current = intersectRvConstraint(current, op, c);
557 rv_intervals[rv] = current;
607bool isAndJointlyInfeasible(
const GenericCircuit &gc,
gate_t root)
609 std::unordered_map<gate_t, Interval> rv_intervals;
610 std::unordered_map<gate_t, Interval> support_cache;
612 walkAndConjunctIntervals(gc, root, rv_intervals, support_cache, complete);
613 for (
const auto &kv : rv_intervals) {
614 if (intervalEmpty(kv.second))
return true;
657bool hasOnlyContinuousSupport(
const GenericCircuit &gc,
gate_t g,
658 std::unordered_map<gate_t, bool> &
cache)
660 auto it =
cache.find(g);
661 if (it !=
cache.
end())
return it->second;
679 if (!hasOnlyContinuousSupport(gc, w,
cache)) { result =
false;
break; }
686 if (w.size() != 3) { result =
false;
break; }
687 result = hasOnlyContinuousSupport(gc, w[1],
cache)
688 && hasOnlyContinuousSupport(gc, w[2],
cache);
721const std::unordered_set<gate_t> &
722collectRandomLeaves(
const GenericCircuit &gc,
gate_t g,
723 std::unordered_map<
gate_t, std::unordered_set<gate_t>> &
cache)
725 auto it =
cache.find(g);
726 if (it !=
cache.
end())
return it->second;
733 cache.emplace(g, std::unordered_set<gate_t>{});
735 std::unordered_set<gate_t> out;
741 const auto &child = collectRandomLeaves(gc, w,
cache);
742 out.insert(child.begin(), child.end());
749 auto fit =
cache.find(g);
750 fit->second = std::move(out);
754using DiracMap = std::unordered_map<double, double>;
755using DiracMapOpt = std::optional<DiracMap>;
789collectDiracMassMap(
const GenericCircuit &gc,
gate_t g,
790 std::unordered_map<gate_t, DiracMapOpt> &
cache)
792 auto it =
cache.find(g);
793 if (it !=
cache.
end())
return it->second;
795 cache.emplace(g, std::nullopt);
804 result = std::move(m);
805 }
catch (
const CircuitException &) {
818 for (std::size_t i = 1; i < w.size(); ++i) {
821 catch (
const CircuitException &) { ok =
false;
break; }
822 const double p = gc.
getProb(w[i]);
823 if (!std::isfinite(p) || p < 0.0 || p > 1.0) { ok =
false;
break; }
826 if (ok) result = std::move(m);
827 }
else if (w.size() == 3
829 const double pi = gc.
getProb(w[0]);
830 if (std::isfinite(pi) && pi >= 0.0 && pi <= 1.0) {
831 auto mx = collectDiracMassMap(gc, w[1],
cache);
832 auto my = collectDiracMassMap(gc, w[2],
cache);
835 for (
const auto &[v, mass] : *mx) m[v] += pi * mass;
836 for (
const auto &[v, mass] : *my) m[v] += (1.0 - pi) * mass;
837 result = std::move(m);
847 auto fit =
cache.find(g);
848 fit->second = result;
856 std::unordered_map<gate_t, Interval>
cache;
861 std::unordered_map<gate_t, bool> continuous_support_cache;
862 std::unordered_map<gate_t, DiracMapOpt> dirac_cache;
863 std::unordered_map<gate_t, std::unordered_set<gate_t>> leaf_cache;
864 unsigned resolved = 0;
872 std::vector<gate_t> cmps;
873 for (std::size_t i = 0; i < nb; ++i) {
874 auto g =
static_cast<gate_t>(i);
887 if (wires.size() != 2)
continue;
896 if (wires[0] == wires[1]) {
897 double p = std::numeric_limits<double>::quiet_NaN();
934 bool lhs_continuous = hasOnlyContinuousSupport(gc, wires[0],
935 continuous_support_cache);
936 bool rhs_continuous = hasOnlyContinuousSupport(gc, wires[1],
937 continuous_support_cache);
938 if (lhs_continuous || rhs_continuous) {
966 auto m_l = collectDiracMassMap(gc, wires[0], dirac_cache);
967 auto m_r = collectDiracMassMap(gc, wires[1], dirac_cache);
969 const auto &leaves_l = collectRandomLeaves(gc, wires[0], leaf_cache);
970 const auto &leaves_r = collectRandomLeaves(gc, wires[1], leaf_cache);
971 bool independent =
true;
972 for (
gate_t leaf : leaves_l) {
973 if (leaves_r.count(leaf)) { independent =
false;
break; }
979 const DiracMap *small = (m_l->size() <= m_r->size()) ? &*m_l : &*m_r;
980 const DiracMap *large = (m_l->size() <= m_r->size()) ? &*m_r : &*m_l;
981 for (
const auto &[v, mass] : *small) {
982 auto fit = large->find(v);
983 if (fit != large->end()) p_eq += mass * fit->second;
988 if (p_eq < 0.0) p_eq = 0.0;
989 if (p_eq > 1.0) p_eq = 1.0;
1004 if (lhs_is_agg != rhs_is_agg) {
1005 gate_t agg_side = lhs_is_agg ? wires[0] : wires[1];
1006 gate_t const_side = lhs_is_agg ? wires[1] : wires[0];
1007 double const_val = extractScalarConst(gc, const_side);
1008 if (!std::isnan(const_val)) {
1009 double p = decideAggVsConstCmp(gc, agg_side, op, const_val,
1011 if (!std::isnan(p)) {
1020 Interval lhs = intervalOf(gc, wires[0],
cache);
1021 Interval rhs = intervalOf(gc, wires[1],
cache);
1024 if (lhs.isAll() && rhs.isAll())
continue;
1026 Interval diff = sub(lhs, rhs);
1027 double p = decideCmp(diff, op);
1028 if (!std::isnan(p)) {
1046 std::vector<gate_t> times_gates;
1047 for (std::size_t i = 0; i < nb_after; ++i) {
1048 auto g =
static_cast<gate_t>(i);
1050 times_gates.push_back(g);
1052 for (
gate_t t : times_gates) {
1054 if (isAndJointlyInfeasible(gc, t)) {
1063std::pair<double, double>
1065 std::optional<gate_t> event_root)
1067 std::unordered_map<gate_t, Interval>
cache;
1068 Interval iv = intervalOf(gc, root,
cache);
1079 if (event_root.has_value()) {
1080 std::unordered_map<gate_t, Interval> rv_intervals;
1082 walkAndConjunctIntervals(gc, *event_root, rv_intervals,
cache, complete);
1083 auto it = rv_intervals.find(root);
1084 if (it != rv_intervals.end()) {
1085 iv.lo = std::max(iv.lo, it->second.lo);
1086 iv.hi = std::min(iv.hi, it->second.hi);
1089 if (iv.lo > iv.hi) iv.lo = iv.hi;
1093 return {iv.lo, iv.hi};
1096std::optional<std::pair<double, double>>
1100 std::unordered_map<gate_t, Interval> rv_intervals;
1101 std::unordered_map<gate_t, Interval> support_cache;
1103 walkAndConjunctIntervals(gc, event_root, rv_intervals, support_cache,
1105 if (!complete)
return std::nullopt;
1112 auto it = rv_intervals.find(target_rv);
1114 if (it != rv_intervals.end()) {
1118 Interval base = intervalOf(gc, target_rv, support_cache);
1119 iv.lo = std::max(iv.lo, base.lo);
1120 iv.hi = std::min(iv.hi, base.hi);
1121 if (iv.lo > iv.hi) iv.lo = iv.hi;
1123 iv = intervalOf(gc, target_rv, support_cache);
1125 return std::make_pair(iv.lo, iv.hi);
1140 const std::string &s = gc.
getExtra(x);
1141 if (s.empty())
return false;
1144 double v = std::stod(s, &idx);
1145 if (idx != s.size() || !std::isfinite(v))
return false;
1158 const std::string &s = gc.
getExtra(mul);
1159 if (s.empty())
return false;
1162 double v = std::stod(s, &idx);
1163 if (idx != s.size() || !std::isfinite(v))
return false;
1171std::optional<TruncatedSingleRv>
1173 std::optional<gate_t> event_root)
1177 if (!spec)
return std::nullopt;
1185 double nat_lo = -std::numeric_limits<double>::infinity();
1186 double nat_hi = +std::numeric_limits<double>::infinity();
1187 switch (spec->kind) {
1190 nat_hi = spec->p2;
break;
1196 if (!event_root.has_value()
1209 if (!iv.has_value())
return std::nullopt;
1210 if (!(iv->first < iv->second))
return std::nullopt;
1216 std::optional<gate_t> event_root)
1218 if (!event_root.has_value())
return false;
1232 if (!iv.has_value())
return false;
1233 return !(iv->first < iv->second);
1252static std::optional<double>
1255 return std::visit([&](
const auto &v) -> std::optional<double> {
1256 using T = std::decay_t<
decltype(v)>;
1257 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1258 const double a = std::max(lo, v.lo);
1259 const double b = std::min(hi, v.hi);
1260 if (!(a < b))
return 0.0;
1261 const double cl = std::isfinite(a) ?
cdfAt(v.spec, a) : 0.0;
1262 const double ch = std::isfinite(b) ?
cdfAt(v.spec, b) : 1.0;
1263 if (std::isnan(cl) || std::isnan(ch))
return std::nullopt;
1265 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1266 return (v.value >= lo && v.value <= hi) ? 1.0 : 0.0;
1267 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1269 for (
const auto &pr : v.outcomes)
1270 if (pr.first >= lo && pr.first <= hi) m += pr.second;
1272 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1275 if (!L || !R)
return std::nullopt;
1276 return v.p * (*L) + (1.0 - v.p) * (*R);
1278 return std::nullopt;
1298static std::optional<ClosedFormShape>
1301 return std::visit([&](
const auto &v) -> std::optional<ClosedFormShape> {
1302 using T = std::decay_t<
decltype(v)>;
1303 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1304 const double a = std::max(lo, v.lo);
1305 const double b = std::min(hi, v.hi);
1306 if (!(a < b))
return std::nullopt;
1308 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1309 if (v.value < lo || v.value > hi)
return std::nullopt;
1311 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1314 for (
const auto &pr : v.outcomes) {
1315 if (pr.first >= lo && pr.first <= hi) {
1316 out.
outcomes.emplace_back(pr.first, pr.second);
1320 if (out.
outcomes.empty() || !(total > 0.0))
return std::nullopt;
1321 for (
auto &pr : out.
outcomes) pr.second /= total;
1323 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1326 if (!mL || !mR)
return std::nullopt;
1327 const double pL = v.p * (*mL);
1328 const double pR = (1.0 - v.p) * (*mR);
1329 const double Z = pL + pR;
1330 if (!(Z > 0.0))
return std::nullopt;
1336 if (!Lt && !Rt)
return std::nullopt;
1341 m.
left = std::make_shared<ClosedFormShape>(std::move(*Lt));
1342 m.
right = std::make_shared<ClosedFormShape>(std::move(*Rt));
1345 return std::nullopt;
1349std::optional<ClosedFormShape>
1351 std::optional<gate_t> event_root)
1355 const bool event_trivial = !event_root.has_value()
1363 if (!m)
return std::nullopt;
1372 auto with_optional_truncation =
1373 [&](std::optional<ClosedFormShape> unc)
1374 -> std::optional<ClosedFormShape> {
1375 if (!unc)
return std::nullopt;
1376 if (event_trivial)
return unc;
1378 if (!iv.has_value())
return std::nullopt;
1379 if (!(iv->first < iv->second))
return std::nullopt;
1405 for (std::size_t i = 1; i < w.size(); ++i) {
1409 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1412 if (cs.
outcomes.empty())
return std::nullopt;
1420 if (w.size() != 3)
return std::nullopt;
1423 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1427 if (!left || !right)
return std::nullopt;
1431 m.
left = std::make_shared<ClosedFormShape>(std::move(*left));
1432 m.
right = std::make_shared<ClosedFormShape>(std::move(*right));
1436 return std::nullopt;
1462 gate_t root_gate, event_gate;
1469 std::optional<gate_t> event_opt;
1471 event_opt = event_gate;
1477 bool nulls[2] = {
false,
false};
1479 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1480 provsql_error(
"rv_support: expected composite return type");
1481 tupdesc = BlessTupleDesc(tupdesc);
1483 values[0] = Float8GetDatum(iv.first);
1484 values[1] = Float8GetDatum(iv.second);
1486 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1487 }
catch (
const std::exception &e) {
ComparisonOperator cmpOpFromOid(Oid op_oid, bool &ok)
Map a PostgreSQL comparison-operator OID to a ComparisonOperator.
AggregationOperator getAggregationOperator(Oid oid)
Map a PostgreSQL aggregate function OID to an AggregationOperator.
Typed aggregation value, operator, and aggregator abstractions.
AggregationOperator
SQL aggregation functions tracked by ProvSQL.
@ COUNT
COUNT(*) or COUNT(expr) → integer.
@ SUM
SUM → integer or float.
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
@ LE
Less than or equal (<=).
@ GE
Greater than or equal (>=).
Closed-form CDF resolution for trivial gate_cmp shapes.
static CircuitCache cache
Process-local singleton circuit gate cache.
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.
Continuous random-variable helpers (distribution parsing, moments).
Datum rv_support(PG_FUNCTION_ARGS)
SQL: rv_support(token uuid, prov uuid, OUT lo float8, OUT hi float8).
Support-based bound check for continuous-RV comparators.
iterator end()
Past-the-end iterator for the cache.
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.
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.
void resolveGateToZero(gate_t g)
Replace an arbitrary gate (typically gate_times) by gate_zero.
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.
void resolveCmpToBernoulli(gate_t g, double p)
Replace a gate_cmp by a constant Boolean leaf (gate_one for p == 1, gate_zero for p == 0) or by a Ber...
std::pair< unsigned, unsigned > getInfos(gate_t g) const
Return the integer annotation pair for gate g.
@ 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::pair< double, double > compute_support(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event_root)
Compute the [lo, hi] support interval of a scalar sub-circuit rooted at root.
static std::optional< ClosedFormShape > truncateShape(const ClosedFormShape &s, double lo, double hi)
Conditional shape after truncating the underlying variable to [lo, hi].
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.
static bool extract_mulinput_value(const GenericCircuit &gc, gate_t mul, double &out)
Same parsing applied to a mulinput's outcome label (categorical).
static bool extract_finite_double(const GenericCircuit &gc, gate_t x, double &out)
Parse a gate_value's extra as a finite float8.
double parseDoubleStrict(const std::string &s)
Strictly parse s as a double.
unsigned runRangeCheck(GenericCircuit &gc)
Run the support-based pruning pass over gc.
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.
static std::optional< double > shape_mass(const ClosedFormShape &s, double lo, double hi)
Unconditional probability mass of a shape over the interval [lo, hi].
std::optional< DistributionSpec > parse_distribution_spec(const std::string &s)
Parse the on-disk text encoding of a gate_rv distribution.
std::optional< std::pair< double, double > > collectRvConstraints(const GenericCircuit &gc, gate_t event_root, gate_t target_rv)
Walk event_root collecting rv op c constraints on target_rv.
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.
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.
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.
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.
Bernoulli mixture (gate_mixture with the [p_token, x_token, y_token] shape).
std::shared_ptr< ClosedFormShape > right
std::shared_ptr< ClosedFormShape > left
Categorical distribution over a finite outcome set.
std::vector< std::pair< double, double > > outcomes
(value, mass) pairs
Point mass at a finite scalar value (a gate_value root, or an as_random(c) leaf surfaced as a gate_va...
Detection result for a closed-form, optionally-truncated single-RV shape.