Repository navigation
Prove literal IN lists with a membership gadget - #210
Merged
Merged
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #203.
Problem
Since #184,
x IN (v1, .., vk)becamex = 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 resultbis committed. The data columns ofxand of every value are folded with the same challenges intofandgᵢ. The gadget checks:m·P(f) = 0withP = ∏(f − gᵢ), wheremis the rows in the list (b, oract − bforNOT IN);P(f) + γ·(1 − (act − m))is nonzero, so no active row outsidemmatches a value;bis boolean, and 0 on inactive rows.Pis built in Horner form from its public coefficients. It expands to one term per power off(k + 1 terms for a number column), not one per subset of the list.InListIR node (in_list/mod.rs), which commits the result and wires the gadget.InListAsComparisonsrule: now rewrites only lists that hold a value other than a non-null literal.ensure_supportedrefuses such a list if it reaches the IR.Shared helper:
activator_exprmoves from BinaryExpr tohints, shared by both nodes.Soundness
xandbare committed. A row withx ≠ vᵢfolds togᵢwith probability at most1/|F|per value.x = vᵢalways makesP = 0, so the nonzero check rejects marking it "not in the list".Measurements (TPC-H Q19
_tt, SF 0.1, 1 thread)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 stringNOT IN, a list matching no row), 8 cases in all. Each was checked to reach the membership gadget, and all pass with and withouthonest-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_q19passes with and withouthonest-prover.cargo test --release -p tt-exec --features test-utils --no-fail-fast) has 225 passed and 1 failed. The failure isbench_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 givel_commentdifferent fingerprint rules; it already failed this way before this change.cargo test --workspace --lib,just test, clippy with CI flags, andcargo 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