From dc0f12118b945c1740373edf450e212c239b189e Mon Sep 17 00:00:00 2001 From: jaideeppyne Date: Wed, 30 Sep 2026 08:00:51 +0530 Subject: [PATCH] Bind HLL get_composite_estimate The composite estimator is order-independent. get_estimate() is the HIP estimator and can change with insertion order. --- src/hll_wrapper.cpp | 8 ++++++++ tests/hll_test.py | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/hll_wrapper.cpp b/src/hll_wrapper.cpp index 51041ad3..5da4bece 100644 --- a/src/hll_wrapper.cpp +++ b/src/hll_wrapper.cpp @@ -53,6 +53,10 @@ void init_hll(nb::module_ &m) { .def_prop_ro("tgt_type", &hll_sketch::get_target_type, "The HLL type (4, 6, or 8) when in estimation mode") .def("get_estimate", &hll_sketch::get_estimate, "Estimate of the distinct count of the input stream") + .def("get_composite_estimate", &hll_sketch::get_composite_estimate, + "Composite estimate of the distinct count. Less accurate than get_estimate() when the HIP estimator " + "is available, but order-independent, and the estimator used once a sketch has been unioned. " + "Intended for error characterization rather than normal use.") .def("get_lower_bound", &hll_sketch::get_lower_bound, nb::arg("num_std_devs"), "Returns the approximate lower error bound given the specified number of standard deviations in {1, 2, 3}") .def("get_upper_bound", &hll_sketch::get_upper_bound, nb::arg("num_std_devs"), @@ -111,6 +115,10 @@ void init_hll(nb::module_ &m) { .def_prop_ro("lg_config_k", &hll_union::get_lg_config_k, "Configured lg_k value for the union") .def("get_estimate", &hll_union::get_estimate, "Estimate of the distinct count of the input stream") + .def("get_composite_estimate", &hll_union::get_composite_estimate, + "Composite estimate of the distinct count. This is less accurate than get_estimate() and is used " + "automatically when a union cannot keep the HIP estimator. Intended for error characterization " + "rather than normal use.") .def("get_lower_bound", &hll_union::get_lower_bound, nb::arg("num_std_devs"), "Returns the approximate lower error bound given the specified number of standard deviations in {1, 2, 3}") .def("get_upper_bound", &hll_union::get_upper_bound, nb::arg("num_std_devs"), diff --git a/tests/hll_test.py b/tests/hll_test.py index fba7727b..19578f9a 100644 --- a/tests/hll_test.py +++ b/tests/hll_test.py @@ -122,7 +122,25 @@ def test_hll_union(self): sk = union.get_result() self.assertTrue(isinstance(sk, hll_sketch)) self.assertEqual(sk.tgt_type, tgt_hll_type.HLL_4) - + + def test_hll_composite_estimate_is_order_independent(self): + lgk = 12 + n = 5000 + forward = hll_sketch(lgk) + reverse = hll_sketch(lgk) + for i in range(n): + forward.update(i) + reverse.update(n - 1 - i) + + # HIP estimates can depend on insertion order. The composite estimator must not. + self.assertEqual(forward.get_composite_estimate(), reverse.get_composite_estimate()) + self.assertGreater(forward.get_composite_estimate(), 0) + + union = hll_union(lgk) + union.update(forward) + union.update(reverse) + self.assertEqual(union.get_composite_estimate(), union.get_result().get_composite_estimate()) + def generate_sketch(self, n, lgk, sk_type=tgt_hll_type.HLL_4, st_idx=0): sk = hll_sketch(lgk, sk_type) for i in range(st_idx, st_idx + n):