22#include <unordered_map>
23#include <unordered_set>
35 std::random_device rd;
36 rng.seed((
static_cast<uint64_t
>(rd()) << 32) | rd());
66bool matchPointObservationCmp(
const GenericCircuit &gc,
gate_t g,
67 gate_t &leaf_out,
double &datum_out)
71 if(wires.size() != 2)
return false;
79 catch(
const CircuitException &) {
return false; }
83 return try_side(wires[0], wires[1]) || try_side(wires[1], wires[0]);
90 Sampler(
const GenericCircuit &gc, std::mt19937_64 &rng)
91 : gc_(gc), rng_(rng) {}
94 void resetIteration() {
96 scalar_cache_.clear();
100 double evalScalar(
gate_t g);
101 double evalWeight(
gate_t g);
105 void fixInputs(
const std::unordered_map<gate_t, bool> *world) {
114 std::unique_ptr<Distribution> buildRvDistribution(
115 gate_t leaf,
const DistributionTemplate &tmpl,
double &p1,
double &p2);
117 const GenericCircuit &gc_;
118 std::mt19937_64 &rng_;
119 std::unordered_map<gate_t, bool> bool_cache_;
120 std::unordered_map<gate_t, double> scalar_cache_;
121 const std::unordered_map<gate_t, bool> *fixed_ =
nullptr;
125 std::unordered_map<gate_t, std::unique_ptr<Distribution>> dist_cache_;
128bool Sampler::evalBool(
gate_t g)
130 auto it = bool_cache_.find(g);
131 if(it != bool_cache_.end())
return it->second;
135 const auto &wires = gc_.
getWires(g);
141 if(fixed_ !=
nullptr) {
142 result = fixed_->at(g);
145 std::uniform_real_distribution<double> u(0.0, 1.0);
146 result = u(rng_) < gc_.
getProb(g);
152 if(evalBool(c)) { result =
true;
break; }
158 if(!evalBool(c)) { result =
false;
break; }
162 if(wires.size() != 2)
163 throw CircuitException(
"gate_monus must have exactly two children");
164 result = evalBool(wires[0]) && !evalBool(wires[1]);
174 if(wires.size() != 2)
175 throw CircuitException(
"gate_cmp must have exactly two children");
179 throw CircuitException(
180 "gate_cmp: unsupported operator OID " +
181 std::to_string(gc_.
getInfos(g).first));
182 double l = evalScalar(wires[0]);
183 double r = evalScalar(wires[1]);
184 result = applyCmp(l, op, r);
188 throw CircuitException(
189 "Monte Carlo over circuits containing gate_mulinput "
190 "is not yet supported on the RV path");
197 if(wires.size() != 1)
198 throw CircuitException(
"gate_delta must have exactly one child");
199 result = evalBool(wires[0]);
206 if(wires.size() != 1)
207 throw CircuitException(
208 "gate_assumed must have exactly one child");
209 result = evalBool(wires[0]);
214 if(wires.size() != 1)
215 throw CircuitException(
"gate_annotation must have exactly one child");
216 result = evalBool(wires[0]);
219 throw CircuitException(
220 "Unsupported gate type in Boolean evaluation: " +
224 bool_cache_[g] = result;
228double Sampler::evalScalar(
gate_t g)
230 auto it = scalar_cache_.find(g);
231 if(it != scalar_cache_.end())
return it->second;
235 const auto &wires = gc_.
getWires(g);
242 result = std::numeric_limits<double>::quiet_NaN();
253 auto dit = dist_cache_.find(g);
254 if(dit != dist_cache_.end()) {
255 result = dit->second->sample(rng_);
260 throw CircuitException(
261 "Malformed gate_rv extra: " + gc_.
getExtra(g));
262 if(!tmpl->parametric()) {
263 DistributionSpec spec{tmpl->family, tmpl->p1.literal, tmpl->p2.literal};
265 result = dit->second->sample(rng_);
275 result = buildRvDistribution(g, *tmpl, p1, p2)->sample(rng_);
281 throw CircuitException(
"gate_arith must have at least one child");
286 for(
gate_t c : wires) result += evalScalar(c);
290 for(
gate_t c : wires) result *= evalScalar(c);
293 if(wires.size() != 2)
294 throw CircuitException(
"gate_arith MINUS must be binary");
295 result = evalScalar(wires[0]) - evalScalar(wires[1]);
300 if(wires.size() != 2)
301 throw CircuitException(
302 std::string(
"gate_arith ")
304 +
" must be binary");
305 const double num = evalScalar(wires[0]);
306 const double den = evalScalar(wires[1]);
318 result = std::numeric_limits<double>::quiet_NaN();
320 result = std::trunc(num / den);
326 if(wires.size() != 1)
327 throw CircuitException(
"gate_arith NEG must be unary");
328 result = -evalScalar(wires[0]);
334 result = evalScalar(wires[0]);
335 for(std::size_t i = 1; i < wires.size(); ++i)
336 result = std::max(result, evalScalar(wires[i]));
339 result = evalScalar(wires[0]);
340 for(std::size_t i = 1; i < wires.size(); ++i)
341 result = std::min(result, evalScalar(wires[i]));
345 if(wires.size() != 2)
346 throw CircuitException(
"gate_arith POW must be binary");
347 const double base = evalScalar(wires[0]);
348 const double expo = evalScalar(wires[1]);
349 result = std::pow(base, expo);
357 if(std::isnan(result) && !std::isnan(base) && !std::isnan(expo))
358 throw CircuitException(
359 "pow: negative base drawn with a non-integer exponent ("
360 + std::to_string(base) +
" ^ " + std::to_string(expo)
361 +
"); restrict the base to be non-negative, e.g. "
362 "pow(greatest(x, 0), p)");
367 if(wires.size() != 1)
368 throw CircuitException(
"gate_arith LN must be unary");
369 const double x = evalScalar(wires[0]);
376 throw CircuitException(
377 "ln: negative draw (" + std::to_string(x)
378 +
"); ln is only defined on [0, +Infinity) -- "
379 "restrict the argument's support");
380 result = std::log(x);
384 if(wires.size() != 1)
385 throw CircuitException(
"gate_arith EXP must be unary");
386 result = std::exp(evalScalar(wires[0]));
395 if(wires.empty() || wires.size() > 2)
396 throw CircuitException(
"gate_arith ROUND takes one or two wires");
397 const double x = evalScalar(wires[0]);
398 if(wires.size() == 1) {
399 result = std::round(x);
401 const double d = evalScalar(wires[1]);
402 if(std::isnan(x) || std::isnan(d)) { result = x + d;
break; }
403 const double f = std::pow(10.0, d);
404 result = std::round(x * f) / f;
409 if(wires.size() != 1)
410 throw CircuitException(
"gate_arith FLOOR must be unary");
411 result = std::floor(evalScalar(wires[0]));
414 if(wires.size() != 1)
415 throw CircuitException(
"gate_arith CEIL must be unary");
416 result = std::ceil(evalScalar(wires[0]));
419 if(wires.size() != 1)
420 throw CircuitException(
"gate_arith ABS must be unary");
421 result = std::fabs(evalScalar(wires[0]));
424 if(wires.size() != 1)
425 throw CircuitException(
"gate_arith ASFLOAT8 must be unary");
426 result = evalScalar(wires[0]);
429 if(wires.size() != 1)
430 throw CircuitException(
"gate_arith ASFLOAT4 must be unary");
431 result =
static_cast<double>(
432 static_cast<float>(evalScalar(wires[0])));
443 if(wires.size() < 2 || wires.size() % 2 != 0)
444 throw CircuitException(
445 "gate_arith PERCENTILE must have interleaved "
446 "indicator/value wires");
449 fraction = std::stod(gc_.
getExtra(g));
450 }
catch(
const std::exception &) {
451 throw CircuitException(
452 "Malformed gate_arith PERCENTILE extra (expected the "
455 std::vector<double> members;
456 bool has_nan =
false;
457 for(std::size_t i = 0; i < wires.size(); i += 2) {
458 if(evalScalar(wires[i]) >= 0.5) {
459 const double x = evalScalar(wires[i + 1]);
463 members.push_back(x);
466 if(has_nan || members.empty()) {
467 result = std::numeric_limits<double>::quiet_NaN();
470 std::sort(members.begin(), members.end());
471 const double pos = fraction * (members.size() - 1);
472 const std::size_t lo =
static_cast<std::size_t
>(pos);
473 const double frac = pos -
static_cast<double>(lo);
474 result = (lo + 1 < members.size())
475 ? members[lo] + frac * (members[lo + 1] - members[lo])
480 throw CircuitException(
481 "Unknown gate_arith operator tag: " +
482 std::to_string(
static_cast<unsigned>(op)));
507 std::unique_ptr<Aggregator> agg =
512 throw CircuitException(
513 "gate_agg: makeAggregator returned null for op " +
514 std::to_string(
static_cast<int>(op)));
515 for(
gate_t child : wires) {
517 const auto &sm = gc_.
getWires(child);
518 if(sm.size() != 2)
continue;
519 if(!evalBool(sm[0]))
continue;
521 agg->add(AggValue(
static_cast<long>(evalScalar(sm[1]))));
523 agg->add(AggValue(evalScalar(sm[1])));
526 AggValue r = agg->finalize();
529 result =
static_cast<double>(std::get<long>(r.
v));
532 result = std::get<double>(r.
v);
562 : std::numeric_limits<double>::quiet_NaN();
565 throw CircuitException(
566 "gate_agg: unsupported aggregate result ValueType in MC");
580 const auto &wires = gc_.
getWires(g);
581 if(wires.size() != 2)
582 throw CircuitException(
583 "gate_semimod must have exactly two children "
584 "[k_gate, value_gate]");
585 result = evalBool(wires[0]) ? evalScalar(wires[1]) : 0.0;
609 std::uniform_real_distribution<double> u(0.0, 1.0);
610 const double r = u(rng_);
614 std::size_t chosen = wires.size() - 1;
615 for(std::size_t i = 1; i < wires.size(); ++i) {
617 if(r < cum) { chosen = i;
break; }
619 for(std::size_t i = 1; i < wires.size(); ++i) {
620 bool_cache_[wires[i]] = (i == chosen);
625 if(wires.size() != 3)
626 throw CircuitException(
627 "gate_mixture must have exactly three children "
628 "[p_token, x_token, y_token]");
629 result = evalBool(wires[0]) ? evalScalar(wires[1])
630 : evalScalar(wires[2]);
641 const auto &wires = gc_.
getWires(g);
643 throw CircuitException(
644 "gate_case must have at least one child (the default)");
645 const std::size_t k = wires.size() / 2;
646 bool matched =
false;
647 for(std::size_t i = 0; i < k; ++i) {
648 if(evalBool(wires[2 * i])) {
649 result = evalScalar(wires[2 * i + 1]);
655 result = evalScalar(wires.back());
659 throw CircuitException(
660 "Unsupported gate type in scalar evaluation: " +
664 scalar_cache_[g] = result;
668std::unique_ptr<Distribution> Sampler::buildRvDistribution(
669 gate_t leaf,
const DistributionTemplate &tmpl,
double &p1,
double &p2)
672 auto resolve = [&](
const DistributionParam &p) {
673 return p.wire_slot < 0 ? p.literal : evalScalar(w[p.wire_slot]);
675 p1 = resolve(tmpl.p1);
676 p2 = resolve(tmpl.p2);
677 auto dist = tmpl.family->factory(p1, p2);
686 if(!dist->integrationRange(dlo, dhi))
687 throw CircuitException(
688 "gate_rv " + std::string(tmpl.family->name)
689 +
": a parameter drawn outside the family's domain "
690 "(e.g. a scale/rate/shape <= 0: got "
691 + std::to_string(p1) +
", " + std::to_string(p2)
692 +
"); put a positive-support prior on it, e.g. "
693 "gamma / lognormal");
697double Sampler::evalWeight(
gate_t g)
700 const auto &wires = gc_.
getWires(g);
709 if(w == 0.0)
return 0.0;
718 if(wires.size() != 1)
719 throw CircuitException(
720 "gate_observe must have exactly one child (the observed leaf)");
721 const gate_t leaf = wires[0];
723 throw CircuitException(
724 "gate_observe child must be a gate_rv leaf");
728 throw CircuitException(
729 "gate_observe: malformed observed gate_rv extra: "
732 return buildRvDistribution(leaf, *tmpl, p1, p2)->pdf(d);
742 if(matchPointObservationCmp(gc_, g, leaf, datum)) {
746 return buildRvDistribution(leaf, *tmpl, p1, p2)->pdf(datum);
749 return evalBool(g) ? 1.0 : 0.0;
756 return evalBool(g) ? 1.0 : 0.0;
764 std::mt19937_64 rng =
seedRng();
765 Sampler sampler(gc, rng);
767 unsigned success = 0;
768 for(
unsigned i = 0; i < samples; ++i) {
769 sampler.resetIteration();
770 if(sampler.evalBool(root))
775 "Interrupted after " + std::to_string(i + 1) +
" samples");
777 return success * 1.0 / samples;
781 double eps,
double delta,
782 unsigned long max_samples,
783 unsigned long &samples_used,
784 bool &reached_target)
787 reached_target =
false;
795 const double e = std::exp(1.0);
796 const double Y = 4.0 * (e - 2.0) * std::log(2.0 / delta) / (eps * eps);
797 const double Y1 = 1.0 + (1.0 + eps) * Y;
799 std::mt19937_64 rng =
seedRng();
800 Sampler sampler(gc, rng);
802 unsigned long success = 0;
803 for(
unsigned long s = 0; s < max_samples; ++s) {
804 sampler.resetIteration();
805 if(sampler.evalBool(root)) {
807 if(
static_cast<double>(success) >= Y1) {
808 samples_used = s + 1;
809 reached_target =
true;
810 return Y1 /
static_cast<double>(samples_used);
815 "Interrupted after " + std::to_string(s + 1) +
" samples");
820 samples_used = max_samples;
821 return static_cast<double>(success) /
static_cast<double>(max_samples);
826 const std::vector<gate_t> &cmps,
829 const unsigned k = cmps.size();
832 "monteCarloJointDistribution: empty cmps list");
835 "monteCarloJointDistribution: too many cmps in island ("
836 + std::to_string(k) +
" > 30)");
838 std::mt19937_64 rng =
seedRng();
839 Sampler sampler(gc, rng);
841 const std::size_t nb_outcomes = std::size_t{1} << k;
842 std::vector<unsigned> counts(nb_outcomes, 0);
844 for (
unsigned i = 0; i < samples; ++i) {
845 sampler.resetIteration();
847 for (
unsigned j = 0; j < k; ++j) {
848 if (sampler.evalBool(cmps[j])) w |= (std::size_t{1} << j);
853 "Interrupted after " + std::to_string(i + 1) +
" samples");
856 std::vector<double> probs(nb_outcomes);
857 for (std::size_t w = 0; w < nb_outcomes; ++w)
858 probs[w] = counts[w] * 1.0 / samples;
865 std::mt19937_64 rng =
seedRng();
866 Sampler sampler(gc, rng);
868 std::vector<double> out;
869 out.reserve(samples);
870 for(
unsigned i = 0; i < samples; ++i) {
871 sampler.resetIteration();
872 out.push_back(sampler.evalScalar(root));
876 "Interrupted after " + std::to_string(i + 1) +
" samples");
884static std::optional<std::vector<gate_t>>
886 std::optional<gate_t> event,
unsigned max_inputs)
888 std::vector<gate_t> inputs;
889 std::unordered_set<gate_t> seen;
890 std::stack<gate_t> todo;
892 if(event) todo.push(*event);
893 while(!todo.empty()) {
896 if(!seen.insert(g).second)
continue;
900 if(std::isnan(gc.
getProb(g)))
return std::nullopt;
911 if(inputs.size() > max_inputs)
return std::nullopt;
922 if(!inputs_opt)
return std::nullopt;
923 const std::vector<gate_t> &inputs = *inputs_opt;
926 Sampler sampler(gc, rng);
927 std::unordered_map<gate_t, bool> world;
928 sampler.fixInputs(&world);
930 const std::uint64_t n_worlds = std::uint64_t(1) << inputs.size();
931 for(std::uint64_t w = 0; w < n_worlds; ++w) {
933 for(std::size_t i = 0; i < inputs.size(); ++i) {
934 const bool present = (w >> i) & 1;
935 const double pi = gc.
getProb(inputs[i]);
936 world[inputs[i]] = present;
937 p *= present ? pi : 1.0 - pi;
939 if(p == 0.0)
continue;
940 sampler.resetIteration();
941 if(sampler.evalBool(root))
951std::optional<std::vector<std::pair<double, double>>>
953 std::optional<gate_t> event,
unsigned max_inputs)
956 if(!inputs_opt)
return std::nullopt;
957 const std::vector<gate_t> &inputs = *inputs_opt;
960 Sampler sampler(gc, rng);
961 std::unordered_map<gate_t, bool> world;
962 sampler.fixInputs(&world);
963 std::vector<std::pair<double, double>> out;
964 const std::uint64_t n_worlds = std::uint64_t(1) << inputs.size();
965 for(std::uint64_t w = 0; w < n_worlds; ++w) {
967 for(std::size_t i = 0; i < inputs.size(); ++i) {
968 const bool present = (w >> i) & 1;
969 const double pi = gc.
getProb(inputs[i]);
970 world[inputs[i]] = present;
971 p *= present ? pi : 1.0 - pi;
973 if(p == 0.0)
continue;
974 sampler.resetIteration();
975 if(event && !sampler.evalBool(*event))
continue;
976 out.emplace_back(p, sampler.evalScalar(root));
985std::pair<std::vector<double>, std::vector<double>>
987 gate_t root_b,
unsigned samples)
989 std::mt19937_64 rng =
seedRng();
990 Sampler sampler(gc, rng);
992 std::vector<double> out_a, out_b;
993 out_a.reserve(samples);
994 out_b.reserve(samples);
995 for(
unsigned i = 0; i < samples; ++i) {
996 sampler.resetIteration();
1002 out_a.push_back(sampler.evalScalar(root_a));
1003 out_b.push_back(sampler.evalScalar(root_b));
1007 "Interrupted after " + std::to_string(i + 1) +
" samples");
1009 return {std::move(out_a), std::move(out_b)};
1015 std::mt19937_64 rng =
seedRng();
1016 Sampler sampler(gc, rng);
1022 for(
unsigned i = 0; i < samples; ++i) {
1023 sampler.resetIteration();
1030 if(sampler.evalBool(event_root)) {
1031 out.
accepted.push_back(sampler.evalScalar(root));
1037 "Interrupted after " + std::to_string(i + 1) +
" samples");
1044 gate_t event_root,
unsigned samples)
1046 std::mt19937_64 rng =
seedRng();
1047 Sampler sampler(gc, rng);
1051 out.
xs.reserve(samples);
1052 out.
ys.reserve(samples);
1054 for(
unsigned i = 0; i < samples; ++i) {
1055 sampler.resetIteration();
1059 if(sampler.evalBool(event_root)) {
1060 out.
xs.push_back(sampler.evalScalar(root_a));
1061 out.
ys.push_back(sampler.evalScalar(root_b));
1067 "Interrupted after " + std::to_string(i + 1) +
" samples");
1072std::optional<std::vector<double>>
1074 gate_t event_root,
unsigned n)
1077 if (!m)
return std::nullopt;
1083 std::mt19937_64 rng =
seedRng();
1090 std::mt19937_64 rng =
seedRng();
1091 Sampler sampler(gc, rng);
1096 for(
unsigned i = 0; i < samples; ++i) {
1097 sampler.resetIteration();
1101 const double w = sampler.evalWeight(evidence);
1104 const double x = sampler.evalScalar(root);
1115 "Interrupted after " + std::to_string(i + 1) +
" samples");
1123 if(samples == 0)
return 0.0;
1124 std::mt19937_64 rng =
seedRng();
1125 Sampler sampler(gc, rng);
1128 for(
unsigned i = 0; i < samples; ++i) {
1129 sampler.resetIteration();
1130 sw += sampler.evalWeight(evidence);
1133 "Interrupted after " + std::to_string(i + 1) +
" samples");
1135 return sw /
static_cast<double>(samples);
1141 std::vector<double> out;
1144 std::vector<double> cum;
1147 for(
const auto &pw : post.
particles) { c += pw.second; cum.push_back(c); }
1149 std::mt19937_64 rng =
seedRng();
1150 std::uniform_real_distribution<double> u(0.0, c);
1152 for(
unsigned i = 0; i < n; ++i) {
1153 const double r = u(rng);
1154 auto it = std::lower_bound(cum.begin(), cum.end(), r);
1155 std::size_t idx =
static_cast<std::size_t
>(it - cum.begin());
1157 out.push_back(post.
particles[idx].first);
1164 std::unordered_set<gate_t> seen;
1165 std::stack<gate_t> stack;
1167 while(!stack.empty()) {
1170 if(!seen.insert(g).second)
continue;
1178 || matchPointObservationCmp(gc, g, leaf, datum))
1187 std::unordered_set<gate_t> seen;
1188 std::stack<gate_t> stack;
1190 while(!stack.empty()) {
1193 if(!seen.insert(g).second)
continue;
1224 std::unordered_set<gate_t> seen;
1225 std::stack<gate_t> stack;
1228 while(!stack.empty()) {
1231 if(!seen.insert(g).second)
continue;
1260 std::unordered_set<gate_t> seen;
1261 std::stack<gate_t> stack;
1263 while(!stack.empty()) {
1266 if(!seen.insert(g).second)
continue;
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.
std::unique_ptr< Aggregator > makeAggregator(AggregationOperator op, ValueType t)
Create a concrete Aggregator for the given operator and value type.
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 (>=).
@ INT
Signed 64-bit integer.
@ FLOAT
Double-precision float.
Generic directed-acyclic-graph circuit template and gate identifier.
gate_t
Strongly-typed gate identifier.
Per-family polymorphic view over a continuous gate_rv distribution (§F.1 class hierarchy).
Monte Carlo sampling over a GenericCircuit, RV-aware.
Continuous random-variable helpers (distribution parsing, moments).
Support-based bound check for continuous-RV comparators.
Exception type thrown by circuit operations on invalid input.
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.
uuid getUUID(gate_t g) const
Return the UUID string associated with gate g.
In-memory provenance circuit with semiring-generic evaluation.
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.
std::pair< unsigned, unsigned > getInfos(gate_t g) const
Return the integer annotation pair for gate g.
std::optional< std::vector< std::pair< double, double > > > enumerateScalarWorlds(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event, unsigned max_inputs)
The possible worlds of a circuit whose only random sources are Boolean inputs: the exact alternative ...
double importanceEvidence(const GenericCircuit &gc, gate_t evidence, unsigned samples)
Marginal likelihood P(data) of evidence: the mean raw importance weight over samples prior draws.
std::optional< double > enumerateBooleanProbability(const GenericCircuit &gc, gate_t root, unsigned max_inputs)
The probability of root, exactly, over the possible worlds of the circuit as it is: the alternative t...
std::pair< std::vector< double >, std::vector< double > > monteCarloScalarPairSamples(const GenericCircuit &gc, gate_t root_a, gate_t root_b, unsigned samples)
Coupled per-iteration draws of two scalar roots.
std::vector< double > posteriorResample(const WeightedPosterior &post, unsigned n)
Sampling-importance-resampling: draw n posterior samples from a weighted particle set (proportional t...
double parseDoubleStrict(const std::string &s)
Strictly parse s as a double.
std::vector< double > monteCarloJointDistribution(const GenericCircuit &gc, const std::vector< gate_t > &cmps, unsigned samples)
Estimate the joint distribution of cmps via Monte Carlo.
bool circuitHasNestedAggValue(const GenericCircuit &gc, gate_t root)
Whether a contribution of an aggregate is itself an aggregate result (a gate_semimod whose M side is ...
double monteCarloRVStopping(const GenericCircuit &gc, gate_t root, double eps, double delta, unsigned long max_samples, unsigned long &samples_used, bool &reached_target)
Whole-circuit (eps,delta)-relative probability via the Dagum-Karp-Luby-Ross stopping rule.
std::unique_ptr< Distribution > makeDistribution(const DistributionSpec &spec)
Construct the per-family Distribution for a parsed spec.
std::mt19937_64 seedRng()
The shared Monte Carlo generator, seeded from the provsql.monte_carlo_seed GUC (-1 = non-deterministi...
bool circuitHasUnresolvedSampleableAgg(const GenericCircuit &gc, gate_t root)
Whether a surviving gate_agg exists and every one is sample-faithful (SUM / AVG / MIN / MAX / COUNT –...
static std::optional< std::vector< gate_t > > enumerableInputs(const GenericCircuit &gc, gate_t root, std::optional< gate_t > event, unsigned max_inputs)
The Boolean inputs the worlds of root (and event) range over, or nullopt when another random source,...
ConditionalScalarSamples monteCarloConditionalScalarSamples(const GenericCircuit &gc, gate_t root, gate_t event_root, unsigned samples)
Rejection-sample root conditioned on event_root.
std::vector< double > monteCarloScalarSamples(const GenericCircuit &gc, gate_t root, unsigned samples)
Sample a scalar sub-circuit samples times and return the draws.
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...
ConditionalScalarPairSamples monteCarloConditionalScalarPairSamples(const GenericCircuit &gc, gate_t root_a, gate_t root_b, gate_t event_root, unsigned samples)
Rejection-sample the PAIR (root_a, root_b) conditioned on event_root.
double monteCarloRV(const GenericCircuit &gc, gate_t root, unsigned samples)
Run Monte Carlo on a circuit that may contain gate_rv leaves.
std::optional< std::vector< double > > try_truncated_closed_form_sample(const GenericCircuit &gc, gate_t root, gate_t event_root, unsigned n)
Try to draw n exact samples from the conditional distribution of root given event_root via closed-for...
WeightedPosterior importanceSampleConditional(const GenericCircuit &gc, gate_t root, gate_t evidence, unsigned samples)
Self-normalised importance sampling of root given evidence.
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.
bool circuitHasRV(const GenericCircuit &gc, gate_t root)
Walk the circuit reachable from root looking for any gate_rv.
bool circuitHasObserve(const GenericCircuit &gc, gate_t root)
Whether the circuit reachable from root contains a gate_observe – the signal that a conditioning even...
int provsql_monte_carlo_seed
Seed for the Monte Carlo sampler; -1 means non-deterministic (std::random_device); controlled by the ...
bool provsql_interrupted
Global variable that becomes true if this particular backend received an interrupt signal.
const char * gate_type_name[]
Names of gate types.
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_ASFLOAT8
unary, child0 as double precision
@ PROVSQL_ARITH_LN
unary, natural logarithm of child0 (a negative draw raises at evaluation)
@ PROVSQL_ARITH_ROUND
child0 rounded half away from zero, to child1 decimal digits where a second child is given (SQL round...
@ 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_ABS
unary, |child0|
@ PROVSQL_ARITH_FLOOR
unary, greatest integer <= child0
@ PROVSQL_ARITH_NEG
unary, -child0
@ PROVSQL_ARITH_INTDIV
binary, child0 / child1 truncated toward zero: SQL's division of two integers
@ PROVSQL_ARITH_MINUS
binary, child0 - child1
@ PROVSQL_ARITH_CEIL
unary, least integer >= child0
@ 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)
@ PROVSQL_ARITH_ASFLOAT4
unary, child0 as real reads it
@ gate_observe
Latent-variable observation (likelihood-weighting evidence): one wire → an observed bare gate_rv leaf...
@ 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_annotation
Transparent single-child wrapper carrying a query-level annotation in extra (inversion-free certifica...
@ 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)
@ gate_assumed
Structural marker over a single child whose sub-circuit was computed under a Boolean-provenance assum...
#define PROVSQL_GATE_NULL_UUID
UUID of the constant value gate standing for the NULL value, the result of gate_null().
#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...
ValueType getType() const
Return the runtime type tag of this value.
std::variant< long, double, bool, std::string, std::vector< long >, std::vector< double >, std::vector< bool >, std::vector< std::string > > v
The variant holding the actual value.
Outcome of a conditional coupled-pair Monte Carlo pass: xs[i] / ys[i] are the two roots' values from ...
Outcome of a conditional Monte Carlo sampling pass.
std::vector< double > accepted
Outcome of a likelihood-weighting (importance-sampling) pass.
double weight_sq_sum
Sum of w^2 over all attempted draws.
unsigned attempted
Number of prior draws.
std::vector< std::pair< double, double > > particles
(x, w) with w > 0.
double weight_sum
Sum of w over all attempted draws.