Skip to content

Commit 4caec1e

Browse files
Joint-width bench: report the FD-mined essential-variable count n_enum and add a broken-FD gadget variant (e=2), showing e goes 1->2 while max_states stays flat -- the FD tightens the complexity parameter, not the realised DP cost
1 parent f83f30b commit 4caec1e

1 file changed

Lines changed: 70 additions & 2 deletions

File tree

test/bench/ucq_joint_bench.sql

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616
-- * an emitted d-D whose size is LINEAR in the number of facts;
1717
-- * near-linear wall-clock time.
1818
--
19+
-- This bench also illustrates the FD-mined essential-variable count
20+
-- (n_enum, the e of the 2^O(k^e) bound). In the chain each x has a single
21+
-- outgoing S-edge, so x -> y holds in the data and e = 1. ucq_bench_nofd
22+
-- gives each x TWO distinct y's (FD broken), so e = 2. The point: e drops
23+
-- from 2 to 1 but max_states is UNCHANGED -- the realised DP state is
24+
-- data-bounded (the DP only ever binds variables from actual facts), so the
25+
-- FD tightens the complexity PARAMETER, not the realised cost.
26+
--
1927
-- Run :
2028
-- createdb ucqbench && psql ucqbench -X -f test/bench/ucq_joint_bench.sql
2129
-- ----------------------------------------------------------------------
2230
CREATE EXTENSION IF NOT EXISTS provsql CASCADE;
2331
SET search_path TO provsql, public;
2432

2533
CREATE OR REPLACE FUNCTION ucq_bench(n int)
26-
RETURNS TABLE(nn int, ms double precision, joint_tw int,
34+
RETURNS TABLE(nn int, ms double precision, joint_tw int, n_enum int,
2735
max_states bigint, dd_size bigint, probability double precision)
2836
AS $$
2937
DECLARE
@@ -57,13 +65,73 @@ BEGIN
5765
frel, fel, far, ftok, fpr);
5866
t1 := clock_timestamp();
5967
nn := n; ms := round((extract(epoch from (t1-t0))*1000)::numeric,1);
60-
joint_tw := st.joint_treewidth; max_states := st.max_states;
68+
joint_tw := st.joint_treewidth; n_enum := st.n_enumerating;
69+
max_states := st.max_states;
70+
dd_size := st.dd_size; probability := st.probability;
71+
RETURN NEXT;
72+
END;
73+
$$ LANGUAGE plpgsql;
74+
75+
-- Broken-FD variant: n independent tree gadgets, gadget i over x-nodes
76+
-- a=2i, b=2i+1 and y-nodes c=2n+2i, d=2n+2i+1, with S-edges a-c, a-d, b-c.
77+
-- Then a -> {c,d} (no x -> y) and c -> {a,b} (no y -> x), so NEITHER FD holds
78+
-- and e = 2. Each gadget is a 3-edge tree, so the joint graph is a forest
79+
-- (joint treewidth 1) -- same width as the chain, isolating the effect of e:
80+
-- e goes 1 -> 2 while max_states stays put.
81+
CREATE OR REPLACE FUNCTION ucq_bench_nofd(n int)
82+
RETURNS TABLE(nn int, ms double precision, joint_tw int, n_enum int,
83+
max_states bigint, dd_size bigint, probability double precision)
84+
AS $$
85+
DECLARE
86+
s_rel int[]; s_el int[]; s_ar int[]; s_tok uuid[]; s_pr float8[];
87+
r_rel int[]; r_el int[]; r_ar int[]; r_tok uuid[]; r_pr float8[];
88+
t_rel int[]; t_el int[]; t_ar int[]; t_tok uuid[]; t_pr float8[];
89+
frel int[]; fel int[]; far int[]; ftok uuid[]; fpr float8[];
90+
t0 timestamptz; t1 timestamptz; st record;
91+
BEGIN
92+
-- S: 3 edges per gadget (j=0: a-c, j=1: a-d, j=2: b-c).
93+
SELECT array_agg(1 ORDER BY i,j), array_agg(2 ORDER BY i,j), array_agg(0.5 ORDER BY i,j),
94+
array_agg(public.uuid_generate_v5(uuid_ns_provsql(),'s'||i||'_'||j) ORDER BY i,j)
95+
INTO s_rel, s_ar, s_pr, s_tok
96+
FROM generate_series(0,n-1) i, generate_series(0,2) j;
97+
SELECT array_agg(el ORDER BY i,j,c) INTO s_el
98+
FROM generate_series(0,n-1) i, generate_series(0,2) j,
99+
LATERAL (VALUES
100+
(0, CASE WHEN j = 2 THEN 2*i+1 ELSE 2*i END),
101+
(1, CASE WHEN j = 1 THEN 2*n+2*i+1 ELSE 2*n+2*i END)) v(c, el);
102+
-- R on every x-node (0 .. 2n-1), T on every y-node (2n .. 4n-1).
103+
SELECT array_agg(0 ORDER BY x), array_agg(1 ORDER BY x), array_agg(0.5 ORDER BY x),
104+
array_agg(x ORDER BY x),
105+
array_agg(public.uuid_generate_v5(uuid_ns_provsql(),'r'||x) ORDER BY x)
106+
INTO r_rel, r_ar, r_pr, r_el, r_tok FROM generate_series(0,2*n-1) x;
107+
SELECT array_agg(2 ORDER BY y), array_agg(1 ORDER BY y), array_agg(0.5 ORDER BY y),
108+
array_agg(y ORDER BY y),
109+
array_agg(public.uuid_generate_v5(uuid_ns_provsql(),'t'||y) ORDER BY y)
110+
INTO t_rel, t_ar, t_pr, t_el, t_tok FROM generate_series(2*n,4*n-1) y;
111+
frel := s_rel || r_rel || t_rel; far := s_ar || r_ar || t_ar;
112+
fpr := s_pr || r_pr || t_pr; ftok := s_tok || r_tok || t_tok;
113+
fel := s_el || r_el || t_el;
114+
t0 := clock_timestamp();
115+
SELECT * INTO st FROM ucq_joint_compile_stats(
116+
'{"disjuncts":[{"n_vars":2,"atoms":[
117+
{"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
118+
frel, fel, far, ftok, fpr);
119+
t1 := clock_timestamp();
120+
nn := n; ms := round((extract(epoch from (t1-t0))*1000)::numeric,1);
121+
joint_tw := st.joint_treewidth; n_enum := st.n_enumerating;
122+
max_states := st.max_states;
61123
dd_size := st.dd_size; probability := st.probability;
62124
RETURN NEXT;
63125
END;
64126
$$ LANGUAGE plpgsql;
65127

128+
\echo 'H0 chain: x -> y holds in the data, so e = 1'
66129
SELECT * FROM ucq_bench(1000);
67130
SELECT * FROM ucq_bench(10000);
68131
SELECT * FROM ucq_bench(100000);
69132
SELECT * FROM ucq_bench(1000000);
133+
\echo 'H0, FD broken (two y per x): e = 2, but max_states unchanged'
134+
SELECT * FROM ucq_bench_nofd(1000);
135+
SELECT * FROM ucq_bench_nofd(10000);
136+
SELECT * FROM ucq_bench_nofd(100000);
137+
SELECT * FROM ucq_bench_nofd(1000000);

0 commit comments

Comments
 (0)