12#include <unordered_map>
13#include <unordered_set>
29#include "access/htup_details.h"
32#include "utils/uuid.h"
55 static Interval point(
double v) {
return {v, v}; }
56 static Interval all() {
return {-std::numeric_limits<double>::infinity(),
57 +std::numeric_limits<double>::infinity()}; }
59 return std::isinf(lo) && lo < 0 && std::isinf(hi) && hi > 0;
63Interval add(Interval a, Interval b) {
return {a.lo + b.lo, a.hi + b.hi}; }
64Interval sub(Interval a, Interval b) {
return {a.lo - b.hi, a.hi - b.lo}; }
65Interval neg(Interval a) {
return {-a.hi, -a.lo}; }
69Interval mul(Interval a, Interval b)
71 double p1 = a.lo * b.lo, p2 = a.lo * b.hi;
72 double p3 = a.hi * b.lo, p4 = a.hi * b.hi;
73 return {std::min({p1, p2, p3, p4}), std::max({p1, p2, p3, p4})};
81Interval divInt(Interval a, Interval b)
83 if (b.lo <= 0.0 && b.hi >= 0.0)
84 return Interval::all();
85 Interval inv = {1.0 / b.hi, 1.0 / b.lo};
103Interval intervalOf(
const GenericCircuit &gc,
gate_t g,
104 std::unordered_map<gate_t, Interval> &
cache)
106 auto it =
cache.find(g);
107 if (it !=
cache.
end())
return it->second;
109 Interval result = Interval::all();
120 }
catch (
const CircuitException &) {
121 result = Interval::all();
127 switch (spec->kind) {
132 result = {spec->p1, spec->p2};
136 result = {0.0, std::numeric_limits<double>::infinity()};
144 if (wires.empty())
break;
145 Interval first = intervalOf(gc, wires[0],
cache);
149 for (std::size_t i = 1; i < wires.size(); ++i)
150 result = add(result, intervalOf(gc, wires[i],
cache));
154 for (std::size_t i = 1; i < wires.size(); ++i)
155 result = mul(result, intervalOf(gc, wires[i],
cache));
158 if (wires.size() != 2)
break;
159 result = sub(first, intervalOf(gc, wires[1],
cache));
162 if (wires.size() != 2)
break;
163 result = divInt(first, intervalOf(gc, wires[1],
cache));
166 if (wires.size() != 1)
break;
182 result = intervalOf(gc, wires[1],
cache);
196 double lo = std::numeric_limits<double>::infinity();
197 double hi = -std::numeric_limits<double>::infinity();
199 for (std::size_t i = 1; i < wires.size(); ++i) {
202 catch (
const CircuitException &) { any =
false;
break; }
203 lo = std::min(lo, v);
204 hi = std::max(hi, v);
207 if (any) result = {lo, hi};
208 }
else if (wires.size() == 3) {
209 Interval ix = intervalOf(gc, wires[1],
cache);
210 Interval iy = intervalOf(gc, wires[2],
cache);
211 result = {std::min(ix.lo, iy.lo), std::max(ix.hi, iy.hi)};
242 if (diff.hi < 0.0)
return 1.0;
243 if (diff.lo >= 0.0)
return 0.0;
246 if (diff.hi <= 0.0)
return 1.0;
247 if (diff.lo > 0.0)
return 0.0;
250 if (diff.lo > 0.0)
return 1.0;
251 if (diff.hi <= 0.0)
return 0.0;
254 if (diff.lo >= 0.0)
return 1.0;
255 if (diff.hi < 0.0)
return 0.0;
263 if (diff.hi < 0.0 || diff.lo > 0.0)
return 0.0;
266 if (diff.hi < 0.0 || diff.lo > 0.0)
return 1.0;
269 return std::numeric_limits<double>::quiet_NaN();
303double decideAggVsConstCmp(
const GenericCircuit &gc,
gate_t agg_gate,
306 bool *out_always_true =
nullptr)
311 std::vector<double> values;
314 return std::numeric_limits<double>::quiet_NaN();
315 const auto &sw = gc.
getWires(child);
317 return std::numeric_limits<double>::quiet_NaN();
318 gate_t value_gate = sw[1];
320 return std::numeric_limits<double>::quiet_NaN();
323 }
catch (
const CircuitException &) {
324 return std::numeric_limits<double>::quiet_NaN();
328 Interval val_interval = Interval::all();
332 val_interval = {0.0,
static_cast<double>(values.size())};
335 double sum_neg = 0.0, sum_pos = 0.0;
336 for (
double v : values) {
337 if (v < 0.0) sum_neg += v;
340 val_interval = {std::min(0.0, sum_neg), std::max(0.0, sum_pos)};
346 return std::numeric_limits<double>::quiet_NaN();
347 val_interval = {*std::min_element(values.begin(), values.end()),
348 *std::max_element(values.begin(), values.end())};
353 return std::numeric_limits<double>::quiet_NaN();
356 Interval lhs = agg_on_lhs ? val_interval : Interval::point(const_val);
357 Interval rhs = agg_on_lhs ? Interval::point(const_val) : val_interval;
358 Interval diff = sub(lhs, rhs);
359 double p = decideCmp(diff, op);
370 if (p == 0.0)
return 0.0;
371 if (p == 1.0 && out_always_true !=
nullptr) *out_always_true =
true;
372 return std::numeric_limits<double>::quiet_NaN();
385double extractScalarConst(
const GenericCircuit &gc,
gate_t g)
390 catch (
const CircuitException &) {
391 return std::numeric_limits<double>::quiet_NaN();
396 if (w.size() != 2)
return std::numeric_limits<double>::quiet_NaN();
398 return std::numeric_limits<double>::quiet_NaN();
400 return std::numeric_limits<double>::quiet_NaN();
402 catch (
const CircuitException &) {
403 return std::numeric_limits<double>::quiet_NaN();
406 return std::numeric_limits<double>::quiet_NaN();
441bool asRvVsConstCmp(
const GenericCircuit &gc,
gate_t cmp_gate,
447 if (!ok)
return false;
448 const auto &wires = gc.
getWires(cmp_gate);
449 if (wires.size() != 2)
return false;
466 catch (
const CircuitException &) {
return false; }
473 catch (
const CircuitException &) {
return false; }
475 op_out = flipCmpOp(op);
497 current.hi = std::min(current.hi, c);
501 current.lo = std::max(current.lo, c);
504 current.lo = std::max(current.lo, c);
505 current.hi = std::min(current.hi, c);
515bool intervalEmpty(Interval i) {
return i.lo > i.hi; }
541void walkAndConjunctIntervals(
542 const GenericCircuit &gc,
gate_t root,
543 std::unordered_map<gate_t, Interval> &rv_intervals,
544 std::unordered_map<gate_t, Interval> &support_cache,
547 std::unordered_set<gate_t> seen;
548 std::stack<gate_t> stk;
552 while (!stk.empty()) {
553 gate_t g = stk.top(); stk.pop();
554 if (!seen.insert(g).second)
continue;
561 if (!asRvVsConstCmp(gc, g, rv, op, c)) {
569 auto it = rv_intervals.find(rv);
570 Interval current = (it == rv_intervals.end())
571 ? intervalOf(gc, rv, support_cache)
573 current = intersectRvConstraint(current, op, c);
574 rv_intervals[rv] = current;
624bool isAndJointlyInfeasible(
const GenericCircuit &gc,
gate_t root)
626 std::unordered_map<gate_t, Interval> rv_intervals;
627 std::unordered_map<gate_t, Interval> support_cache;
629 walkAndConjunctIntervals(gc, root, rv_intervals, support_cache, complete);
630 for (
const auto &kv : rv_intervals) {
631 if (intervalEmpty(kv.second))
return true;
674bool hasOnlyContinuousSupport(
const GenericCircuit &gc,
gate_t g,
675 std::unordered_map<gate_t, bool> &
cache)
677 auto it =
cache.find(g);
678 if (it !=
cache.
end())
return it->second;
696 if (!hasOnlyContinuousSupport(gc, w,
cache)) { result =
false;
break; }
703 if (w.size() != 3) { result =
false;
break; }
704 result = hasOnlyContinuousSupport(gc, w[1],
cache)
705 && hasOnlyContinuousSupport(gc, w[2],
cache);
738const std::unordered_set<gate_t> &
739collectRandomLeaves(
const GenericCircuit &gc,
gate_t g,
740 std::unordered_map<
gate_t, std::unordered_set<gate_t>> &
cache)
742 auto it =
cache.find(g);
743 if (it !=
cache.
end())
return it->second;
750 cache.emplace(g, std::unordered_set<gate_t>{});
752 std::unordered_set<gate_t> out;
758 const auto &child = collectRandomLeaves(gc, w,
cache);
759 out.insert(child.begin(), child.end());
766 auto fit =
cache.find(g);
767 fit->second = std::move(out);
771using DiracMap = std::unordered_map<double, double>;
772using DiracMapOpt = std::optional<DiracMap>;
806collectDiracMassMap(
const GenericCircuit &gc,
gate_t g,
807 std::unordered_map<gate_t, DiracMapOpt> &
cache)
809 auto it =
cache.find(g);
810 if (it !=
cache.
end())
return it->second;
812 cache.emplace(g, std::nullopt);
821 result = std::move(m);
822 }
catch (
const CircuitException &) {
835 for (std::size_t i = 1; i < w.size(); ++i) {
838 catch (
const CircuitException &) { ok =
false;
break; }
839 const double p = gc.
getProb(w[i]);
840 if (!std::isfinite(p) || p < 0.0 || p > 1.0) { ok =
false;
break; }
843 if (ok) result = std::move(m);
844 }
else if (w.size() == 3
846 const double pi = gc.
getProb(w[0]);
847 if (std::isfinite(pi) && pi >= 0.0 && pi <= 1.0) {
848 auto mx = collectDiracMassMap(gc, w[1],
cache);
849 auto my = collectDiracMassMap(gc, w[2],
cache);
852 for (
const auto &[v, mass] : *mx) m[v] += pi * mass;
853 for (
const auto &[v, mass] : *my) m[v] += (1.0 - pi) * mass;
854 result = std::move(m);
864 auto fit =
cache.find(g);
865 fit->second = result;
873 std::unordered_map<gate_t, Interval>
cache;
878 std::unordered_map<gate_t, bool> continuous_support_cache;
879 std::unordered_map<gate_t, DiracMapOpt> dirac_cache;
880 std::unordered_map<gate_t, std::unordered_set<gate_t>> leaf_cache;
881 unsigned resolved = 0;
889 std::vector<gate_t> cmps;
890 for (std::size_t i = 0; i < nb; ++i) {
891 auto g =
static_cast<gate_t>(i);
904 if (wires.size() != 2)
continue;
913 if (wires[0] == wires[1]) {
914 double p = std::numeric_limits<double>::quiet_NaN();
951 bool lhs_continuous = hasOnlyContinuousSupport(gc, wires[0],
952 continuous_support_cache);
953 bool rhs_continuous = hasOnlyContinuousSupport(gc, wires[1],
954 continuous_support_cache);
955 if (lhs_continuous || rhs_continuous) {
983 auto m_l = collectDiracMassMap(gc, wires[0], dirac_cache);
984 auto m_r = collectDiracMassMap(gc, wires[1], dirac_cache);
986 const auto &leaves_l = collectRandomLeaves(gc, wires[0], leaf_cache);
987 const auto &leaves_r = collectRandomLeaves(gc, wires[1], leaf_cache);
988 bool independent =
true;
989 for (
gate_t leaf : leaves_l) {
990 if (leaves_r.count(leaf)) { independent =
false;
break; }
996 const DiracMap *small = (m_l->size() <= m_r->size()) ? &*m_l : &*m_r;
997 const DiracMap *large = (m_l->size() <= m_r->size()) ? &*m_r : &*m_l;
998 for (
const auto &[v, mass] : *small) {
999 auto fit = large->find(v);
1000 if (fit != large->end()) p_eq += mass * fit->second;
1005 if (p_eq < 0.0) p_eq = 0.0;
1006 if (p_eq > 1.0) p_eq = 1.0;
1021 if (lhs_is_agg != rhs_is_agg) {
1022 gate_t agg_side = lhs_is_agg ? wires[0] : wires[1];
1023 gate_t const_side = lhs_is_agg ? wires[1] : wires[0];
1024 double const_val = extractScalarConst(gc, const_side);
1025 if (!std::isnan(const_val)) {
1026 double p = decideAggVsConstCmp(gc, agg_side, op, const_val,
1028 if (!std::isnan(p)) {
1037 Interval lhs = intervalOf(gc, wires[0],
cache);
1038 Interval rhs = intervalOf(gc, wires[1],
cache);
1041 if (lhs.isAll() && rhs.isAll())
continue;
1043 Interval diff = sub(lhs, rhs);
1044 double p = decideCmp(diff, op);
1045 if (!std::isnan(p)) {
1063 std::vector<gate_t> times_gates;
1064 for (std::size_t i = 0; i < nb_after; ++i) {
1065 auto g =
static_cast<gate_t>(i);
1067 times_gates.push_back(g);
1069 for (
gate_t t : times_gates) {
1071 if (isAndJointlyInfeasible(gc, t)) {
1117 unsigned resolved = 0;
1120 std::vector<gate_t> cmps;
1121 cmps.reserve(nb / 8);
1122 for (std::size_t i = 0; i < nb; ++i) {
1123 auto g =
static_cast<gate_t>(i);
1135 const auto &wires = gc.
getWires(c);
1136 if (wires.size() != 2)
continue;
1140 if (lhs_is_agg == rhs_is_agg)
continue;
1142 gate_t agg_side = lhs_is_agg ? wires[0] : wires[1];
1143 gate_t const_side = lhs_is_agg ? wires[1] : wires[0];
1145 double const_val = extractScalarConst(gc, const_side);
1146 if (std::isnan(const_val))
continue;
1148 bool always_true =
false;
1149 double p = decideAggVsConstCmp(gc, agg_side, op, const_val,
1150 lhs_is_agg, &always_true);
1171 std::vector<gate_t> ks;
1172 bool shape_ok =
true;
1173 ks.reserve(gc.
getWires(agg_side).size());
1177 if (sw.size() != 2) { shape_ok =
false;
break; }
1178 ks.push_back(sw[0]);
1180 if (!shape_ok || ks.empty())
continue;
1207 std::unordered_map<gate_t, Interval> &
cache)
1211 const auto &wires = gc.
getWires(root);
1212 if (wires.size() != 2)
return Interval::all();
1213 Interval vi = intervalOf(gc, wires[1],
cache);
1217 return Interval{std::min(0.0, vi.lo), std::max(0.0, vi.hi)};
1221 const auto &wires = gc.
getWires(root);
1225 std::vector<gate_t> sm_children;
1226 sm_children.reserve(wires.size());
1230 auto child_value_iv = [&](
gate_t sm) -> Interval {
1232 if (sw.size() != 2)
return Interval::all();
1233 return intervalOf(gc, sw[1],
cache);
1235 auto child_always_fires = [&](
gate_t sm) ->
bool {
1240 const auto inf = std::numeric_limits<double>::infinity();
1244 return Interval{0.0,
static_cast<double>(sm_children.size())};
1250 double lo = 0.0, hi = 0.0;
1251 for (
gate_t sm : sm_children) {
1252 Interval vi = child_value_iv(sm);
1253 if (vi.isAll())
return Interval::all();
1254 if (child_always_fires(sm)) {
1258 lo += std::min(0.0, vi.lo);
1259 hi += std::max(0.0, vi.hi);
1262 return Interval{lo, hi};
1271 if (sm_children.empty())
return Interval::all();
1274 for (
gate_t sm : sm_children) {
1275 Interval vi = child_value_iv(sm);
1276 if (vi.isAll())
return Interval::all();
1277 lo = std::min(lo, vi.lo);
1278 hi = std::max(hi, vi.hi);
1280 if (lo > hi)
return Interval::all();
1281 return Interval{lo, hi};
1287 return Interval::all();
1293std::pair<double, double>
1295 std::optional<gate_t> event_root)
1297 std::unordered_map<gate_t, Interval>
cache;
1298 Interval iv = aggSupportOf(gc, root,
cache);
1309 if (event_root.has_value()) {
1310 std::unordered_map<gate_t, Interval> rv_intervals;
1312 walkAndConjunctIntervals(gc, *event_root, rv_intervals,
cache, complete);
1313 auto it = rv_intervals.find(root);
1314 if (it != rv_intervals.end()) {
1315 iv.lo = std::max(iv.lo, it->second.lo);
1316 iv.hi = std::min(iv.hi, it->second.hi);
1319 if (iv.lo > iv.hi) iv.lo = iv.hi;
1323 return {iv.lo, iv.hi};
1326std::optional<std::pair<double, double>>
1330 std::unordered_map<gate_t, Interval> rv_intervals;
1331 std::unordered_map<gate_t, Interval> support_cache;
1333 walkAndConjunctIntervals(gc, event_root, rv_intervals, support_cache,
1335 if (!complete)
return std::nullopt;
1342 auto it = rv_intervals.find(target_rv);
1344 if (it != rv_intervals.end()) {
1348 Interval base = intervalOf(gc, target_rv, support_cache);
1349 iv.lo = std::max(iv.lo, base.lo);
1350 iv.hi = std::min(iv.hi, base.hi);
1351 if (iv.lo > iv.hi) iv.lo = iv.hi;
1353 iv = intervalOf(gc, target_rv, support_cache);
1355 return std::make_pair(iv.lo, iv.hi);
1370 const std::string &s = gc.
getExtra(x);
1371 if (s.empty())
return false;
1374 double v = std::stod(s, &idx);
1375 if (idx != s.size() || !std::isfinite(v))
return false;
1388 const std::string &s = gc.
getExtra(mul);
1389 if (s.empty())
return false;
1392 double v = std::stod(s, &idx);
1393 if (idx != s.size() || !std::isfinite(v))
return false;
1401std::optional<TruncatedSingleRv>
1403 std::optional<gate_t> event_root)
1407 if (!spec)
return std::nullopt;
1415 double nat_lo = -std::numeric_limits<double>::infinity();
1416 double nat_hi = +std::numeric_limits<double>::infinity();
1417 switch (spec->kind) {
1420 nat_hi = spec->p2;
break;
1426 if (!event_root.has_value()
1439 if (!iv.has_value())
return std::nullopt;
1440 if (!(iv->first < iv->second))
return std::nullopt;
1446 std::optional<gate_t> event_root)
1448 if (!event_root.has_value())
return false;
1462 if (!iv.has_value())
return false;
1463 return !(iv->first < iv->second);
1482static std::optional<double>
1485 return std::visit([&](
const auto &v) -> std::optional<double> {
1486 using T = std::decay_t<
decltype(v)>;
1487 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1488 const double a = std::max(lo, v.lo);
1489 const double b = std::min(hi, v.hi);
1490 if (!(a < b))
return 0.0;
1491 const double cl = std::isfinite(a) ?
cdfAt(v.spec, a) : 0.0;
1492 const double ch = std::isfinite(b) ?
cdfAt(v.spec, b) : 1.0;
1493 if (std::isnan(cl) || std::isnan(ch))
return std::nullopt;
1495 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1496 return (v.value >= lo && v.value <= hi) ? 1.0 : 0.0;
1497 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1499 for (
const auto &pr : v.outcomes)
1500 if (pr.first >= lo && pr.first <= hi) m += pr.second;
1502 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1505 if (!L || !R)
return std::nullopt;
1506 return v.p * (*L) + (1.0 - v.p) * (*R);
1508 return std::nullopt;
1528static std::optional<ClosedFormShape>
1531 return std::visit([&](
const auto &v) -> std::optional<ClosedFormShape> {
1532 using T = std::decay_t<
decltype(v)>;
1533 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1534 const double a = std::max(lo, v.lo);
1535 const double b = std::min(hi, v.hi);
1536 if (!(a < b))
return std::nullopt;
1538 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1539 if (v.value < lo || v.value > hi)
return std::nullopt;
1541 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1544 for (
const auto &pr : v.outcomes) {
1545 if (pr.first >= lo && pr.first <= hi) {
1546 out.
outcomes.emplace_back(pr.first, pr.second);
1550 if (out.
outcomes.empty() || !(total > 0.0))
return std::nullopt;
1551 for (
auto &pr : out.
outcomes) pr.second /= total;
1553 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1556 if (!mL || !mR)
return std::nullopt;
1557 const double pL = v.p * (*mL);
1558 const double pR = (1.0 - v.p) * (*mR);
1559 const double Z = pL + pR;
1560 if (!(Z > 0.0))
return std::nullopt;
1566 if (!Lt && !Rt)
return std::nullopt;
1571 m.
left = std::make_shared<ClosedFormShape>(std::move(*Lt));
1572 m.
right = std::make_shared<ClosedFormShape>(std::move(*Rt));
1575 return std::nullopt;
1579std::optional<ClosedFormShape>
1581 std::optional<gate_t> event_root)
1585 const bool event_trivial = !event_root.has_value()
1593 if (!m)
return std::nullopt;
1602 auto with_optional_truncation =
1603 [&](std::optional<ClosedFormShape> unc)
1604 -> std::optional<ClosedFormShape> {
1605 if (!unc)
return std::nullopt;
1606 if (event_trivial)
return unc;
1608 if (!iv.has_value())
return std::nullopt;
1609 if (!(iv->first < iv->second))
return std::nullopt;
1635 for (std::size_t i = 1; i < w.size(); ++i) {
1639 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1642 if (cs.
outcomes.empty())
return std::nullopt;
1650 if (w.size() != 3)
return std::nullopt;
1653 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1657 if (!left || !right)
return std::nullopt;
1661 m.
left = std::make_shared<ClosedFormShape>(std::move(*left));
1662 m.
right = std::make_shared<ClosedFormShape>(std::move(*right));
1666 return std::nullopt;
1692 gate_t root_gate, event_gate;
1699 std::optional<gate_t> event_opt;
1701 event_opt = event_gate;
1712 bool nulls[2] = {
false,
false};
1714 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1715 provsql_error(
"rv_support: expected composite return type");
1716 tupdesc = BlessTupleDesc(tupdesc);
1718 values[0] = Float8GetDatum(iv.first);
1719 values[1] = Float8GetDatum(iv.second);
1721 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1722 }
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.
Analytical expectation / variance / moment evaluator over RV circuits.
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.
void resolveCmpToPlusOfKGates(gate_t g, const std::vector< gate_t > &ks)
Replace a gate_cmp by a gate_plus over the given per-row K-gates (the OR of the agg's row-presence in...
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.
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.
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.
double cdfAt(const DistributionSpec &d, double c)
Closed-form CDF for a basic continuous distribution.
unsigned runHavingAlwaysTrueRewriter(GenericCircuit &gc)
Probability-side pre-pass: rewrite HAVING-style gate_cmp gates that are provably TRUE on the agg's va...
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)
#define PROVSQL_AGG_SCALAR_FLAG
Scalar-aggregation flag, stored in the upper bit of a gate_agg's info2 (whose low 31 bits hold the ag...
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.