Skip to content

Commit 301e495

Browse files
author
Miloš Stojko
committed
Add ordered parameter to update_theta_sketch::get_result()
Matches the ordered = true parameter of theta_union::get_result() and theta_intersection::get_result(); sorts only when ordered is requested. Co-authored-by: Isaac
1 parent c827ef0 commit 301e495

3 files changed

Lines changed: 36 additions & 23 deletions

File tree

theta/include/theta_sketch.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,11 @@ class update_theta_sketch_alloc: public theta_sketch_alloc<Allocator> {
338338

339339
/**
340340
* Produces a compact sketch trimmed to the nominal size k in a single pass.
341-
* Like trim() followed by compact(), but without rebuilding the hash table. Result is unordered.
341+
* Like trim() followed by compact(), but without rebuilding the hash table.
342+
* @param ordered optional flag to specify if an ordered sketch should be produced
342343
* @return compact sketch with at most k retained entries
343344
*/
344-
compact_theta_sketch_alloc<Allocator> get_result() const;
345+
compact_theta_sketch_alloc<Allocator> get_result(bool ordered = true) const;
345346

346347
virtual iterator begin();
347348
virtual iterator end();

theta/include/theta_sketch_impl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::compact(bool ordered
245245
}
246246

247247
template<typename A>
248-
compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::get_result() const {
248+
compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::get_result(bool ordered) const {
249249
std::vector<uint64_t, A> entries(table_.allocator_);
250250
if (is_empty()) {
251251
return compact_theta_sketch_alloc<A>(true, true, get_seed_hash(), get_theta64(), std::move(entries));
@@ -259,7 +259,7 @@ compact_theta_sketch_alloc<A> update_theta_sketch_alloc<A>::get_result() const {
259259
theta = entries[nominal_num];
260260
entries.erase(entries.begin() + nominal_num, entries.end());
261261
}
262-
const bool ordered = entries.size() <= 1;
262+
if (ordered) std::sort(entries.begin(), entries.end());
263263
return compact_theta_sketch_alloc<A>(false, ordered, get_seed_hash(), theta, std::move(entries));
264264
}
265265

theta/test/theta_sketch_test.cpp

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -175,40 +175,52 @@ TEST_CASE("theta sketch: get_result trims to k in one pass", "[theta_sketch]") {
175175
const uint32_t k = 1 << theta_constants::DEFAULT_LG_K;
176176
REQUIRE(update_sketch.get_num_retained() > k); // over-provisioned before trimming
177177

178-
compact_theta_sketch result = update_sketch.get_result();
179-
REQUIRE_FALSE(result.is_empty());
180-
REQUIRE(result.is_estimation_mode());
181-
REQUIRE(result.get_num_retained() == k); // trimmed to nominal size
182-
REQUIRE_FALSE(result.is_ordered()); // unordered: no sort performed
183-
184-
// fused get_result() must match trim() + compact()
178+
// default is ordered, matching union/intersection get_result
179+
compact_theta_sketch ordered_result = update_sketch.get_result();
180+
REQUIRE_FALSE(ordered_result.is_empty());
181+
REQUIRE(ordered_result.is_estimation_mode());
182+
REQUIRE(ordered_result.get_num_retained() == k); // trimmed to nominal size
183+
REQUIRE(ordered_result.is_ordered());
184+
REQUIRE(std::is_sorted(ordered_result.begin(), ordered_result.end()));
185+
186+
// fused get_result(true) must match trim() + compact(true)
185187
update_theta_sketch trimmed = update_sketch;
186188
trimmed.trim();
187-
compact_theta_sketch expected = trimmed.compact(false);
188-
REQUIRE(result.get_theta64() == expected.get_theta64());
189-
REQUIRE(result.get_num_retained() == expected.get_num_retained());
190-
REQUIRE(result.get_estimate() == expected.get_estimate());
191-
192-
// same set of retained hashes (order-independent)
193-
std::vector<uint64_t> a(result.begin(), result.end());
194-
std::vector<uint64_t> b(expected.begin(), expected.end());
195-
std::sort(a.begin(), a.end());
196-
std::sort(b.begin(), b.end());
197-
REQUIRE(a == b);
189+
compact_theta_sketch expected = trimmed.compact(true);
190+
REQUIRE(ordered_result.get_theta64() == expected.get_theta64());
191+
REQUIRE(ordered_result.get_num_retained() == expected.get_num_retained());
192+
REQUIRE(ordered_result.get_estimate() == expected.get_estimate());
193+
REQUIRE(std::vector<uint64_t>(ordered_result.begin(), ordered_result.end())
194+
== std::vector<uint64_t>(expected.begin(), expected.end()));
195+
196+
// unordered variant: same trimmed set and theta, no sort
197+
compact_theta_sketch unordered_result = update_sketch.get_result(false);
198+
REQUIRE_FALSE(unordered_result.is_ordered());
199+
REQUIRE(unordered_result.get_num_retained() == k);
200+
REQUIRE(unordered_result.get_theta64() == expected.get_theta64());
201+
std::vector<uint64_t> unordered_hashes(unordered_result.begin(), unordered_result.end());
202+
std::sort(unordered_hashes.begin(), unordered_hashes.end());
203+
REQUIRE(unordered_hashes == std::vector<uint64_t>(expected.begin(), expected.end()));
198204
}
199205

200206
TEST_CASE("theta sketch: get_result on empty and below-k sketches", "[theta_sketch]") {
201207
compact_theta_sketch empty_result = update_theta_sketch::builder().build().get_result();
202208
REQUIRE(empty_result.is_empty());
203209
REQUIRE(empty_result.get_num_retained() == 0);
210+
REQUIRE(empty_result.is_ordered());
204211

205212
update_theta_sketch small = update_theta_sketch::builder().build();
206213
for (int i = 0; i < 100; i++) small.update(i);
207214
REQUIRE_FALSE(small.is_estimation_mode());
208-
compact_theta_sketch small_result = small.get_result();
215+
216+
compact_theta_sketch small_result = small.get_result(); // default ordered
209217
REQUIRE_FALSE(small_result.is_estimation_mode());
210218
REQUIRE(small_result.get_num_retained() == 100); // below k: nothing trimmed
211219
REQUIRE(small_result.get_estimate() == Approx(100.0));
220+
REQUIRE(small_result.is_ordered());
221+
REQUIRE(std::is_sorted(small_result.begin(), small_result.end()));
222+
223+
REQUIRE_FALSE(small.get_result(false).is_ordered()); // unordered variant
212224
}
213225

214226
TEST_CASE("theta sketch: deserialize compact v1 empty from java", "[theta_sketch]") {

0 commit comments

Comments
 (0)