12#include <unordered_map>
13#include <unordered_set>
31#include "access/htup_details.h"
34#include "utils/uuid.h"
57 static Interval point(
double v) {
return {v, v}; }
58 static Interval all() {
return {-std::numeric_limits<double>::infinity(),
59 +std::numeric_limits<double>::infinity()}; }
61 return std::isinf(lo) && lo < 0 && std::isinf(hi) && hi > 0;
65Interval add(Interval a, Interval b) {
return {a.lo + b.lo, a.hi + b.hi}; }
66Interval sub(Interval a, Interval b) {
return {a.lo - b.hi, a.hi - b.lo}; }
67Interval neg(Interval a) {
return {-a.hi, -a.lo}; }
71Interval mul(Interval a, Interval b)
73 double p1 = a.lo * b.lo, p2 = a.lo * b.hi;
74 double p3 = a.hi * b.lo, p4 = a.hi * b.hi;
75 return {std::min({p1, p2, p3, p4}), std::max({p1, p2, p3, p4})};
84Interval expInt(Interval a) {
return {std::exp(a.lo), std::exp(a.hi)}; }
89Interval lnInt(Interval a)
91 const double lo = a.lo > 0.0 ? std::log(a.lo)
92 : -std::numeric_limits<double>::infinity();
93 const double hi = a.hi > 0.0 ? std::log(a.hi)
94 : -std::numeric_limits<double>::infinity();
104Interval powInt(Interval b, Interval e)
107 return Interval::all();
108 double lo = std::numeric_limits<double>::infinity();
109 double hi = -std::numeric_limits<double>::infinity();
110 for (
double x : {b.lo, b.hi})
111 for (
double y : {e.lo, e.hi}) {
112 const double v = std::pow(x, y);
114 return Interval::all();
115 lo = std::min(lo, v);
116 hi = std::max(hi, v);
121Interval divInt(Interval a, Interval b)
123 if (b.lo <= 0.0 && b.hi >= 0.0)
124 return Interval::all();
125 Interval inv = {1.0 / b.hi, 1.0 / b.lo};
143Interval intervalOf(
const GenericCircuit &gc,
gate_t g,
144 std::unordered_map<gate_t, Interval> &
cache)
146 auto it =
cache.find(g);
147 if (it !=
cache.
end())
return it->second;
149 Interval result = Interval::all();
160 }
catch (
const CircuitException &) {
161 result = Interval::all();
169 result = {s.lo, s.hi};
175 if (wires.empty())
break;
176 Interval first = intervalOf(gc, wires[0],
cache);
180 for (std::size_t i = 1; i < wires.size(); ++i)
181 result = add(result, intervalOf(gc, wires[i],
cache));
185 for (std::size_t i = 1; i < wires.size(); ++i)
186 result = mul(result, intervalOf(gc, wires[i],
cache));
189 if (wires.size() != 2)
break;
190 result = sub(first, intervalOf(gc, wires[1],
cache));
193 if (wires.size() != 2)
break;
194 result = divInt(first, intervalOf(gc, wires[1],
cache));
197 if (wires.size() != 1)
break;
205 for (std::size_t i = 1; i < wires.size(); ++i) {
206 Interval o = intervalOf(gc, wires[i],
cache);
207 result = { std::max(result.lo, o.lo), std::max(result.hi, o.hi) };
212 for (std::size_t i = 1; i < wires.size(); ++i) {
213 Interval o = intervalOf(gc, wires[i],
cache);
214 result = { std::min(result.lo, o.lo), std::min(result.hi, o.hi) };
218 if (wires.size() != 2)
break;
219 result = powInt(first, intervalOf(gc, wires[1],
cache));
222 if (wires.size() != 1)
break;
223 result = lnInt(first);
226 if (wires.size() != 1)
break;
227 result = expInt(first);
234 if (wires.size() < 2 || wires.size() % 2 != 0)
break;
235 result = intervalOf(gc, wires[1],
cache);
236 for (std::size_t i = 3; i < wires.size(); i += 2) {
237 Interval o = intervalOf(gc, wires[i],
cache);
238 result = { std::min(result.lo, o.lo), std::max(result.hi, o.hi) };
254 result = intervalOf(gc, wires[1],
cache);
268 double lo = std::numeric_limits<double>::infinity();
269 double hi = -std::numeric_limits<double>::infinity();
271 for (std::size_t i = 1; i < wires.size(); ++i) {
274 catch (
const CircuitException &) { any =
false;
break; }
275 lo = std::min(lo, v);
276 hi = std::max(hi, v);
279 if (any) result = {lo, hi};
280 }
else if (wires.size() == 3) {
281 Interval ix = intervalOf(gc, wires[1],
cache);
282 Interval iy = intervalOf(gc, wires[2],
cache);
283 result = {std::min(ix.lo, iy.lo), std::max(ix.hi, iy.hi)};
293 if (wires.empty())
break;
294 result = intervalOf(gc, wires.back(),
cache);
295 for (std::size_t i = 1; i + 1 < wires.size(); i += 2) {
296 Interval o = intervalOf(gc, wires[i],
cache);
297 result = { std::min(result.lo, o.lo), std::max(result.hi, o.hi) };
328 if (diff.hi < 0.0)
return 1.0;
329 if (diff.lo >= 0.0)
return 0.0;
332 if (diff.hi <= 0.0)
return 1.0;
333 if (diff.lo > 0.0)
return 0.0;
336 if (diff.lo > 0.0)
return 1.0;
337 if (diff.hi <= 0.0)
return 0.0;
340 if (diff.lo >= 0.0)
return 1.0;
341 if (diff.hi < 0.0)
return 0.0;
349 if (diff.hi < 0.0 || diff.lo > 0.0)
return 0.0;
352 if (diff.hi < 0.0 || diff.lo > 0.0)
return 1.0;
355 return std::numeric_limits<double>::quiet_NaN();
389double decideAggVsConstCmp(
const GenericCircuit &gc,
gate_t agg_gate,
392 bool *out_always_true =
nullptr)
397 std::vector<double> values;
400 return std::numeric_limits<double>::quiet_NaN();
401 const auto &sw = gc.
getWires(child);
403 return std::numeric_limits<double>::quiet_NaN();
404 gate_t value_gate = sw[1];
406 return std::numeric_limits<double>::quiet_NaN();
409 }
catch (
const CircuitException &) {
410 return std::numeric_limits<double>::quiet_NaN();
414 Interval val_interval = Interval::all();
418 val_interval = {0.0,
static_cast<double>(values.size())};
421 double sum_neg = 0.0, sum_pos = 0.0;
422 for (
double v : values) {
423 if (v < 0.0) sum_neg += v;
426 val_interval = {std::min(0.0, sum_neg), std::max(0.0, sum_pos)};
432 return std::numeric_limits<double>::quiet_NaN();
433 val_interval = {*std::min_element(values.begin(), values.end()),
434 *std::max_element(values.begin(), values.end())};
439 return std::numeric_limits<double>::quiet_NaN();
442 Interval lhs = agg_on_lhs ? val_interval : Interval::point(const_val);
443 Interval rhs = agg_on_lhs ? Interval::point(const_val) : val_interval;
444 Interval diff = sub(lhs, rhs);
445 double p = decideCmp(diff, op);
456 if (p == 0.0)
return 0.0;
457 if (p == 1.0 && out_always_true !=
nullptr) *out_always_true =
true;
458 return std::numeric_limits<double>::quiet_NaN();
471double extractScalarConst(
const GenericCircuit &gc,
gate_t g)
476 catch (
const CircuitException &) {
477 return std::numeric_limits<double>::quiet_NaN();
482 if (w.size() != 2)
return std::numeric_limits<double>::quiet_NaN();
484 return std::numeric_limits<double>::quiet_NaN();
486 return std::numeric_limits<double>::quiet_NaN();
488 catch (
const CircuitException &) {
489 return std::numeric_limits<double>::quiet_NaN();
492 return std::numeric_limits<double>::quiet_NaN();
527bool asRvVsConstCmp(
const GenericCircuit &gc,
gate_t cmp_gate,
533 if (!ok)
return false;
534 const auto &wires = gc.
getWires(cmp_gate);
535 if (wires.size() != 2)
return false;
552 catch (
const CircuitException &) {
return false; }
559 catch (
const CircuitException &) {
return false; }
561 op_out = flipCmpOp(op);
583 current.hi = std::min(current.hi, c);
587 current.lo = std::max(current.lo, c);
590 current.lo = std::max(current.lo, c);
591 current.hi = std::min(current.hi, c);
601bool intervalEmpty(Interval i) {
return i.lo > i.hi; }
627void walkAndConjunctIntervals(
628 const GenericCircuit &gc,
gate_t root,
629 std::unordered_map<gate_t, Interval> &rv_intervals,
630 std::unordered_map<gate_t, Interval> &support_cache,
633 std::unordered_set<gate_t> seen;
634 std::stack<gate_t> stk;
638 while (!stk.empty()) {
639 gate_t g = stk.top(); stk.pop();
640 if (!seen.insert(g).second)
continue;
647 if (!asRvVsConstCmp(gc, g, rv, op, c)) {
655 auto it = rv_intervals.find(rv);
656 Interval current = (it == rv_intervals.end())
657 ? intervalOf(gc, rv, support_cache)
659 current = intersectRvConstraint(current, op, c);
660 rv_intervals[rv] = current;
710bool isAndJointlyInfeasible(
const GenericCircuit &gc,
gate_t root)
712 std::unordered_map<gate_t, Interval> rv_intervals;
713 std::unordered_map<gate_t, Interval> support_cache;
715 walkAndConjunctIntervals(gc, root, rv_intervals, support_cache, complete);
716 for (
const auto &kv : rv_intervals) {
717 if (intervalEmpty(kv.second))
return true;
760bool hasOnlyContinuousSupport(
const GenericCircuit &gc,
gate_t g,
761 std::unordered_map<gate_t, bool> &
cache)
763 auto it =
cache.find(g);
764 if (it !=
cache.
end())
return it->second;
780 result = !(tmpl && tmpl->family->factory(0.0, 0.0)->isDiscrete());
789 if (!hasOnlyContinuousSupport(gc, w,
cache)) { result =
false;
break; }
796 if (w.size() != 3) { result =
false;
break; }
797 result = hasOnlyContinuousSupport(gc, w[1],
cache)
798 && hasOnlyContinuousSupport(gc, w[2],
cache);
831const std::unordered_set<gate_t> &
832collectRandomLeaves(
const GenericCircuit &gc,
gate_t g,
833 std::unordered_map<
gate_t, std::unordered_set<gate_t>> &
cache)
835 auto it =
cache.find(g);
836 if (it !=
cache.
end())
return it->second;
843 cache.emplace(g, std::unordered_set<gate_t>{});
845 std::unordered_set<gate_t> out;
851 const auto &child = collectRandomLeaves(gc, w,
cache);
852 out.insert(child.begin(), child.end());
859 auto fit =
cache.find(g);
860 fit->second = std::move(out);
864using DiracMap = std::unordered_map<double, double>;
865using DiracMapOpt = std::optional<DiracMap>;
899collectDiracMassMap(
const GenericCircuit &gc,
gate_t g,
900 std::unordered_map<gate_t, DiracMapOpt> &
cache)
902 auto it =
cache.find(g);
903 if (it !=
cache.
end())
return it->second;
905 cache.emplace(g, std::nullopt);
914 result = std::move(m);
915 }
catch (
const CircuitException &) {
929 if (tmpl && tmpl->family->factory(0.0, 0.0)->isDiscrete())
930 result = std::nullopt;
940 for (std::size_t i = 1; i < w.size(); ++i) {
943 catch (
const CircuitException &) { ok =
false;
break; }
944 const double p = gc.
getProb(w[i]);
945 if (!std::isfinite(p) || p < 0.0 || p > 1.0) { ok =
false;
break; }
948 if (ok) result = std::move(m);
949 }
else if (w.size() == 3
951 const double pi = gc.
getProb(w[0]);
952 if (std::isfinite(pi) && pi >= 0.0 && pi <= 1.0) {
953 auto mx = collectDiracMassMap(gc, w[1],
cache);
954 auto my = collectDiracMassMap(gc, w[2],
cache);
957 for (
const auto &[v, mass] : *mx) m[v] += pi * mass;
958 for (
const auto &[v, mass] : *my) m[v] += (1.0 - pi) * mass;
959 result = std::move(m);
969 auto fit =
cache.find(g);
970 fit->second = result;
978 std::unordered_map<gate_t, Interval>
cache;
983 std::unordered_map<gate_t, bool> continuous_support_cache;
984 std::unordered_map<gate_t, DiracMapOpt> dirac_cache;
985 std::unordered_map<gate_t, std::unordered_set<gate_t>> leaf_cache;
986 unsigned resolved = 0;
994 std::vector<gate_t> cmps;
995 for (std::size_t i = 0; i < nb; ++i) {
996 auto g =
static_cast<gate_t>(i);
1008 const auto &wires = gc.
getWires(c);
1009 if (wires.size() != 2)
continue;
1018 if (wires[0] == wires[1]) {
1019 double p = std::numeric_limits<double>::quiet_NaN();
1056 bool lhs_continuous = hasOnlyContinuousSupport(gc, wires[0],
1057 continuous_support_cache);
1058 bool rhs_continuous = hasOnlyContinuousSupport(gc, wires[1],
1059 continuous_support_cache);
1060 if (lhs_continuous || rhs_continuous) {
1088 auto m_l = collectDiracMassMap(gc, wires[0], dirac_cache);
1089 auto m_r = collectDiracMassMap(gc, wires[1], dirac_cache);
1091 const auto &leaves_l = collectRandomLeaves(gc, wires[0], leaf_cache);
1092 const auto &leaves_r = collectRandomLeaves(gc, wires[1], leaf_cache);
1093 bool independent =
true;
1094 for (
gate_t leaf : leaves_l) {
1095 if (leaves_r.count(leaf)) { independent =
false;
break; }
1101 const DiracMap *small = (m_l->size() <= m_r->size()) ? &*m_l : &*m_r;
1102 const DiracMap *large = (m_l->size() <= m_r->size()) ? &*m_r : &*m_l;
1103 for (
const auto &[v, mass] : *small) {
1104 auto fit = large->find(v);
1105 if (fit != large->end()) p_eq += mass * fit->second;
1110 if (p_eq < 0.0) p_eq = 0.0;
1111 if (p_eq > 1.0) p_eq = 1.0;
1126 if (lhs_is_agg != rhs_is_agg) {
1127 gate_t agg_side = lhs_is_agg ? wires[0] : wires[1];
1128 gate_t const_side = lhs_is_agg ? wires[1] : wires[0];
1129 double const_val = extractScalarConst(gc, const_side);
1130 if (!std::isnan(const_val)) {
1131 double p = decideAggVsConstCmp(gc, agg_side, op, const_val,
1133 if (!std::isnan(p)) {
1142 Interval lhs = intervalOf(gc, wires[0],
cache);
1143 Interval rhs = intervalOf(gc, wires[1],
cache);
1146 if (lhs.isAll() && rhs.isAll())
continue;
1148 Interval diff = sub(lhs, rhs);
1149 double p = decideCmp(diff, op);
1150 if (!std::isnan(p)) {
1168 std::vector<gate_t> times_gates;
1169 for (std::size_t i = 0; i < nb_after; ++i) {
1170 auto g =
static_cast<gate_t>(i);
1172 times_gates.push_back(g);
1174 for (
gate_t t : times_gates) {
1176 if (isAndJointlyInfeasible(gc, t)) {
1222 unsigned resolved = 0;
1225 std::vector<gate_t> cmps;
1226 cmps.reserve(nb / 8);
1227 for (std::size_t i = 0; i < nb; ++i) {
1228 auto g =
static_cast<gate_t>(i);
1240 const auto &wires = gc.
getWires(c);
1241 if (wires.size() != 2)
continue;
1245 if (lhs_is_agg == rhs_is_agg)
continue;
1247 gate_t agg_side = lhs_is_agg ? wires[0] : wires[1];
1248 gate_t const_side = lhs_is_agg ? wires[1] : wires[0];
1250 double const_val = extractScalarConst(gc, const_side);
1251 if (std::isnan(const_val))
continue;
1253 bool always_true =
false;
1254 double p = decideAggVsConstCmp(gc, agg_side, op, const_val,
1255 lhs_is_agg, &always_true);
1276 std::vector<gate_t> ks;
1277 bool shape_ok =
true;
1278 ks.reserve(gc.
getWires(agg_side).size());
1282 if (sw.size() != 2) { shape_ok =
false;
break; }
1283 ks.push_back(sw[0]);
1285 if (!shape_ok || ks.empty())
continue;
1312 std::unordered_map<gate_t, Interval> &
cache)
1316 const auto &wires = gc.
getWires(root);
1317 if (wires.size() != 2)
return Interval::all();
1318 Interval vi = intervalOf(gc, wires[1],
cache);
1322 return Interval{std::min(0.0, vi.lo), std::max(0.0, vi.hi)};
1326 const auto &wires = gc.
getWires(root);
1330 std::vector<gate_t> sm_children;
1331 sm_children.reserve(wires.size());
1335 auto child_value_iv = [&](
gate_t sm) -> Interval {
1337 if (sw.size() != 2)
return Interval::all();
1338 return intervalOf(gc, sw[1],
cache);
1340 auto child_always_fires = [&](
gate_t sm) ->
bool {
1345 const auto inf = std::numeric_limits<double>::infinity();
1349 return Interval{0.0,
static_cast<double>(sm_children.size())};
1355 double lo = 0.0, hi = 0.0;
1356 for (
gate_t sm : sm_children) {
1357 Interval vi = child_value_iv(sm);
1358 if (vi.isAll())
return Interval::all();
1359 if (child_always_fires(sm)) {
1363 lo += std::min(0.0, vi.lo);
1364 hi += std::max(0.0, vi.hi);
1367 return Interval{lo, hi};
1376 if (sm_children.empty())
return Interval::all();
1379 for (
gate_t sm : sm_children) {
1380 Interval vi = child_value_iv(sm);
1381 if (vi.isAll())
return Interval::all();
1382 lo = std::min(lo, vi.lo);
1383 hi = std::max(hi, vi.hi);
1385 if (lo > hi)
return Interval::all();
1386 return Interval{lo, hi};
1392 return Interval::all();
1398std::pair<double, double>
1400 std::optional<gate_t> event_root)
1402 std::unordered_map<gate_t, Interval>
cache;
1403 Interval iv = aggSupportOf(gc, root,
cache);
1414 if (event_root.has_value()) {
1415 std::unordered_map<gate_t, Interval> rv_intervals;
1417 walkAndConjunctIntervals(gc, *event_root, rv_intervals,
cache, complete);
1418 auto it = rv_intervals.find(root);
1419 if (it != rv_intervals.end()) {
1420 iv.lo = std::max(iv.lo, it->second.lo);
1421 iv.hi = std::min(iv.hi, it->second.hi);
1424 if (iv.lo > iv.hi) iv.lo = iv.hi;
1428 return {iv.lo, iv.hi};
1431std::optional<std::pair<double, double>>
1435 std::unordered_map<gate_t, Interval> rv_intervals;
1436 std::unordered_map<gate_t, Interval> support_cache;
1438 walkAndConjunctIntervals(gc, event_root, rv_intervals, support_cache,
1440 if (!complete)
return std::nullopt;
1447 auto it = rv_intervals.find(target_rv);
1449 if (it != rv_intervals.end()) {
1453 Interval base = intervalOf(gc, target_rv, support_cache);
1454 iv.lo = std::max(iv.lo, base.lo);
1455 iv.hi = std::min(iv.hi, base.hi);
1456 if (iv.lo > iv.hi) iv.lo = iv.hi;
1458 iv = intervalOf(gc, target_rv, support_cache);
1460 return std::make_pair(iv.lo, iv.hi);
1475 const std::string &s = gc.
getExtra(x);
1476 if (s.empty())
return false;
1479 double v = std::stod(s, &idx);
1480 if (idx != s.size() || !std::isfinite(v))
return false;
1493 const std::string &s = gc.
getExtra(mul);
1494 if (s.empty())
return false;
1497 double v = std::stod(s, &idx);
1498 if (idx != s.size() || !std::isfinite(v))
return false;
1506std::optional<TruncatedSingleRv>
1508 std::optional<gate_t> event_root)
1512 if (!spec)
return std::nullopt;
1521 double nat_lo = nat_support.
lo;
1522 double nat_hi = nat_support.
hi;
1525 if (!event_root.has_value()
1538 if (!iv.has_value())
return std::nullopt;
1539 if (!(iv->first < iv->second))
return std::nullopt;
1545 std::optional<gate_t> event_root)
1547 if (!event_root.has_value())
return false;
1561 if (!iv.has_value())
return false;
1562 return !(iv->first < iv->second);
1581static std::optional<double>
1584 return std::visit([&](
const auto &v) -> std::optional<double> {
1585 using T = std::decay_t<
decltype(v)>;
1586 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1587 const double a = std::max(lo, v.lo);
1588 const double b = std::min(hi, v.hi);
1589 if (!(a < b))
return 0.0;
1590 const double cl = std::isfinite(a) ?
cdfAt(v.spec, a) : 0.0;
1591 const double ch = std::isfinite(b) ?
cdfAt(v.spec, b) : 1.0;
1592 if (std::isnan(cl) || std::isnan(ch))
return std::nullopt;
1594 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1595 return (v.value >= lo && v.value <= hi) ? 1.0 : 0.0;
1596 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1598 for (
const auto &pr : v.outcomes)
1599 if (pr.first >= lo && pr.first <= hi) m += pr.second;
1601 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1604 if (!L || !R)
return std::nullopt;
1605 return v.p * (*L) + (1.0 - v.p) * (*R);
1607 return std::nullopt;
1627static std::optional<ClosedFormShape>
1630 return std::visit([&](
const auto &v) -> std::optional<ClosedFormShape> {
1631 using T = std::decay_t<
decltype(v)>;
1632 if constexpr (std::is_same_v<T, TruncatedSingleRv>) {
1633 const double a = std::max(lo, v.lo);
1634 const double b = std::min(hi, v.hi);
1635 if (!(a < b))
return std::nullopt;
1637 }
else if constexpr (std::is_same_v<T, DiracShape>) {
1638 if (v.value < lo || v.value > hi)
return std::nullopt;
1640 }
else if constexpr (std::is_same_v<T, CategoricalShape>) {
1643 for (
const auto &pr : v.outcomes) {
1644 if (pr.first >= lo && pr.first <= hi) {
1645 out.
outcomes.emplace_back(pr.first, pr.second);
1649 if (out.
outcomes.empty() || !(total > 0.0))
return std::nullopt;
1650 for (
auto &pr : out.
outcomes) pr.second /= total;
1652 }
else if constexpr (std::is_same_v<T, BernoulliMixtureShape>) {
1655 if (!mL || !mR)
return std::nullopt;
1656 const double pL = v.p * (*mL);
1657 const double pR = (1.0 - v.p) * (*mR);
1658 const double Z = pL + pR;
1659 if (!(Z > 0.0))
return std::nullopt;
1665 if (!Lt && !Rt)
return std::nullopt;
1670 m.
left = std::make_shared<ClosedFormShape>(std::move(*Lt));
1671 m.
right = std::make_shared<ClosedFormShape>(std::move(*Rt));
1674 return std::nullopt;
1678std::optional<ClosedFormShape>
1680 std::optional<gate_t> event_root)
1684 const bool event_trivial = !event_root.has_value()
1704 if (!m)
return std::nullopt;
1713 auto with_optional_truncation =
1714 [&](std::optional<ClosedFormShape> unc)
1715 -> std::optional<ClosedFormShape> {
1716 if (!unc)
return std::nullopt;
1717 if (event_trivial)
return unc;
1719 if (!iv.has_value())
return std::nullopt;
1720 if (!(iv->first < iv->second))
return std::nullopt;
1746 for (std::size_t i = 1; i < w.size(); ++i) {
1750 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1753 if (cs.
outcomes.empty())
return std::nullopt;
1761 if (w.size() != 3)
return std::nullopt;
1764 if (!std::isfinite(p) || p < 0.0 || p > 1.0)
return std::nullopt;
1768 if (!left || !right)
return std::nullopt;
1772 m.
left = std::make_shared<ClosedFormShape>(std::move(*left));
1773 m.
right = std::make_shared<ClosedFormShape>(std::move(*right));
1777 return std::nullopt;
1803 gate_t root_gate, event_gate;
1810 std::optional<gate_t> event_opt;
1812 event_opt = event_gate;
1823 bool nulls[2] = {
false,
false};
1825 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1826 provsql_error(
"rv_support: expected composite return type");
1827 tupdesc = BlessTupleDesc(tupdesc);
1829 values[0] = Float8GetDatum(iv.first);
1830 values[1] = Float8GetDatum(iv.second);
1832 PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
1833 }
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(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.
Exact conjugate-prior posteriors for observe-evidence circuits.
Per-family polymorphic view over a continuous gate_rv distribution (§F.1 class hierarchy).
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.
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.
std::unique_ptr< Distribution > makeDistribution(const DistributionSpec &spec)
Construct the per-family Distribution for a parsed spec.
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 > conjugatePosterior(const GenericCircuit &gc, gate_t target, gate_t evidence)
The exact posterior of target given evidence, as a resolved distribution spec, when the circuit match...
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< DistributionTemplate > parse_distribution_template(const std::string &s)
Parse the on-disk text encoding of a gate_rv distribution, keeping wired (token) parameters as wire r...
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_PERCENTILE
continuous percentile (order-statistic aggregate): wires are interleaved [ind_1, x_1,...
@ PROVSQL_ARITH_DIV
binary, child0 / child1
@ PROVSQL_ARITH_LN
unary, natural logarithm of child0 (a negative draw raises at evaluation)
@ PROVSQL_ARITH_PLUS
n-ary, sum of children
@ PROVSQL_ARITH_POW
binary, child0 ^ child1 (real branch only: a negative base drawn with a non-integer exponent raises a...
@ PROVSQL_ARITH_NEG
unary, -child0
@ PROVSQL_ARITH_MINUS
binary, child0 - child1
@ PROVSQL_ARITH_EXP
unary, e^child0
@ PROVSQL_ARITH_TIMES
n-ary, product of children
@ PROVSQL_ARITH_MIN
n-ary, min of children (order statistic; least / min aggregate)
@ PROVSQL_ARITH_MAX
n-ary, max of children (order statistic; greatest / max aggregate)
@ gate_rv
Continuous random-variable leaf (extra encodes distribution).
@ gate_case
N-ary guarded selection over scalar (RV) children: wires are [guard_1, value_1, .....
@ 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...
A closed support interval [lo, hi] (±infinity for unbounded).
Detection result for a closed-form, optionally-truncated single-RV shape.