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);
108 std::unique_ptr<Distribution> buildRvDistribution(
109 gate_t leaf,
const DistributionTemplate &tmpl,
double &p1,
double &p2);
111 const GenericCircuit &gc_;
112 std::mt19937_64 &rng_;
113 std::unordered_map<gate_t, bool> bool_cache_;
114 std::unordered_map<gate_t, double> scalar_cache_;
118 std::unordered_map<gate_t, std::unique_ptr<Distribution>> dist_cache_;
121bool Sampler::evalBool(
gate_t g)
123 auto it = bool_cache_.find(g);
124 if(it != bool_cache_.end())
return it->second;
128 const auto &wires = gc_.
getWires(g);
134 std::uniform_real_distribution<double> u(0.0, 1.0);
135 result = u(rng_) < gc_.
getProb(g);
141 if(evalBool(c)) { result =
true;
break; }
147 if(!evalBool(c)) { result =
false;
break; }
151 if(wires.size() != 2)
152 throw CircuitException(
"gate_monus must have exactly two children");
153 result = evalBool(wires[0]) && !evalBool(wires[1]);
163 if(wires.size() != 2)
164 throw CircuitException(
"gate_cmp must have exactly two children");
168 throw CircuitException(
169 "gate_cmp: unsupported operator OID " +
170 std::to_string(gc_.
getInfos(g).first));
171 double l = evalScalar(wires[0]);
172 double r = evalScalar(wires[1]);
173 result = applyCmp(l, op, r);
177 throw CircuitException(
178 "Monte Carlo over circuits containing gate_mulinput "
179 "is not yet supported on the RV path");
186 if(wires.size() != 1)
187 throw CircuitException(
"gate_delta must have exactly one child");
188 result = evalBool(wires[0]);
195 if(wires.size() != 1)
196 throw CircuitException(
197 "gate_assumed must have exactly one child");
198 result = evalBool(wires[0]);
203 if(wires.size() != 1)
204 throw CircuitException(
"gate_annotation must have exactly one child");
205 result = evalBool(wires[0]);
208 throw CircuitException(
209 "Unsupported gate type in Boolean evaluation: " +
213 bool_cache_[g] = result;
217double Sampler::evalScalar(
gate_t g)
219 auto it = scalar_cache_.find(g);
220 if(it != scalar_cache_.end())
return it->second;
224 const auto &wires = gc_.
getWires(g);
237 auto dit = dist_cache_.find(g);
238 if(dit != dist_cache_.end()) {
239 result = dit->second->sample(rng_);
244 throw CircuitException(
245 "Malformed gate_rv extra: " + gc_.
getExtra(g));
246 if(!tmpl->parametric()) {
247 DistributionSpec spec{tmpl->family, tmpl->p1.literal, tmpl->p2.literal};
249 result = dit->second->sample(rng_);
259 result = buildRvDistribution(g, *tmpl, p1, p2)->sample(rng_);
265 throw CircuitException(
"gate_arith must have at least one child");
270 for(
gate_t c : wires) result += evalScalar(c);
274 for(
gate_t c : wires) result *= evalScalar(c);
277 if(wires.size() != 2)
278 throw CircuitException(
"gate_arith MINUS must be binary");
279 result = evalScalar(wires[0]) - evalScalar(wires[1]);
282 if(wires.size() != 2)
283 throw CircuitException(
"gate_arith DIV must be binary");
284 result = evalScalar(wires[0]) / evalScalar(wires[1]);
287 if(wires.size() != 1)
288 throw CircuitException(
"gate_arith NEG must be unary");
289 result = -evalScalar(wires[0]);
295 result = evalScalar(wires[0]);
296 for(std::size_t i = 1; i < wires.size(); ++i)
297 result = std::max(result, evalScalar(wires[i]));
300 result = evalScalar(wires[0]);
301 for(std::size_t i = 1; i < wires.size(); ++i)
302 result = std::min(result, evalScalar(wires[i]));
306 if(wires.size() != 2)
307 throw CircuitException(
"gate_arith POW must be binary");
308 const double base = evalScalar(wires[0]);
309 const double expo = evalScalar(wires[1]);
310 result = std::pow(base, expo);
318 if(std::isnan(result) && !std::isnan(base) && !std::isnan(expo))
319 throw CircuitException(
320 "pow: negative base drawn with a non-integer exponent ("
321 + std::to_string(base) +
" ^ " + std::to_string(expo)
322 +
"); restrict the base to be non-negative, e.g. "
323 "pow(greatest(x, 0), p)");
328 if(wires.size() != 1)
329 throw CircuitException(
"gate_arith LN must be unary");
330 const double x = evalScalar(wires[0]);
337 throw CircuitException(
338 "ln: negative draw (" + std::to_string(x)
339 +
"); ln is only defined on [0, +Infinity) -- "
340 "restrict the argument's support");
341 result = std::log(x);
345 if(wires.size() != 1)
346 throw CircuitException(
"gate_arith EXP must be unary");
347 result = std::exp(evalScalar(wires[0]));
358 if(wires.size() < 2 || wires.size() % 2 != 0)
359 throw CircuitException(
360 "gate_arith PERCENTILE must have interleaved "
361 "indicator/value wires");
364 fraction = std::stod(gc_.
getExtra(g));
365 }
catch(
const std::exception &) {
366 throw CircuitException(
367 "Malformed gate_arith PERCENTILE extra (expected the "
370 std::vector<double> members;
371 bool has_nan =
false;
372 for(std::size_t i = 0; i < wires.size(); i += 2) {
373 if(evalScalar(wires[i]) >= 0.5) {
374 const double x = evalScalar(wires[i + 1]);
378 members.push_back(x);
381 if(has_nan || members.empty()) {
382 result = std::numeric_limits<double>::quiet_NaN();
385 std::sort(members.begin(), members.end());
386 const double pos = fraction * (members.size() - 1);
387 const std::size_t lo =
static_cast<std::size_t
>(pos);
388 const double frac = pos -
static_cast<double>(lo);
389 result = (lo + 1 < members.size())
390 ? members[lo] + frac * (members[lo + 1] - members[lo])
395 throw CircuitException(
396 "Unknown gate_arith operator tag: " +
397 std::to_string(
static_cast<unsigned>(op)));
422 std::unique_ptr<Aggregator> agg =
427 throw CircuitException(
428 "gate_agg: makeAggregator returned null for op " +
429 std::to_string(
static_cast<int>(op)));
430 for(
gate_t child : wires) {
432 const auto &sm = gc_.
getWires(child);
433 if(sm.size() != 2)
continue;
434 if(!evalBool(sm[0]))
continue;
436 agg->add(AggValue(
static_cast<long>(evalScalar(sm[1]))));
438 agg->add(AggValue(evalScalar(sm[1])));
441 AggValue r = agg->finalize();
444 result =
static_cast<double>(std::get<long>(r.
v));
447 result = std::get<double>(r.
v);
477 : std::numeric_limits<double>::quiet_NaN();
480 throw CircuitException(
481 "gate_agg: unsupported aggregate result ValueType in MC");
495 const auto &wires = gc_.
getWires(g);
496 if(wires.size() != 2)
497 throw CircuitException(
498 "gate_semimod must have exactly two children "
499 "[k_gate, value_gate]");
500 result = evalBool(wires[0]) ? evalScalar(wires[1]) : 0.0;
524 std::uniform_real_distribution<double> u(0.0, 1.0);
525 const double r = u(rng_);
529 std::size_t chosen = wires.size() - 1;
530 for(std::size_t i = 1; i < wires.size(); ++i) {
532 if(r < cum) { chosen = i;
break; }
534 for(std::size_t i = 1; i < wires.size(); ++i) {
535 bool_cache_[wires[i]] = (i == chosen);
540 if(wires.size() != 3)
541 throw CircuitException(
542 "gate_mixture must have exactly three children "
543 "[p_token, x_token, y_token]");
544 result = evalBool(wires[0]) ? evalScalar(wires[1])
545 : evalScalar(wires[2]);
556 const auto &wires = gc_.
getWires(g);
558 throw CircuitException(
559 "gate_case must have at least one child (the default)");
560 const std::size_t k = wires.size() / 2;
561 bool matched =
false;
562 for(std::size_t i = 0; i < k; ++i) {
563 if(evalBool(wires[2 * i])) {
564 result = evalScalar(wires[2 * i + 1]);
570 result = evalScalar(wires.back());
574 throw CircuitException(
575 "Unsupported gate type in scalar evaluation: " +
579 scalar_cache_[g] = result;
583std::unique_ptr<Distribution> Sampler::buildRvDistribution(
584 gate_t leaf,
const DistributionTemplate &tmpl,
double &p1,
double &p2)
587 auto resolve = [&](
const DistributionParam &p) {
588 return p.wire_slot < 0 ? p.literal : evalScalar(w[p.wire_slot]);
590 p1 = resolve(tmpl.p1);
591 p2 = resolve(tmpl.p2);
592 auto dist = tmpl.family->factory(p1, p2);
601 if(!dist->integrationRange(dlo, dhi))
602 throw CircuitException(
603 "gate_rv " + std::string(tmpl.family->name)
604 +
": a parameter drawn outside the family's domain "
605 "(e.g. a scale/rate/shape <= 0: got "
606 + std::to_string(p1) +
", " + std::to_string(p2)
607 +
"); put a positive-support prior on it, e.g. "
608 "gamma / lognormal");
612double Sampler::evalWeight(
gate_t g)
615 const auto &wires = gc_.
getWires(g);
624 if(w == 0.0)
return 0.0;
633 if(wires.size() != 1)
634 throw CircuitException(
635 "gate_observe must have exactly one child (the observed leaf)");
636 const gate_t leaf = wires[0];
638 throw CircuitException(
639 "gate_observe child must be a gate_rv leaf");
643 throw CircuitException(
644 "gate_observe: malformed observed gate_rv extra: "
647 return buildRvDistribution(leaf, *tmpl, p1, p2)->pdf(d);
657 if(matchPointObservationCmp(gc_, g, leaf, datum)) {
661 return buildRvDistribution(leaf, *tmpl, p1, p2)->pdf(datum);
664 return evalBool(g) ? 1.0 : 0.0;
671 return evalBool(g) ? 1.0 : 0.0;
679 std::mt19937_64 rng =
seedRng();
680 Sampler sampler(gc, rng);
682 unsigned success = 0;
683 for(
unsigned i = 0; i < samples; ++i) {
684 sampler.resetIteration();
685 if(sampler.evalBool(root))
690 "Interrupted after " + std::to_string(i + 1) +
" samples");
692 return success * 1.0 / samples;
696 double eps,
double delta,
697 unsigned long max_samples,
698 unsigned long &samples_used,
699 bool &reached_target)
702 reached_target =
false;
710 const double e = std::exp(1.0);
711 const double Y = 4.0 * (e - 2.0) * std::log(2.0 / delta) / (eps * eps);
712 const double Y1 = 1.0 + (1.0 + eps) * Y;
714 std::mt19937_64 rng =
seedRng();
715 Sampler sampler(gc, rng);
717 unsigned long success = 0;
718 for(
unsigned long s = 0; s < max_samples; ++s) {
719 sampler.resetIteration();
720 if(sampler.evalBool(root)) {
722 if(
static_cast<double>(success) >= Y1) {
723 samples_used = s + 1;
724 reached_target =
true;
725 return Y1 /
static_cast<double>(samples_used);
730 "Interrupted after " + std::to_string(s + 1) +
" samples");
735 samples_used = max_samples;
736 return static_cast<double>(success) /
static_cast<double>(max_samples);
741 const std::vector<gate_t> &cmps,
744 const unsigned k = cmps.size();
747 "monteCarloJointDistribution: empty cmps list");
750 "monteCarloJointDistribution: too many cmps in island ("
751 + std::to_string(k) +
" > 30)");
753 std::mt19937_64 rng =
seedRng();
754 Sampler sampler(gc, rng);
756 const std::size_t nb_outcomes = std::size_t{1} << k;
757 std::vector<unsigned> counts(nb_outcomes, 0);
759 for (
unsigned i = 0; i < samples; ++i) {
760 sampler.resetIteration();
762 for (
unsigned j = 0; j < k; ++j) {
763 if (sampler.evalBool(cmps[j])) w |= (std::size_t{1} << j);
768 "Interrupted after " + std::to_string(i + 1) +
" samples");
771 std::vector<double> probs(nb_outcomes);
772 for (std::size_t w = 0; w < nb_outcomes; ++w)
773 probs[w] = counts[w] * 1.0 / samples;
780 std::mt19937_64 rng =
seedRng();
781 Sampler sampler(gc, rng);
783 std::vector<double> out;
784 out.reserve(samples);
785 for(
unsigned i = 0; i < samples; ++i) {
786 sampler.resetIteration();
787 out.push_back(sampler.evalScalar(root));
791 "Interrupted after " + std::to_string(i + 1) +
" samples");
796std::pair<std::vector<double>, std::vector<double>>
798 gate_t root_b,
unsigned samples)
800 std::mt19937_64 rng =
seedRng();
801 Sampler sampler(gc, rng);
803 std::vector<double> out_a, out_b;
804 out_a.reserve(samples);
805 out_b.reserve(samples);
806 for(
unsigned i = 0; i < samples; ++i) {
807 sampler.resetIteration();
813 out_a.push_back(sampler.evalScalar(root_a));
814 out_b.push_back(sampler.evalScalar(root_b));
818 "Interrupted after " + std::to_string(i + 1) +
" samples");
820 return {std::move(out_a), std::move(out_b)};
826 std::mt19937_64 rng =
seedRng();
827 Sampler sampler(gc, rng);
833 for(
unsigned i = 0; i < samples; ++i) {
834 sampler.resetIteration();
841 if(sampler.evalBool(event_root)) {
842 out.
accepted.push_back(sampler.evalScalar(root));
848 "Interrupted after " + std::to_string(i + 1) +
" samples");
855 gate_t event_root,
unsigned samples)
857 std::mt19937_64 rng =
seedRng();
858 Sampler sampler(gc, rng);
862 out.
xs.reserve(samples);
863 out.
ys.reserve(samples);
865 for(
unsigned i = 0; i < samples; ++i) {
866 sampler.resetIteration();
870 if(sampler.evalBool(event_root)) {
871 out.
xs.push_back(sampler.evalScalar(root_a));
872 out.
ys.push_back(sampler.evalScalar(root_b));
878 "Interrupted after " + std::to_string(i + 1) +
" samples");
883std::optional<std::vector<double>>
885 gate_t event_root,
unsigned n)
888 if (!m)
return std::nullopt;
894 std::mt19937_64 rng =
seedRng();
901 std::mt19937_64 rng =
seedRng();
902 Sampler sampler(gc, rng);
907 for(
unsigned i = 0; i < samples; ++i) {
908 sampler.resetIteration();
912 const double w = sampler.evalWeight(evidence);
915 const double x = sampler.evalScalar(root);
926 "Interrupted after " + std::to_string(i + 1) +
" samples");
934 if(samples == 0)
return 0.0;
935 std::mt19937_64 rng =
seedRng();
936 Sampler sampler(gc, rng);
939 for(
unsigned i = 0; i < samples; ++i) {
940 sampler.resetIteration();
941 sw += sampler.evalWeight(evidence);
944 "Interrupted after " + std::to_string(i + 1) +
" samples");
946 return sw /
static_cast<double>(samples);
952 std::vector<double> out;
955 std::vector<double> cum;
958 for(
const auto &pw : post.
particles) { c += pw.second; cum.push_back(c); }
960 std::mt19937_64 rng =
seedRng();
961 std::uniform_real_distribution<double> u(0.0, c);
963 for(
unsigned i = 0; i < n; ++i) {
964 const double r = u(rng);
965 auto it = std::lower_bound(cum.begin(), cum.end(), r);
966 std::size_t idx =
static_cast<std::size_t
>(it - cum.begin());
968 out.push_back(post.
particles[idx].first);
975 std::unordered_set<gate_t> seen;
976 std::stack<gate_t> stack;
978 while(!stack.empty()) {
981 if(!seen.insert(g).second)
continue;
989 || matchPointObservationCmp(gc, g, leaf, datum))
998 std::unordered_set<gate_t> seen;
999 std::stack<gate_t> stack;
1001 while(!stack.empty()) {
1004 if(!seen.insert(g).second)
continue;
1035 std::unordered_set<gate_t> seen;
1036 std::stack<gate_t> stack;
1039 while(!stack.empty()) {
1042 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.
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.
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::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.
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 –...
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_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_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_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.