Skip to content

Prove literal IN lists with a membership gadget - #210

Merged
alireza-shirzad merged 1 commit into
mainfrom
in-list-membership
Nov 11, 2026
Merged

alireza-shirzad merged 1 commit into
mainfrom
in-list-membership

Conversation

@alireza-shirzad

Copy link
Copy Markdown
Collaborator

Fixes #203.

Problem

Since #184, x IN (v1, .., vk) became x = v1 OR .. OR x = vk. OR stays virtual (a + b − ab), so a 4-value list is 15 terms, and Q19 ORs three branches that each hold one. Degree reduction expands the whole claim before it reduces anything: Q19's claim had 4,882 terms of degree up to 27, and 787 chunk commitments were needed to bring the degree down. That took Q19's verifier from 42 ms to 173 ms. The September proof is not a sound baseline: its IN lists were committed without any check.

Change

  • Membership gadget (irs/nodes/plan/exprs/in_list/gadget.rs). A list of literal values is proved directly. The result b is committed. The data columns of x and of every value are folded with the same challenges into f and gᵢ. The gadget checks:

    • m·P(f) = 0 with P = ∏(f − gᵢ), where m is the rows in the list (b, or act − b for NOT IN);
    • P(f) + γ·(1 − (act − m)) is nonzero, so no active row outside m matches a value;
    • b is boolean, and 0 on inactive rows.

    P is built in Horner form from its public coefficients. It expands to one term per power of f (k + 1 terms for a number column), not one per subset of the list.

  • InList IR node (in_list/mod.rs), which commits the result and wires the gadget.

  • InListAsComparisons rule: now rewrites only lists that hold a value other than a non-null literal. ensure_supported refuses such a list if it reaches the IR.

  • Shared helper: activator_expr moves from BinaryExpr to hints, shared by both nodes.

Soundness

  • The fold challenges are drawn after x and b are committed. A row with x ≠ vᵢ folds to gᵢ with probability at most 1/|F| per value.
  • A row with x = vᵢ always makes P = 0, so the nonzero check rejects marking it "not in the list".

Measurements (TPC-H Q19 _tt, SF 0.1, 1 thread)

before (Nov 9) after
verifier, total 173.4 ms 48.2 ms
verifier, crypto only 144.7 ms 26.2 ms
prover (one sample) 45.8 s 42.9 s
proof, compressed 45,573 B 39,682 B

On test data, the largest sumcheck claim goes from 4,882 terms (4,096 over the degree limit) to 836 (56 over).

Tests

  • tests/in_list.rs: three new cases (an expression as input, a string NOT IN, a list matching no row), 8 cases in all. Each was checked to reach the membership gadget, and all pass with and without honest-prover.
  • tests/in_list_forgery.rs: the prover commits the negated result of a string IN list and claims it. Verification rejects it. With the gadget's two membership checks removed, the same forgery verifies.
  • rewritten_in_list_matches_datafusion: lists with a column value are rewritten; literal lists are kept by this rule and by the full rule set.
  • tpch_q19 passes with and without honest-prover.
  • The full e2e suite (cargo test --release -p tt-exec --features test-utils --no-fail-fast) has 225 passed and 1 failed. The failure is bench_like_infix_lineitem, which passes when run alone. It fails only when it shares a process with the test-data LIKE tests, because the two give l_comment different fingerprint rules; it already failed this way before this change.
  • Also passing: debug cargo test --workspace --lib, just test, clippy with CI flags, and cargo fmt --check.

Open

#183: a long list still grows. It is k + 1 terms for a number column and about 2^(k+1) for a string (hash and length folded together). That is never more than the OR form it replaces. The TODO in the rule points there.

🤖 Generated with Claude Code

Since #184, `x IN (v1..vk)` was proved as `x = v1 OR .. OR x = vk`. OR
stays virtual (a + b - ab), so a 4-value list is 15 terms, and TPC-H
Q19 ORs three branches that each hold one. Degree reduction expands the
whole claim first: 4,882 terms of degree up to 27, then 787 chunk
commitments. Q19's verifier went from 42 ms to 173 ms (#203).

A list of non-null literals now reaches a new InList node. It commits
the result b, and a membership gadget checks it. With f and gi the
folds of x and of each value under shared challenges, P = prod(f - gi)
is built in Horner form from public coefficients, and the gadget checks:
m*P = 0 on the rows in the list, a nonzero check on P elsewhere among
the active rows, and b boolean and 0 on inactive rows.
InListAsComparisons now rewrites only lists with another kind of
value, and ensure_supported refuses such a list if it reaches the IR.

TPC-H Q19 _tt, SF 0.1, 1 thread: verifier 173.4 -> 48.2 ms (crypto
part 144.7 -> 26.2 ms), prover 45.8 -> 42.9 s (one sample), compressed
proof 45.6 -> 39.7 KB.

Tests: three new e2e IN-list cases; a forgery test (negated committed
result) that is rejected, and verifies once the gadget's membership
checks are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alireza-shirzad
alireza-shirzad merged commit c5b4b61 into main Nov 11, 2026
12 checks passed
alireza-shirzad added a commit that referenced this pull request Nov 13, 2026
Rerun the _tt TPC-H benches (SF 0.05 and 0.1, 1 and 4 threads) and the
optimization ablation (SF 0.05, 1 thread) on cf9ce63.

Q19's verifier drops from 173 ms to 46 ms (SF 0.1) with the IN-list
membership gadget of #210, its prover from 45.8 s to 42.7 s and its proof
from 44.5 KB to 38.8 KB; in the ablation its verifier goes from 171-392 ms
to about 45 ms in every configuration. Every other query's prover time is
within 5% and its proof size unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alireza-shirzad added a commit that referenced this pull request Nov 13, 2026
Rerun the _tt TPC-H benches (SF 0.05 and 0.1, 1 and 4 threads) and the
optimization ablation (SF 0.05, 1 thread) on cf9ce63.

Q19's verifier drops from 173 ms to 46 ms (SF 0.1) with the IN-list
membership gadget of #210, its prover from 45.8 s to 42.7 s and its proof
from 44.5 KB to 38.8 KB; in the ablation its verifier goes from 171-392 ms
to about 45 ms in every configuration. Every other query's prover time is
within 5% and its proof size unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TPC-H Q19 verifier: 42 ms to 173 ms since September

1 participant