Skip to content

hydra: KLL CDF and UnivMon sum rows; KLL cell merge buffer in the footprint - #200

Merged
zzylol merged 6 commits into
mainfrom
hydra/more-statistics
Oct 11, 2026
Merged

zzylol merged 6 commits into
mainfrom
hydra/more-statistics

Conversation

@zzylol

@zzylol zzylol commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

PR 5 of the Hydra implementation spec, stacked on #199 (hydra/target-grouping).

What changed

  • hydra-kll-cdf (lib, polars): new variant, registered the same way the hydra-univmon-* rows register one row per statistic. It asks the KLL grid HydraQuery::Cdf(x) per subpopulation. The thresholds x are the group's own values at φ = 0.01..0.99 (99 points, index floor(φ·n)). It is scored by absolute CDF error |F̂_q(x) − F_q(x)| through a new comparator, SubpopCdfErrorGT (subpop-cdf-error, capability subpop-cdf), which reuses the rank-error truth (sorted values per group). The polars baseline takes the exact share at or below x over the same sorted groups. Per-group output from hydra: score any target grouping; per-group error output #199 (err_*, groups_scored, --per-group-out) applies.
  • hydra-univmon-sum (lib, polars): each record is inserted with count = Some(value) (new hydra_shared::update_counted), so the UnivMon grid's L1 is the group's sum. It is scored by relative error against exact per-group sums: new SubpopSumGT (subpop-sum), and the polars baseline is group_by(subset).agg(v.sum()). It builds at i64/u64 only. --dtype f64 or str is refused by name, and so is a value outside [0, i32::MAX] (the library's weight is an i32). The univmon drivers now take a feed fn so counted and weighted inserts share one code path.
  • KLL footprint: memory_hydra_kll now uses kll_lib_bytes::<f64>(cell_k) per cell, so each cell's merge buffer of cell_k items is counted, as it is for the kll-* lib rows. At rows=3 cols=1024 cell_k=200 this moves the footprint from 26,984,704 to 31,899,904 bytes (+1,600 B per cell).
  • Docs: README (Hydra flags section), docs/concepts.md capabilities, and the hydra datagen yaml headers.

Not done: hydra-hll cell_lg_k

asap_sketchlib 0.3.0 defines HydraCounter::HLL(HyperLogLog<ErtlMLE>), and HyperLogLog is an alias for HyperLogLogP14. P12 and P16 exist as standalone types but can't go in a Hydra cell (this is true of every sketchlib checkout I have too). A cell lg_k sweep needs an upstream change (a generic HLL cell), so it is out of scope here. hydra-hll stays at P14.

Tests (each checked by mutation)

  • aqpbm-core: sum truth adds the values (40/90 on the fixture, n_q = records), refuses an f64 value column by type; the CDF truth counts ties at or below (F_a(10) = 2/3), the exact answer scores 0, a zero answer misses by the share.
  • sketch-bench: KLL footprint is hand-derived as (1004 + 200)·8 + 62·8 per cell (fails without the buffer). The lib Cdf below k is exact inside the group (fails if asked as Quantile). The polars CDF baseline gives the share at or below (fails with <). Weighted L1 equals the exact sums (45, 7, 42) on a non-colliding 3×1024 grid (fails if inserted unweighted). Values i32::MAX+1 and -1 are refused by name. The polars sum baseline answers 40/30.
  • aqpbm-cli/tests/hydra_grouping.rs: hydra-kll-cdf and hydra-univmon-sum were added to the any-subset test (polars err_max = 0, lib ≤ 0.01 on a big grid, for groupings 1 and 0,1). A new test checks that --dtype f64 is refused by name at lib and polars.
  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace: clean.

Evidence (release build, --operations query --metrics accuracy,memory --runs 1)

hydra-kll-cdf, configs/datagen/hydra_columns_f64.yaml (200k rows), rows=3 cols=1024 cell_k=200:

group cols lib err_mean / p50 / p90 / max polars err_max groups lib bytes
0 0.0040 / 0.0036 / 0.0064 / 0.0163 0 200 31,899,904
0,1 0.130 / 0.096 / 0.261 / 0.82 0 9,950 31,899,904

hydra-univmon-sum, configs/datagen/hydra_columns.yaml (200k rows), rows=3 cols=1024 cell_heap_size=64 cell_sketch_row=3 cell_sketch_col=256 cell_layer_size=4 (relative error):

group cols lib err_mean / p50 / p90 / max polars err_max groups lib bytes
0 0.332 / 0.212 / 0.895 / 1.91 0 200 114,327,808
0,1 500.9 / 32.5 / 635 / 84,343 0 9,950 114,327,808

For comparison, the unweighted hydra-univmon-l1-norm at the same config with 0 gives err_mean 0.330 and err_max 1.96. At 0,1, about 10.2k subkeys per row land in 1,024 columns, so small groups take collision mass from heavy ones. That is the saturation the study (PR 6) maps; the exactness test above shows the rule holds on a grid without collisions.

--dtype f64 → hydra-univmon-sum/lib counts each value by itself, an integer weight, so builds at i64 or u64; --dtype f64 is not an integer.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis

@zzylol zzylol changed the title hydra: KLL CDF and UnivMon sum rows; HLL cell lg_k; KLL cell merge buffer hydra: KLL CDF and UnivMon sum rows; KLL cell merge buffer in the footprint Oct 10, 2026
@zzylol
zzylol force-pushed the hydra/more-statistics branch from 3966bdb to 2ba16fb Compare October 10, 2026 04:50
zzylol and others added 5 commits October 11, 2026 16:35
Every hydra-* row (lib and polars, all five variants and hydra-univmon's
four statistics) takes `--group-columns J,K,...` (default `0`, today's
behavior) in place of the hardcoded SCORED_LABEL_COLUMN. A subpopulation
probe is now positioned (`Vec<Option<String>>`, Some at the grouped
columns), so `hydra_shared::query` asks any non-empty subset of the key
columns and the polars baselines look up `subset_key` with that subset's
mask (`group_key`, generalizing `prefix_key`).

Each grouped comparator reports its per-group error in its own metric
(relative error; mean rank error for hydra-kll) and summarises it as
err_mean, err_p50, err_p90, err_max, groups_scored, plus schema_width,
records and fanned_mass. `--per-group-out PATH` writes the rows as
`group_key,n_q,error`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
…curacy

- Group keys escape `\`, `:` and `;` as polars_shared::subset_key does.
- Groups that cannot be scored (zero true statistic, e.g. zero entropy) are
  written with an empty `error`, so n_q sums to the stream's records.
- --per-group-out with no accuracy measurement is refused instead of
  writing an empty file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
- hydra-kll-cdf (lib, polars): asks the KLL grid HydraQuery::Cdf(x) at each
  group's own values at phi = 0.01..0.99, scored by absolute CDF error
  (SubpopCdfErrorGT, comparator subpop-cdf-error). The polars baseline is the
  exact share at or below x over the same sorted groups.
- hydra-univmon-sum (lib, polars): each record inserted with count = value,
  so the UnivMon grid's L1 is the group's sum, scored by relative error
  against exact per-group sums (SubpopSumGT, comparator subpop-sum).
  Integer value dtypes only; f64/str and values outside [0, i32::MAX] are
  refused by name.
- memory_hydra_kll counts each cell as kll_lib_bytes does, merge buffer of
  cell_k items included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
The grid clones its cells from one template, and a cloned Vec keeps no
capacity, so a cell's merge buffer is empty at build and grows to about
cell_k items at its first compaction. Say the footprint counts it at that
bound. The CDF test now asks a group of ties (at or below) and a mixed-tie
group on a grid wide enough that it shares no cell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
The lib row inserts each value as a UnivMon count and refuses negatives;
the truth (and so the polars baseline) accepted them, so a spec with
negatives ran on polars and failed on lib.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhcWJExZcmVqS6r6rEtjis
@zzylol
zzylol force-pushed the hydra/more-statistics branch from 00337aa to 6253698 Compare October 11, 2026 16:36
@zzylol
zzylol force-pushed the hydra/target-grouping branch from 8f73ed3 to bc770e3 Compare October 11, 2026 16:36
@zzylol
zzylol changed the base branch from hydra/target-grouping to main October 11, 2026 16:37
# Conflicts:
#	README.md
#	aqpbm-cli/src/rows/mod.rs
#	aqpbm-cli/src/rows/statistic.rs
#	aqpbm-cli/tests/hydra_grouping.rs
#	aqpbm-core/src/accuracy/subpopulation/frequency_vector.rs
#	aqpbm-core/src/accuracy/subpopulation/rank_error.rs
#	sketch-bench/src/wrappers/hydra_univmon/sketchlib.rs
@zzylol
zzylol merged commit eb120fa into main Oct 11, 2026
2 checks passed
zzylol added a commit that referenced this pull request Oct 11, 2026
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