\set ECHO none \pset format unaligned -- SQL-standard statistic aggregates over random_variable rows: -- covar_pop / covar_samp / corr (two-argument), stddev_pop / stddev_samp -- (one-argument), and the ordered-set percentile_cont. Row presence is -- carried by a per-row indicator RV; a provenance-tracked query is -- rewritten by the planner hook to the rv_*_impl aggregates so absent -- rows drop out of the statistic. The moments are Monte Carlo (there is -- no closed form for these compound circuits), but every assertion below -- on CERTAIN inputs is exact: all draws coincide, so the MC mean is the -- constant itself. SET provsql.monte_carlo_seed = 42; SET provsql.rv_mc_samples = 20000; -- --------------------------------------------------------------------- -- Untracked (certain) rows: the public aggregates fold with the certain -- indicator, and every statistic over Dirac inputs is a Dirac at the -- classical sample statistic. x = (1, 3), y = (2, 8): -- covar_pop = E[xy] - E[x]E[y] = 13 - 10 = 3; covar_samp = 6; -- stddev_pop(x) = 1; stddev_samp(x) = sqrt(2); corr = 1 (linear). -- --------------------------------------------------------------------- CREATE TABLE st(g int, x random_variable, y random_variable); INSERT INTO st VALUES (1, provsql.as_random(1), provsql.as_random(2)), (1, provsql.as_random(3), provsql.as_random(8)); SELECT round(expected(covar_pop(x, y))::numeric, 4) AS covar_pop, round(expected(covar_samp(x, y))::numeric, 4) AS covar_samp, round(expected(corr(x, y))::numeric, 4) AS corr FROM st GROUP BY g; SELECT round(expected(stddev_pop(x))::numeric, 4) AS stddev_pop, round(expected(stddev_samp(x))::numeric, 4) AS stddev_samp FROM st GROUP BY g; -- Draw coupling: corr(x, x) over two independent Normal rows is 1 in -- every draw (the same per-row draw feeds both argument positions), so -- the MC mean is exactly 1. CREATE TABLE stn(g int, x random_variable); INSERT INTO stn VALUES (1, provsql.normal(5, 1)), (1, provsql.normal(7, 2)); SELECT round(expected(corr(x, x))::numeric, 4) AS corr_self FROM stn; DROP TABLE stn; DROP TABLE st; -- --------------------------------------------------------------------- -- Provenance-tracked rows: the planner rewrites to rv_covar_pop_impl & -- co. with the per-row provenance indicator. Two rows each present with -- probability 0.5, x = (1, 3) / y = (2, 8) as above. Worlds: -- {} covar undefined (N = 0) -> NaN, skipped; -- {r1},{r2} single row -> covar_pop = 0; -- {r1,r2} covar_pop = 3. -- E[covar_pop | defined] = (0 + 0 + 3)/3 = 1. -- --------------------------------------------------------------------- CREATE TABLE stp(g int, x random_variable, y random_variable); INSERT INTO stp VALUES (1, provsql.as_random(1), provsql.as_random(2)), (1, provsql.as_random(3), provsql.as_random(8)); SELECT add_provenance('stp'); DO $$ BEGIN PERFORM set_prob(provenance(), 0.5) FROM stp; END $$; -- Materialise the aggregate results, then read them back untracked so the -- (run-dependent) group provenance token is not printed. CREATE TABLE stq AS SELECT g, covar_pop(x, y) AS c, covar_samp(x, y) AS cs, stddev_pop(x) AS s FROM stp GROUP BY g; SET provsql.active = off; SELECT abs(expected(c) - 1.0) < 0.1 AS covar_pop_tracked_close FROM stq; -- covar_samp needs two rows: {r1,r2} is the only defined world, where it -- is 2 * covar_pop = 6; conditional-on-defined the expectation is exact. SELECT abs(expected(cs) - 6.0) < 0.05 AS covar_samp_tracked_close FROM stq; -- stddev_pop over the same tracked x = (1, 3): defined worlds -- {r1} -> 0, {r2} -> 0, {r1,r2} -> 1; E = 1/3. SELECT abs(expected(s) - 1.0/3) < 0.05 AS stddev_pop_tracked_close FROM stq; SET provsql.active = on; DROP TABLE stq; DROP TABLE stp; -- --------------------------------------------------------------------- -- percentile_cont over tracked rows. Certain rows first: the median and -- the lower quartile of (10, 20, 30) are 20 and 15 in every world, so -- the MC mean is exact. -- --------------------------------------------------------------------- CREATE TABLE pc(g int, x random_variable); INSERT INTO pc VALUES (1, provsql.as_random(10)), (1, provsql.as_random(20)), (1, provsql.as_random(30)); SELECT add_provenance('pc'); DO $$ BEGIN PERFORM set_prob(provenance(), 1.0) FROM pc; END $$; CREATE TABLE pct AS SELECT g, percentile_cont(0.5) WITHIN GROUP (ORDER BY x) AS m, percentile_cont(0.25) WITHIN GROUP (ORDER BY x) AS q1 FROM pc GROUP BY g; SET provsql.active = off; SELECT round(expected(m)::numeric, 4) AS median_certain, round(expected(q1)::numeric, 4) AS q1_certain FROM pct; -- The gate is the PROVSQL_ARITH_PERCENTILE arith opcode with the fraction -- in extra. SELECT get_gate_type(m::uuid) AS pct_gate, (get_infos(m::uuid)).info1 AS pct_op, get_extra(m::uuid) AS pct_fraction FROM pct; SET provsql.active = on; DROP TABLE pct; -- Fraction domain validation. SELECT expected(percentile_cont(1.5) WITHIN GROUP (ORDER BY x)) FROM pc GROUP BY g; DROP TABLE pc; -- Uncertain member set: rows 10 and 30 certain, 25 with probability 0.5. -- Median with the middle row = 25, without = (10+30)/2 = 20; E = 22.5. CREATE TABLE pcu(g int, x random_variable, w float8); INSERT INTO pcu VALUES (1, provsql.as_random(10), 1.0), (1, provsql.as_random(25), 0.5), (1, provsql.as_random(30), 1.0); SELECT add_provenance('pcu'); DO $$ BEGIN PERFORM set_prob(provenance(), w) FROM pcu; END $$; CREATE TABLE pcm AS SELECT g, percentile_cont(0.5) WITHIN GROUP (ORDER BY x) AS m FROM pcu GROUP BY g; SET provsql.active = off; SELECT abs(expected(m) - 22.5) < 0.2 AS median_uncertain_close FROM pcm; SET provsql.active = on; DROP TABLE pcm; DROP TABLE pcu; RESET provsql.rv_mc_samples; RESET provsql.monte_carlo_seed; SELECT 'ok'::text AS stat_aggregates_done;