leerho opened a new pull request, #524:
URL: https://github.com/apache/datasketches-cpp/pull/524
## What changed
Adds an optional second flag to the existing `compact()` on
`update_theta_sketch_alloc`:
```cpp
compact_theta_sketch_alloc<Allocator> compact(bool ordered = true, bool trim
= false) const;
```
`trim = true` reduces the returned sketch to at most the nominal size `k`.
The default is unchanged, and because the parameter is defaulted and
`compact()` is not virtual here, every existing call site compiles untouched.
Thanks to @stojkomilos for raising the underlying need in #515. This takes a
different approach — a flag on the existing method rather than a new
`get_result()` — for two reasons: it avoids adding public API surface, and
trimming turns out to be lossy enough that it should be an explicit opt-in
rather than the natural way to obtain a result.
## Why trimming is opt-in
Relative error scales with `1 / sqrt(retained)`, so discarding entries
always widens the confidence bounds, whatever mode the source is in. Measured
at the default `lg_k`, comparing `compact()` against `compact(true, true)`:
| n | retained | 2-sigma width | trimmed width | widened |
|-------:|---------:|--------------:|--------------:|--------:|
| 20000 | 5644 | 895.13 | 1120.26 | 1.25x |
| 40000 | 6037 | 1900.72 | 2419.57 | 1.27x |
| 60000 | 4800 | 3331.83 | 3585.58 | 1.08x |
| 100000 | 4285 | 5873.90 | 6024.14 | 1.03x |
The cost depends on where in the rebuild cycle the sketch is caught, which a
caller cannot predict. Worst case is bounded: a sketch grown to just under the
`15/16 * 2k` rebuild threshold loses nearly half its entries, widening the
bounds by about `sqrt(15/8)`, roughly 37%.
Separately, a sketch in **exact mode** can retain more than `k` entries —
nothing has been evicted, so theta is still 1.0. Trimming there discards real
data and returns an estimating sketch:
```
n=5000 k=4096 retained=5000 source exact=YES
compact() -> estimate 5000 exact=YES
compact(true, true) -> estimate 4983.64 exact=no
```
Both effects are documented on the `trim` parameter and pinned by tests.
## Implementation
The trim path copies the retained entries first — the method is `const`, and
quick select would permute the live table — then partitions with
`std::nth_element` at 0-based index `k`, so `entries[k]` becomes the new theta
and the `k` entries below it are kept. Sorting happens only when an ordered
result is requested. This is the same pivot convention already used by
`rebuild()` in `theta_update_sketch_base_impl.hpp`, so trimming and rebuilding
cannot drift apart.
## How tested
New cases in `theta/test/theta_sketch_test.cpp`:
- **all four ordered/trim combinations**, including that the source sketch
is unmodified afterwards, that trimmed output matches `trim()` + `compact()` on
theta and retained set, and that every retained hash is strictly below the new
theta
- **exact to estimation conversion** at n=5000
- **bounds widening** for a source already in estimation mode
- **empty and below-k** sketches, where trimming is a no-op
```
./build/theta/test/theta_test
All tests passed (20250056 assertions in 89 test cases)
ctest --test-dir build
100% tests passed, 0 tests failed out of 17
```
## Note on the Java side
`datasketches-java` should get the same capability, but it cannot use the
same mechanism: `ThetaSketch.compact(boolean dstOrdered, MemorySegment dstSeg)`
is `abstract` and already occupies the two-argument slot, so Java needs an
overload rather than an added parameter, and `compact(true, null)` would be
ambiguous. Also worth care there: `QuickSelect.selectExcludingZeros` is 1-based
while `std::nth_element` is 0-based, so the Java port should assert `retained
== k` the way these tests do. That work is deliberately left to a separate PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_017EDHa7UhfW4eSj5L82panJ
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]