Conversation
This was referenced Oct 3, 2026
zzylol
marked this pull request as draft
October 3, 2026 19:29
Contributor
Author
|
Parked as draft: PR priorities changed (see #528). Order is now (A) finish #511 operator sharing, (B) the #572 crate/module reorganization, (C) #509 end-to-end stages. This PR sits on the old 🤖 Generated with Claude Code |
This was referenced Oct 4, 2026
Contributor
Author
|
Ported onto the current stack in #598 |
zzylol
added a commit
that referenced
this pull request
Oct 4, 2026
Port of the parked #562 and #564 for #509 Example 2. The SQL frontend names `SQRT(SUM(c*c))` over Float64 grouped unit counts as FrequencyL2, and `-SUM(p*LN(p))` with `p = COUNT(*)*1.0 / SUM(COUNT(*)) OVER ()` as FrequencyEntropy converted to nats. An exact population count guards SQL's empty-input NULL. Integer products, nullable keys, filters, HAVING, other log bases and partial windows are refused. The rules are the old Pass 1 SemanticEquivalentRewriteStrategy rules, run by lower_sql at the query root so the Stage 1 pipeline sees the intents; the exact candidate is the native exact reducer. The executor gains SQL sqrt (from #562). 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.
Problem: the #509 Example 2 entropy query (Q2) has no
Entropyintent, so Pass 1 cannot offer summary candidates for it#509 §Pass 1: Local candidate generation lists
Entropy(x)with three local candidates: "Exact entropy, a specialized entropy summary, UnivMon". #509 §Example 2 ("One summary for several computations") then writes Q2 in SQL and says:Q3 (L2) was recognized earlier in this stack. Q2 was not. Q2 is:
Before this PR, the lowered graph is only relational. Nothing in it says "entropy":
So Pass 1 has no
FrequencyEntropyintent to attach the entropy-summary or UnivMon candidates to, and the Pass 2 summary-capability rule cannot put Q2 on a shared UnivMon with Q1 and Q3.There are also two semantic traps in a naive rewrite:
AggIntent::FrequencyEntropyis "Shannon entropy in bits". The SQL usesLN, so the answer is in nats. A rewrite that returns the intent directly is off by a factor ofln 2.SUMover zero groups returnsNULL. The L2 rewrite added earlier in this stack restored this withCASE WHEN l2 = 0.0 THEN NULL. That is fine while the statistic is exact. It is wrong once the statistic is approximate: an estimate may be0.0for nonempty input, and then the query would returnNULLinstead of a number. Entropy has the same problem worse, because exact entropy is0for any single-identity population.Scope covered here. Recognition of the Q2 natural-log form as
FrequencyEntropy(the "TODO" above, for entropy), with SQL units,NULL, and negative-zero behavior kept. The same exact population guard is applied to the existing L2 rewrite.Left out. Native execution of the original Q2 graph (the SQL window and
LN) is the next PR, #565. UnivMon accuracy for entropy is not certified. Pass 2 sharing partitions (Example 2's 37 candidates) are not enumerated.Proposed method
The rule is a Pass 1 logical rewrite. It runs inside the existing
SemanticEquivalentRewriteStrategyinasap-aware-mapping, on the common IR after the SQL frontend. It adds one alternative and keeps the original exact graph as another candidate.Matching, top-down (all in
frequency_rewrite.rs):Projectwith one item. After following projections (expand), the item isNegative(SQL)of column 0 (casts toFloat64are ignored).Aggregatewith exactly one measureSum { col }, no measure filters, noHAVING.x * LN(x)(either order, SQL semantics).probability_termchecks that theLNargument is structurally equal to the other factor.xisunit_count / Column(2)with SQL division andFloat64type.unit_count_termacceptsColumn(1)orColumn(1) * 1.0(either order). Any other scale, such as* 2.0, is refused.Column(2)comes from anSQLWindowFuncwithfunc: Sum,args == [Column(1)], emptypartition_by, emptyorder_by, and a frame ofUNBOUNDED PRECEDINGtoUNBOUNDED FOLLOWING.grouped_unit_count, shared with L2): one group key, oneCountmeasure, no measure filter, noHAVING, and the key is a non-nullableBool,Int64orUtf8field.If every check passes, the rewrite builds (
sql_frequency_result):* LN_2converts bits to nats.-(0.0 - x)equalsx, except thatx = 0gives-0.0. SQL's-SUM(1 * LN(1))is also-0.0, so a single-identity population keeps the same bit pattern.population_countis always exact. The approximate statistic never decides whether the result isNULL.accuracyis copied from the originalCOUNT(*)measure, which carries the query's target.The L2 rewrite now uses the same
grouped_unit_countandsql_frequency_result. Its output changes fromCASE WHEN l2 = 0.0 …to the exact-count guard. Its value expression isColumn(0)(no unit change).Key code interfaces
All new functions are crate-private. The public entry point is unchanged:
SemanticEquivalentRewriteStrategy(incrates/asap-aware-mapping/src/rewrite.rs) now also calls the entropy rule inmatchesandreplacements. Inreplacementsthe entropy rule is tried first.crates/asap-aware-mapping/src/frequency_rewrite.rs:The replacement it emits (
rewrite.rs):substitute(projection lineage) now also passes throughScalarExpr::LiteralandScalarExpr::Negative.Usage (from
crates/frontend-sql/tests/frequency_entropy.rs):Fields
frequency_entropy_rewriteroot&Rc<OperatorNode>Projectof the Q2 shape.Option<Rc<OperatorNode>>Noneif any check in "Proposed method" fails.Nonemeans "not this rule", not an error.grouped_unit_countnode&Rc<OperatorNode>Aggregate { reduction: Reduce(keys), measures: [Count], having: None }..0usizecolof the frequency intent..1AccuracyTargetCountmeasure's accuracy. Becomes the statistic'saccuracy..2Rc<OperatorNode>Refused when:
keys.is_without(), any measure filter, key field nullable, or key type notBool/Int64/Utf8. A nullable key is refused becauseGROUP BYmakes a realNULLgroup that the frequency intent would skip.sql_frequency_resultroot&Rc<OperatorNode>qualifier, and the schema the result must equal.inputRc<OperatorNode>grouped_unit_count).measureAggIntentFrequencyEntropy { col, accuracy }orFrequencyL2 { col, accuracy }.name&str"frequency_entropy"or"frequency_l2".valueScalarExprELSEbranch over the joined row (Column(0)= statistic,Column(1)=population_count). Entropy passes the nats/negative-zero expression; L2 passesColumn(0).Option<Rc<OperatorNode>>Noneif node construction fails or the schema differs fromroot.schema.probability_term/unit_count_termprobability_term(product)a * LN(b)orLN(b) * awith SQL semantics anda == b, returnsa. Function name match is case-insensitive.unit_count_term(expr)Column(1)orColumn(1) * 1.0/1.0 * Column(1), ignoringFloat64casts. Column 1 is theCOUNToutput of the grouped aggregate.AggIntent::FrequencyEntropy(existing, not changed):col: Option<C>is the identity column (a column index here);accuracy: AccuracyTargetis the target; the value is in bits.Examples
End-to-end (
crates/integration-tests/tests/sql_frequency_entropy.rs). Tableflows(src_ip Utf8, keep Bool). Query:Each case also has one
("discard", false)row that theWHERE keepmust drop. The rewritten graph is executed natively:src_ipvaluesNULL(population_count = 0)a, a-0.0(bit pattern checked)a, a, b, bln 2a, a, a, b-0.75·ln 0.75 − 0.25·ln 0.25Accepted vs refused shapes (
crates/frontend-sql/tests/frequency_entropy.rs,declines_non_equivalent_entropy_shapes):COUNT(*)*1.0 / SUM(COUNT(*)) OVER (),LN, non-nullsrc_ipCOUNT(*)*2.0 / …-SUM(p*LOG2(p))… OVER (PARTITION BY src_ip)… OVER (ORDER BY src_ip)… GROUP BY src_ip HAVING COUNT(*) > 1src_ipNULLgroupOther tests:
recognizes_sql_entropy_in_nats: rewritten schema equals the original; original has no entropy intent.entropy_search_preserves_relational_alternative:search_workload_with_targetswithdefault_strategies()returns candidates both with and withoutFrequencyEntropy.entropy_accuracy_and_population_guard_are_separate: withEpsilonDelta { epsilon: 0.05, delta: 0.01 }(Q2's target), the entropy aggregate gets that target and the count aggregate getsExact.frequency_l2.rs,frequency_empty_input_guard_uses_an_exact_count: L2 withEpsilonDelta { 0.01, 0.01 }now has an exactCounton the right side of the join.Out of scope
SUM(…) OVER (),LN): feat(runtime): execute exact SQL entropy fallback #565.HAVING, nullable or other key types.docs/develop_docs/planner-layering-status.mdis updated to record what this step does and does not cover.Stack and validation
Stacked on #563 (
stack/509-18-exact-cardinality). Head:stack/509-19-sql-frequency-entropy. Next: #565.Validation (from the current PR body):
🤖 Generated with Claude Code