Skip to content

feat(frontend-sql): recognize the SQL frequency L2 and entropy idioms - #598

Draft
zzylol wants to merge 2 commits into
stack/509-w7b-1-frequency-runtimefrom
stack/509-w7b-2-sql-frequency-recognition
Draft

zzylol wants to merge 2 commits into
stack/509-w7b-1-frequency-runtimefrom
stack/509-w7b-2-sql-frequency-recognition

Conversation

@zzylol

@zzylol zzylol commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Rebased on main d4869a7 (DF 54).

Wave 1 chain: #594 → #593 → #592 → #591 → #595 → #596 → #597 → #598 → #599 (on #589)

Why

#509 Example 2 writes two of its three statistics as SQL idioms:

-- Q2: Entropy(src_ip)
SELECT -SUM(p * LN(p)) FROM (SELECT COUNT(*) * 1.0 / SUM(COUNT(*)) OVER () AS p FROM flows WHERE ... GROUP BY src_ip);
-- Q3: L2(src_ip)
SELECT SQRT(SUM(c * c)) FROM (SELECT src_ip, COUNT(*) AS c FROM flows WHERE ... GROUP BY src_ip);

and says "The SQL frontend does not yet recognize the Q2 and Q3 forms as Entropy and L2. TODO: add this recognition to the per-language frontends." Without an Entropy/L2 intent, Pass 1 has nothing to attach the exact, summary and UnivMon candidates to, and Pass 2 cannot share a UnivMon with Q1.

The parked #562 (L2) and #564 (entropy) added these rules as a Pass 1 SemanticEquivalentRewriteStrategy in the old asap-aware-mapping. This PR ports them. Part of #509 / #580 (W8).

What

Before (Q2, lowered):

Project(-col0) ← Aggregate(SUM(p*LN(p))) ← Project(p = COUNT*1.0/total) ← SQLWindowFunc(SUM(COUNT) OVER ()) ← Aggregate(COUNT(*) GROUP BY src_ip) ← Scan(flows)

After:

Project(CASE WHEN population_count = 0 THEN NULL ELSE -(0.0 - frequency_entropy * LN 2) END)
└── Join(Cross)
    ├── Aggregate[FrequencyEntropy { col: src_ip, accuracy: <query target> }]
    └── Aggregate[Count { accuracy: Exact }]          (both read the filtered scan)

Q3 becomes the same shape with FrequencyL2 and no unit change. Stage 1 then offers PassThrough (the exact reducer from #597) and summary alternatives, including UnivMon, for each intent.

How

  • Rules (verbatim from feat(planner): recognize floating SQL frequency L2 idioms #562 + feat(planner): recognize normalized SQL entropy in nats #564). crates/frontend-sql/src/frequency.rs: follow projections only; require a single-key unfiltered COUNT(*) over a non-nullable Bool/Int64/Utf8 key, no HAVING; for L2, SQRT(SUM(c*c)) with a product already typed Float64; for entropy, LN, p = COUNT(*)[*1.0] / SUM(COUNT(*)) OVER () over the complete unordered frame. The result converts bits to nats, keeps SQL's -0.0, and uses an exact population count for SQL's empty-input NULL, so an approximate estimate never decides NULL. The schema must equal the original.
  • Adaptation: where the rule runs. The old rule was a Pass 1 replacement strategy used by the legacy search, which plan_stages does not run: Stage 1 only enumerates realizations of single-measure aggregates. Following the design doc's TODO ("per-language frontends"), lower_sql_dialect now applies the rule at the query root. So the original relational DAG is no longer kept as a separate candidate. The exact alternative is the native exact reducer, and the tests show that it agrees with the original SQL.
  • SQL sqrt (from feat(planner): recognize floating SQL frequency L2 idioms #562). The executor's expression compiler accepts sqrt on Int64/Float64/NULL.

Refused, as before: the uncast integer product (which is the design's literal Q3, because SQL Int64 overflow is observable), nullable keys, HAVING, scaled probabilities, LOG2, and partitioned or ordered windows.

Tests

Ported from #562/#564: frontend-sql/tests/frequency_{l2,entropy}.rs (recognition, refusals, accuracy propagation, exact population guard) and integration-tests/tests/sql_frequency_{l2,entropy}.rs (original and recognized L2 agree; entropy in nats, -0.0, NULL on empty input). The legacy-search inventory tests become stage1_offers_exact_and_summary_l2_alternatives over enumerate_local_logical_candidates. Without the lower_sql call, the recognition and entropy execution tests fail.

Gate: fmt and clippy (-D warnings) pass. cargo test --workspace: 1,538 passed, 10 ignored (#589: 1,517 / 10).

Ports #562, #564. Stacked on #597. Next: #599.

🤖 Generated with Claude Code

zzylol and others added 2 commits October 5, 2026 04:48
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>
Pass 1 takes the declared metric types since #593.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zzylol
zzylol force-pushed the stack/509-w7b-2-sql-frequency-recognition branch from 66bc1a6 to e211fba Compare October 5, 2026 06:21
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.

1 participant