25static std::vector<double> partialPMF(
const std::vector<double> &p,
28 const std::size_t N = p.size();
29 jmax = std::min(jmax, N);
30 std::vector<double> dp(jmax + 1, 0.0);
32 for (std::size_t i = 0; i < N; ++i) {
33 const double pi = p[i];
34 const double qi = 1.0 - pi;
37 const std::size_t upper = std::min(jmax, i + 1);
38 for (std::size_t j = upper; j >= 1; --j) {
39 dp[j] = dp[j] * qi + dp[j - 1] * pi;
49static double probZero(
const std::vector<double> &p)
52 for (
double pi : p) q *= (1.0 - pi);
64static double probAtLeast(
const std::vector<double> &p,
int T)
66 const int N =
static_cast<int>(p.size());
67 if (T <= 0)
return 1.0;
68 if (T > N)
return 0.0;
71 auto dp = partialPMF(p,
static_cast<std::size_t
>(T - 1));
73 for (
int j = 0; j <= T - 1; ++j) sum += dp[j];
76 std::vector<double> q(N);
77 for (
int i = 0; i < N; ++i) q[i] = 1.0 - p[i];
78 auto dp = partialPMF(q,
static_cast<std::size_t
>(N - T));
80 for (
int j = 0; j <= N - T; ++j) sum += dp[j];
89static double probAtMost(
const std::vector<double> &p,
int T)
91 const int N =
static_cast<int>(p.size());
92 if (T < 0)
return 0.0;
93 if (T >= N)
return 1.0;
96 auto dp = partialPMF(p,
static_cast<std::size_t
>(T));
98 for (
int j = 0; j <= T; ++j) sum += dp[j];
101 std::vector<double> q(N);
102 for (
int i = 0; i < N; ++i) q[i] = 1.0 - p[i];
103 auto dp = partialPMF(q,
static_cast<std::size_t
>(N - 1 - T));
105 for (
int j = 0; j <= N - 1 - T; ++j) sum += dp[j];
114static double probEqual(
const std::vector<double> &p,
int T)
116 const int N =
static_cast<int>(p.size());
117 if (T < 0 || T > N)
return 0.0;
120 auto dp = partialPMF(p,
static_cast<std::size_t
>(T));
123 std::vector<double> q(N);
124 for (
int i = 0; i < N; ++i) q[i] = 1.0 - p[i];
125 auto dp = partialPMF(q,
static_cast<std::size_t
>(N - T));
160static double cdfForOperator(
const std::vector<double> &p,
162 int C,
bool is_scalar)
164 const int N =
static_cast<int>(p.size());
171 r = probAtLeast(p, std::max(C, 1));
175 r = probAtLeast(p, std::max(C + 1, 1));
180 const int T = std::min(C, N);
181 r = (T < 1) ? 0.0 : probAtMost(p, T) - probZero(p);
185 const int T = std::min(C - 1, N);
186 r = (T < 1) ? 0.0 : probAtMost(p, T) - probZero(p);
190 r = (C < 1 || C > N) ? 0.0 : probEqual(p, C);
195 const double nonempty = 1.0 - probZero(p);
196 const double eq = (C >= 1 && C <= N) ? probEqual(p, C) : 0.0;
201 if (is_scalar && zeroSatisfies(op, C))
225static double probInRange(
const std::vector<double> &p,
long lo,
long hi,
228 const long N =
static_cast<long>(p.size());
229 lo = std::max(lo, is_scalar ? 0L : 1L);
230 hi = std::min(hi, N);
233 const auto dp = partialPMF(p,
static_cast<std::size_t
>(hi));
235 for (
long j = lo; j <= hi; ++j)
236 r += dp[
static_cast<std::size_t
>(j)];
251static unsigned resolveCountRangePairs(GenericCircuit &gc,
252 const std::vector<unsigned> &ref)
259 std::map<gate_t, std::vector<gate_t>> parents;
260 std::map<gate_t, std::vector<Candidate>> by_top;
261 unsigned resolved = 0;
263 for (std::size_t i = 0; i < nb; ++i) {
264 auto g =
static_cast<gate_t>(i);
267 parents[w].push_back(g);
269 for (
const auto &kv : parents) {
271 if (kv.second.size() != 1 || !
matchAggCmp(gc, kv.first, match) ||
274 if (!std::all_of(match.ms.begin(), match.ms.end(),
275 [](
long m) { return m == 1; }))
277 const gate_t top = match.
via.empty() ? match.agg : match.via.front();
278 by_top[top].push_back({kv.first, std::move(match)});
281 for (
const auto &kv : by_top) {
282 const auto &pair = kv.second;
283 if (pair.size() != 2 || ref[
static_cast<std::size_t
>(kv.first)] != 2)
285 const AggCmpMatch &m0 = pair[0].match, &m1 = pair[1].match;
286 const gate_t parent = parents[pair[0].cmp].front();
287 if (parents[pair[1].cmp].front() != parent ||
289 m0.agg != m1.agg || m0.via != m1.via)
293 for (std::size_t i = 1; i < m0.via.size(); ++i)
294 if (ref[
static_cast<std::size_t
>(m0.via[i])] != 1)
296 if (!m0.via.empty() && ref[
static_cast<std::size_t
>(m0.agg)] != 1)
298 std::vector<double> p;
299 p.reserve(m0.ks.size());
300 for (std::size_t i = 0; sound && i < m0.ks.size(); ++i) {
301 if (ref[
static_cast<std::size_t
>(m0.semimods[i])] != 1) {
311 long lo0, hi0, lo1, hi1;
312 if (!countBounds(m0.op, m0.C, lo0, hi0) ||
313 !countBounds(m1.op, m1.C, lo1, hi1))
315 const bool is_scalar =
317 double pr = probInRange(p, std::max(lo0, lo1), std::min(hi0, hi1),
319 if (pr < 0.0) pr = 0.0;
320 if (pr > 1.0) pr = 1.0;
333 unsigned resolved = 0;
338 std::vector<gate_t> cmps;
339 for (std::size_t i = 0; i < nb; ++i) {
340 auto g =
static_cast<gate_t>(i);
344 if (cmps.empty())
return 0;
369 for (
long m : match.
ms)
if (m != 1) { all_one =
false;
break; }
370 if (!all_one)
continue;
374 const auto &semimods = match.
semimods;
375 const auto &ks = match.
ks;
377 const int C =
static_cast<int>(match.
C);
413 std::vector<double> p;
414 p.reserve(ks.size());
415 for (std::size_t i = 0; i < ks.size(); ++i) {
416 if (ref[
static_cast<std::size_t
>(semimods[i])] != 1) { sound =
false;
break; }
421 if (!sound)
continue;
425 const bool is_scalar =
429 double pr = cdfForOperator(p, op, C, is_scalar);
432 if (pr < 0.0) pr = 0.0;
433 if (pr > 1.0) pr = 1.0;
439 resolved += resolveCountRangePairs(gc, ref);
Typed aggregation value, operator, and aggregator abstractions.
@ COUNT
COUNT(*) or COUNT(expr) → integer.
ComparisonOperator
SQL comparison operators used in gate_cmp circuit gates.
@ LE
Less than or equal (<=).
@ GE
Greater than or equal (>=).
gate_t
Strongly-typed gate identifier.
Shared machinery for the closed-form HAVING gate_cmp probability evaluators (Poisson-binomial COUNT,...
Closed-form Poisson-binomial CDF resolution for HAVING COUNT(*) op C gate_cmps.
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 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.
unsigned runCountCmpEvaluator(GenericCircuit &gc)
Run the Poisson-binomial pre-pass over gc.
bool aggPrivateToCmp(const AggCmpMatch &match, const std::vector< unsigned > &ref)
Whether the aggregate of match is consumed by its comparison alone: agg and every gate of via has ref...
std::vector< unsigned > computeRefCounts(const GenericCircuit &gc)
Reference count of every gate as a wire-target across the whole circuit.
bool matchAggCmp(GenericCircuit &gc, gate_t cmp, AggCmpMatch &out)
Try to match cmp against gate_cmp(gate_agg(α, semimod_i(K_i, m_i)*), gate_value(C)).
double contributorProb(const GenericCircuit &gc, gate_t g, const std::vector< unsigned > &ref, bool &ok)
Read-once marginal probability of a count/aggregate contributor (the K side of a semimod).
#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...
Result of matching a gate_cmp against the canonical HAVING aggregate-comparison shape.
gate_t agg
the gate_agg operand of the cmp
long C
the constant threshold, on the same integer grid as ms
std::vector< gate_t > ks
the K side of each semimod (contributor root)
std::vector< gate_t > semimods
the per-child gate_semimod parents
std::vector< gate_t > via
the gate_arith gates of constant arithmetic between the cmp and agg, folded into op and C
std::vector< long > ms
the M side of each semimod (per-row value), scaled to a common integer grid (numeric / decimal-float ...
AggregationOperator agg_kind
effective aggregate (SUM-of-1s remapped to COUNT)
ComparisonOperator op
comparator, flipped if the agg sits on the right