fix(hll): preserve estimator state across union transformations - #239
Merged
Conversation
…y union HllUnion::copy_or_downsample rebuilt the gadget with a bulk register merge, which marks the estimator out of order and drops the HIP accumulator. The source HIP was written back afterwards but the flag was not, so it was never used. Java and C++ carry both fields over from the source. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
tisonkun
requested review from
notfilippo and
tisonkun
and
a lite review from Copilot
August 29, 2026 02:11
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an HLL union correctness issue where merging an HLL-mode sketch into an empty HllUnion rebuilt registers via bulk operations, inadvertently invalidating the HIP estimator and causing estimates/bounds to use the out-of-order (composite) estimator path even after restoring the HIP accumulator.
Changes:
- Preserve both HIP accumulator and the estimator “out-of-order” validity flag when
HllUnion::updateinitializes its gadget from an array-mode sketch. - Add internal APIs to read/write the out-of-order flag on array sketches to enable faithful state carry-over.
- Update and extend integration tests to pin expected estimator/bounds behavior, and add a user-visible changelog entry.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
datasketches/src/hll/union.rs |
Copies HIP accumulator and out-of-order flag during copy/downsample initialization to preserve estimator semantics when unioning into an empty gadget. |
datasketches/src/hll/array8.rs |
Exposes internal setter/getter for the estimator out-of-order flag on Array8. |
datasketches/src/hll/array6.rs |
Exposes internal getter for the estimator out-of-order flag on Array6. |
datasketches/src/hll/array4.rs |
Exposes internal getter for the estimator out-of-order flag on Array4. |
tests-integration/tests/hll_test/union.rs |
Adjusts idempotency assertions and adds tests to ensure a single-sketch union reproduces estimates/bounds. |
CHANGELOG.md |
Documents the user-visible HllUnion estimator/bounds behavior fix. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Model HIP and composite estimates as mutually exclusive state so copies cannot construct mismatched accumulator and validity flags. Carry that state through union copying, downsampling, and result-type conversion.
Keep the regression tests at the public behavior boundary, cover result-type invariance in both HIP and composite modes, and simplify the estimator documentation and changelog entry.
tisonkun
approved these changes
Aug 29, 2026
tisonkun
left a comment
Member
There was a problem hiding this comment.
Good catch! Thanks for your contribution @jaideeppyne
I pushed one follow-up to refactor the EstimateState to leverage Rust's type system.
Merging ...
tisonkun
enabled auto-merge (squash)
August 29, 2026 07:35
The array estimator owns both HIP and composite estimation. Rename the component accordingly and reserve out-of-order terminology for the serialized flag, while core logic names the composite strategy directly.
The estimator module is already private, so narrowing the type to pub(super) adds no API boundary and unnecessarily couples it to the current module hierarchy.
tisonkun
disabled auto-merge
August 29, 2026 07:49
tisonkun
enabled auto-merge (squash)
August 29, 2026 07:49
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
HllUnionreconstructs HLL register arrays when it copies an HLL-mode input into an empty union, downsamples that input, or converts an HLL8 result to HLL4 or HLL6. These operations change the representation but not the logical sketch.Before this change, rebuilding the registers also rebuilt or invalidated estimator metadata. A single-input union could therefore switch from the HIP estimator to the composite estimator, and result-type conversion could preserve an estimate only conditionally while still using confidence bounds for the wrong estimator mode.
HIP depends on the ordered history of register updates. The composite estimate and KxQ cache depend only on the current registers. Treating the HIP accumulator and out-of-order flag as independently mutable fields made it possible to construct inconsistent combinations and made correctness depend on setter order.
Solution
Hip(accumulator)orComposite, while keeping register-derived KxQ state separate.Estimatorrather thanHipEstimator, because it owns both estimation strategies.This preserves the Java and C++ invariant that estimator state must accompany the registers for an isomorphic copy or downsample.
Reference implementations
copy_or_downsample.Observable behavior
lg_kis unchanged, its confidence bounds.lg_k.Tests
The regression coverage exercises all three source encodings and all three result encodings in HIP mode, result conversion after a composite-mode merge, single-input downsampling, and repeated-input stability.
Validated with:
cargo x checkcargo x testcargo x lintThe original issue was found with a Rust/C++ differential harness. The initial patch and harness were developed with Claude Code and manually verified; the estimator-state refactor and final review were developed with Codex and verified with the repository's full test and lint workflows.