nx_sketch_lossy_counting.nx
buildroot/runtime/nx_sketch_lossy_counting.nx
about
sketch_lossy_counting.nx -- Manku-Motwani 2002 Lossy Counting.
THIRD major frequency-counting algorithm joining CMS / SpaceSaving /
Misra-Gries / CountSketch. Distinct technique: BUCKET-PRUNING.
ALGORITHM:
Stream divided into "buckets" of size w = ceil(1/epsilon).
For each item:
- if tracked: increment frequency
- else: insert with frequency=1, error=current_bucket-1
At each bucket boundary (every w items):
- decrement EVERY tracked item's frequency by 1
- REMOVE items with frequency = 0
GUARANTEE:
stored_freq <= true_freq (underestimate)
stored_freq + error >= true_freq (upper bound)
Items with true_freq > epsilon * N are NEVER missed
No item has stored_freq > true_freq (NEVER overestimates)
COMPARISON to other frequency algorithms:
- CMS: random projection, overestimate-only, O(1) query
- SpaceSaving: k-bounded counters, overestimate-only, top-K native
- Misra-Gries: k-bounded counters, underestimate-only
- CountSketch: median-based, UNBIASED, supports negative
- Lossy Counting (THIS): bucket-pruning, underestimate + explicit
error tracking per entry
MEMORY: O((1/epsilon) log(epsilon * N)) -- bounded but data-dependent.
We use a hash-set with linear probing; cap at NX_LC_MAX_ENTRIES.
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_sketch_types.nx
imported by: nobody (leaf or entry point)
structs
| 45 | struct LcEntry |
| 51 | struct LossyCounting |
consts
| 41 | const NX_LC_MIN_EPS_PPM: i64 = 100 // epsilon >= 0.0001 |
| 42 | const NX_LC_MAX_EPS_PPM: i64 = 500000 // epsilon <= 0.5 |
| 43 | const NX_LC_MAX_ENTRIES: i64 = 100000 |
| 62 | const NX_LC_EMPTY_KEY: i64 = 0 // assume real keys != 0 by remapping |
functions
| 66 | func nx_lc_is_pow2(n: i64) -> i64 called by 1: nx_lc_alloc |
| 72 | func nx_lc_alloc(capacity: i64, epsilon_ppm: i64) -> *LossyCounting |
| 101 | func nx_lc_hash(key: i64) -> i64 called by 1: nx_lc_probe |
| 106 | func nx_lc_entry_at(lc: *LossyCounting, idx: i64) -> *LcEntry |
| 113 | func nx_lc_probe(lc: *LossyCounting, key: i64) -> i64 |
| 144 | func nx_lc_prune(lc: *LossyCounting) -> i64 |
| 164 | func nx_lc_add(lc: *LossyCounting, key: i64) -> i64 |
| 195 | func nx_lc_estimate(lc: *LossyCounting, key: i64) -> i64 |
| 204 | func nx_lc_upper_bound(lc: *LossyCounting, key: i64) -> i64 |
| 213 | func nx_lc_max_undercount(lc: *LossyCounting) -> i64 called by 1: nx_lc_query |
| 217 | func nx_lc_query(lc: *LossyCounting, key: i64) -> *ApproxI64 |
| 227 | func nx_lc_n_entries(lc: *LossyCounting) -> i64 |
| 231 | func nx_lc_total_seen(lc: *LossyCounting) -> i64 |
| 235 | func nx_lc_memory_bytes(lc: *LossyCounting) -> i64 |