|
16 | 16 | -- * an emitted d-D whose size is LINEAR in the number of facts; |
17 | 17 | -- * near-linear wall-clock time. |
18 | 18 | -- |
| 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 | +-- |
19 | 27 | -- Run : |
20 | 28 | -- createdb ucqbench && psql ucqbench -X -f test/bench/ucq_joint_bench.sql |
21 | 29 | -- ---------------------------------------------------------------------- |
22 | 30 | CREATE EXTENSION IF NOT EXISTS provsql CASCADE; |
23 | 31 | SET search_path TO provsql, public; |
24 | 32 |
|
25 | 33 | 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, |
27 | 35 | max_states bigint, dd_size bigint, probability double precision) |
28 | 36 | AS $$ |
29 | 37 | DECLARE |
@@ -57,13 +65,73 @@ BEGIN |
57 | 65 | frel, fel, far, ftok, fpr); |
58 | 66 | t1 := clock_timestamp(); |
59 | 67 | 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; |
61 | 123 | dd_size := st.dd_size; probability := st.probability; |
62 | 124 | RETURN NEXT; |
63 | 125 | END; |
64 | 126 | $$ LANGUAGE plpgsql; |
65 | 127 |
|
| 128 | +\echo 'H0 chain: x -> y holds in the data, so e = 1' |
66 | 129 | SELECT * FROM ucq_bench(1000); |
67 | 130 | SELECT * FROM ucq_bench(10000); |
68 | 131 | SELECT * FROM ucq_bench(100000); |
69 | 132 | 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