datasketches-cpp
Loading...
Searching...
No Matches
req_sketch_impl.hpp
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#ifndef REQ_SKETCH_IMPL_HPP_
21#define REQ_SKETCH_IMPL_HPP_
22
23#include <sstream>
24#include <stdexcept>
25#include <type_traits>
26
27namespace datasketches {
28
29template<typename T, typename C, typename A>
30req_sketch<T, C, A>::req_sketch(uint16_t k, bool hra, const C& comparator, const A& allocator):
31comparator_(comparator),
32allocator_(allocator),
33k_(std::max<uint16_t>(static_cast<int>(k) & -2, static_cast<int>(req_constants::MIN_K))), //rounds down one if odd
34hra_(hra),
35max_nom_size_(0),
36num_retained_(0),
37n_(0),
38compactors_(allocator),
39min_item_(),
40max_item_(),
41sorted_view_(nullptr)
42{
43 if (k > req_constants::MAX_K) {
44 throw std::invalid_argument("K must be <= " + std::to_string(req_constants::MAX_K) + ": " + std::to_string(k));
45 }
46 grow();
47}
48
49template<typename T, typename C, typename A>
50req_sketch<T, C, A>::~req_sketch() {
51 reset_sorted_view();
52}
53
54template<typename T, typename C, typename A>
55req_sketch<T, C, A>::req_sketch(const req_sketch& other):
56comparator_(other.comparator_),
57allocator_(other.allocator_),
58k_(other.k_),
59hra_(other.hra_),
60max_nom_size_(other.max_nom_size_),
61num_retained_(other.num_retained_),
62n_(other.n_),
63compactors_(other.compactors_),
64min_item_(other.min_item_),
65max_item_(other.max_item_),
66sorted_view_(nullptr)
67{}
68
69template<typename T, typename C, typename A>
70req_sketch<T, C, A>::req_sketch(req_sketch&& other) noexcept :
71comparator_(std::move(other.comparator_)),
72allocator_(std::move(other.allocator_)),
73k_(other.k_),
74hra_(other.hra_),
75max_nom_size_(other.max_nom_size_),
76num_retained_(other.num_retained_),
77n_(other.n_),
78compactors_(std::move(other.compactors_)),
79min_item_(std::move(other.min_item_)),
80max_item_(std::move(other.max_item_)),
81sorted_view_(nullptr)
82{}
83
84template<typename T, typename C, typename A>
85req_sketch<T, C, A>& req_sketch<T, C, A>::operator=(const req_sketch& other) {
86 req_sketch copy(other);
87 std::swap(comparator_, copy.comparator_);
88 std::swap(allocator_, copy.allocator_);
89 std::swap(k_, copy.k_);
90 std::swap(hra_, copy.hra_);
91 std::swap(max_nom_size_, copy.max_nom_size_);
92 std::swap(num_retained_, copy.num_retained_);
93 std::swap(n_, copy.n_);
94 std::swap(compactors_, copy.compactors_);
95 std::swap(min_item_, copy.min_item_);
96 std::swap(max_item_, copy.max_item_);
97 reset_sorted_view();
98 return *this;
99}
100
101template<typename T, typename C, typename A>
102req_sketch<T, C, A>& req_sketch<T, C, A>::operator=(req_sketch&& other) {
103 std::swap(comparator_, other.comparator_);
104 std::swap(allocator_, other.allocator_);
105 std::swap(k_, other.k_);
106 std::swap(hra_, other.hra_);
107 std::swap(max_nom_size_, other.max_nom_size_);
108 std::swap(num_retained_, other.num_retained_);
109 std::swap(n_, other.n_);
110 std::swap(compactors_, other.compactors_);
111 std::swap(min_item_, other.min_item_);
112 std::swap(max_item_, other.max_item_);
113 reset_sorted_view();
114 return *this;
115}
116
117template<typename T, typename C, typename A>
118template<typename TT, typename CC, typename AA>
119req_sketch<T, C, A>::req_sketch(const req_sketch<TT, CC, AA>& other, const C& comparator, const A& allocator):
120comparator_(comparator),
121allocator_(allocator),
122k_(other.k_),
123hra_(other.hra_),
124max_nom_size_(other.max_nom_size_),
125num_retained_(other.num_retained_),
126n_(other.n_),
127compactors_(allocator),
128min_item_(other.min_item_),
129max_item_(other.max_item_),
130sorted_view_(nullptr)
131{
132 static_assert(
133 std::is_constructible<T, TT>::value,
134 "Type converting constructor requires new type to be constructible from existing type"
135 );
136 compactors_.reserve(other.compactors_.size());
137 for (const auto& compactor: other.compactors_) {
138 compactors_.push_back(req_compactor<T, C, A>(compactor, comparator_, allocator_));
139 }
140}
141
142template<typename T, typename C, typename A>
144 return k_;
145}
146
147template<typename T, typename C, typename A>
149 return hra_;
150}
151
152template<typename T, typename C, typename A>
154 return n_ == 0;
155}
156
157template<typename T, typename C, typename A>
159 return n_;
160}
161
162template<typename T, typename C, typename A>
164 return num_retained_;
165}
166
167template<typename T, typename C, typename A>
169 return compactors_.size() > 1;
170}
171
172template<typename T, typename C, typename A>
173template<typename FwdT>
175 if (!check_update_item(item)) { return; }
176 if (is_empty()) {
177 min_item_.emplace(item);
178 max_item_.emplace(item);
179 } else {
180 if (comparator_(item, *min_item_)) *min_item_ = item;
181 if (comparator_(*max_item_, item)) *max_item_ = item;
182 }
183 compactors_[0].append(std::forward<FwdT>(item));
184 ++num_retained_;
185 ++n_;
186 if (num_retained_ == max_nom_size_) compress();
187 reset_sorted_view();
188}
189
190template<typename T, typename C, typename A>
191template<typename FwdSk>
192void req_sketch<T, C, A>::merge(FwdSk&& other) {
193 if (is_HRA() != other.is_HRA()) throw std::invalid_argument("merging HRA and LRA is not valid");
194 if (other.is_empty()) return;
195 if (is_empty()) {
196 min_item_.emplace(conditional_forward<FwdSk>(*other.min_item_));
197 max_item_.emplace(conditional_forward<FwdSk>(*other.max_item_));
198 } else {
199 if (comparator_(*other.min_item_, *min_item_)) *min_item_ = conditional_forward<FwdSk>(*other.min_item_);
200 if (comparator_(*max_item_, *other.max_item_)) *max_item_ = conditional_forward<FwdSk>(*other.max_item_);
201 }
202 // grow until this has at least as many compactors as other
203 while (get_num_levels() < other.get_num_levels()) grow();
204 // merge the items in all height compactors
205 for (size_t i = 0; i < other.get_num_levels(); ++i) {
206 compactors_[i].merge(conditional_forward<FwdSk>(other.compactors_[i]));
207 }
208 n_ += other.n_;
209 update_max_nom_size();
210 update_num_retained();
211 if (num_retained_ >= max_nom_size_) compress();
212 reset_sorted_view();
213}
214
215template<typename T, typename C, typename A>
217 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
218 return *min_item_;
219}
220
221template<typename T, typename C, typename A>
223 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
224 return *max_item_;
225}
226
227template<typename T, typename C, typename A>
229 return comparator_;
230}
231
232template<typename T, typename C, typename A>
234 return allocator_;
235}
236
237template<typename T, typename C, typename A>
238double req_sketch<T, C, A>::get_rank(const T& item, bool inclusive) const {
239 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
240 uint64_t weight = 0;
241 for (const auto& compactor: compactors_) {
242 weight += compactor.compute_weight(item, inclusive);
243 }
244 return static_cast<double>(weight) / n_;
245}
246
247template<typename T, typename C, typename A>
248auto req_sketch<T, C, A>::get_PMF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
249 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
250 setup_sorted_view();
251 return sorted_view_->get_PMF(split_points, size, inclusive);
252}
253
254template<typename T, typename C, typename A>
255auto req_sketch<T, C, A>::get_CDF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
256 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
257 setup_sorted_view();
258 return sorted_view_->get_CDF(split_points, size, inclusive);
259}
260
261template<typename T, typename C, typename A>
262auto req_sketch<T, C, A>::get_quantile(double rank, bool inclusive) const -> quantile_return_type {
263 if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
264 if ((rank < 0.0) || (rank > 1.0)) {
265 throw std::invalid_argument("Normalized rank cannot be less than 0 or greater than 1");
266 }
267 // possible side-effect of sorting level zero
268 setup_sorted_view();
269 return sorted_view_->get_quantile(rank, inclusive);
270}
271
272template<typename T, typename C, typename A>
274 if (!compactors_[0].is_sorted()) {
275 const_cast<Compactor&>(compactors_[0]).sort(); // allow this side effect
276 }
277 quantiles_sorted_view<T, C, A> view(get_num_retained(), comparator_, allocator_);
278
279 for (auto& compactor: compactors_) {
280 view.add(compactor.begin(), compactor.end(), 1ULL << compactor.get_lg_weight());
281 }
282
283 view.convert_to_cummulative();
284 return view;
285}
286
287template<typename T, typename C, typename A>
288double req_sketch<T, C, A>::get_rank_lower_bound(double rank, uint8_t num_std_dev) const {
289 return get_rank_lb(get_k(), get_num_levels(), rank, num_std_dev, get_n(), hra_);
290}
291
292template<typename T, typename C, typename A>
293double req_sketch<T, C, A>::get_rank_upper_bound(double rank, uint8_t num_std_dev) const {
294 return get_rank_ub(get_k(), get_num_levels(), rank, num_std_dev, get_n(), hra_);
295}
296
297template<typename T, typename C, typename A>
298double req_sketch<T, C, A>::get_RSE(uint16_t k, double rank, bool hra, uint64_t n) {
299 return get_rank_ub(k, 2, rank, 1, n, hra) - rank;
300}
301
302template<typename T, typename C, typename A>
303double req_sketch<T, C, A>::get_rank_lb(uint16_t k, uint8_t num_levels, double rank, uint8_t num_std_dev, uint64_t n, bool hra) {
304 if (is_exact_rank(k, num_levels, rank, n, hra)) return rank;
305 const double relative = relative_rse_factor() / k * (hra ? 1.0 - rank : rank);
306 const double fixed = FIXED_RSE_FACTOR / k;
307 const double lb_rel = rank - num_std_dev * relative;
308 const double lb_fix = rank - num_std_dev * fixed;
309 return std::max(lb_rel, lb_fix);
310}
311
312template<typename T, typename C, typename A>
313double req_sketch<T, C, A>::get_rank_ub(uint16_t k, uint8_t num_levels, double rank, uint8_t num_std_dev, uint64_t n, bool hra) {
314 if (is_exact_rank(k, num_levels, rank, n, hra)) return rank;
315 const double relative = relative_rse_factor() / k * (hra ? 1.0 - rank : rank);
316 const double fixed = FIXED_RSE_FACTOR / k;
317 const double ub_rel = rank + num_std_dev * relative;
318 const double ub_fix = rank + num_std_dev * fixed;
319 return std::min(ub_rel, ub_fix);
320}
321
322template<typename T, typename C, typename A>
323bool req_sketch<T, C, A>::is_exact_rank(uint16_t k, uint8_t num_levels, double rank, uint64_t n, bool hra) {
324 const unsigned base_cap = k * req_constants::INIT_NUM_SECTIONS;
325 if (num_levels == 1 || n <= base_cap) return true;
326 const double exact_rank_thresh = static_cast<double>(base_cap) / n;
327 return (hra && rank >= 1.0 - exact_rank_thresh) || (!hra && rank <= exact_rank_thresh);
328}
329
330template<typename T, typename C, typename A>
331double req_sketch<T, C, A>::relative_rse_factor() {
332 return sqrt(0.0512 / req_constants::INIT_NUM_SECTIONS);
333}
334
335// implementation for fixed-size arithmetic types (integral and floating point)
336template<typename T, typename C, typename A>
337template<typename TT, typename SerDe, typename std::enable_if<std::is_arithmetic<TT>::value, int>::type>
339 size_t size = PREAMBLE_SIZE_BYTES;
340 if (is_empty()) return size;
341 if (is_estimation_mode()) {
342 size += sizeof(n_) + sizeof(TT) * 2; // min and max
343 }
344 if (n_ == 1) {
345 size += sizeof(TT);
346 } else {
347 for (const auto& compactor: compactors_) size += compactor.get_serialized_size_bytes(sd);
348 }
349 return size;
350}
351
352// implementation for all other types
353template<typename T, typename C, typename A>
354template<typename TT, typename SerDe, typename std::enable_if<!std::is_arithmetic<TT>::value, int>::type>
355size_t req_sketch<T, C, A>::get_serialized_size_bytes(const SerDe& sd) const {
356 size_t size = PREAMBLE_SIZE_BYTES;
357 if (is_empty()) return size;
358 if (is_estimation_mode()) {
359 size += sizeof(n_);
360 size += sd.size_of_item(*min_item_);
361 size += sd.size_of_item(*max_item_);
362 }
363 if (n_ == 1) {
364 size += sd.size_of_item(*compactors_[0].begin());
365 } else {
366 for (const auto& compactor: compactors_) size += compactor.get_serialized_size_bytes(sd);
367 }
368 return size;
369}
370
371template<typename T, typename C, typename A>
372template<typename SerDe>
373void req_sketch<T, C, A>::serialize(std::ostream& os, const SerDe& sd) const {
374 const uint8_t preamble_ints = is_estimation_mode() ? 4 : 2;
375 write(os, preamble_ints);
376 const uint8_t serial_version = SERIAL_VERSION;
377 write(os, serial_version);
378 const uint8_t family = FAMILY;
379 write(os, family);
380 const bool raw_items = n_ <= req_constants::MIN_K;
381 const uint8_t flags_byte(
382 (is_empty() ? 1 << flags::IS_EMPTY : 0)
383 | (hra_ ? 1 << flags::IS_HIGH_RANK : 0)
384 | (raw_items ? 1 << flags::RAW_ITEMS : 0)
385 | (compactors_[0].is_sorted() ? 1 << flags::IS_LEVEL_ZERO_SORTED : 0)
386 );
387 write(os, flags_byte);
388 write(os, k_);
389 const uint8_t num_levels = is_empty() ? 0 : get_num_levels();
390 write(os, num_levels);
391 const uint8_t num_raw_items = raw_items ? static_cast<uint8_t>(n_) : 0;
392 write(os, num_raw_items);
393 if (is_empty()) return;
394 if (is_estimation_mode()) {
395 write(os, n_);
396 sd.serialize(os, &*min_item_, 1);
397 sd.serialize(os, &*max_item_, 1);
398 }
399 if (raw_items) {
400 sd.serialize(os, compactors_[0].begin(), num_raw_items);
401 } else {
402 for (const auto& compactor: compactors_) compactor.serialize(os, sd);
403 }
404}
405
406template<typename T, typename C, typename A>
407template<typename SerDe>
408auto req_sketch<T, C, A>::serialize(unsigned header_size_bytes, const SerDe& sd) const -> vector_bytes {
409 const size_t size = header_size_bytes + get_serialized_size_bytes(sd);
410 vector_bytes bytes(size, 0, allocator_);
411 uint8_t* ptr = bytes.data() + header_size_bytes;
412 const uint8_t* end_ptr = ptr + size;
413
414 const uint8_t preamble_ints = is_estimation_mode() ? 4 : 2;
415 ptr += copy_to_mem(preamble_ints, ptr);
416 const uint8_t serial_version = SERIAL_VERSION;
417 ptr += copy_to_mem(serial_version, ptr);
418 const uint8_t family = FAMILY;
419 ptr += copy_to_mem(family, ptr);
420 const bool raw_items = n_ <= req_constants::MIN_K;
421 const uint8_t flags_byte(
422 (is_empty() ? 1 << flags::IS_EMPTY : 0)
423 | (hra_ ? 1 << flags::IS_HIGH_RANK : 0)
424 | (raw_items ? 1 << flags::RAW_ITEMS : 0)
425 | (compactors_[0].is_sorted() ? 1 << flags::IS_LEVEL_ZERO_SORTED : 0)
426 );
427 ptr += copy_to_mem(flags_byte, ptr);
428 ptr += copy_to_mem(k_, ptr);
429 const uint8_t num_levels = is_empty() ? 0 : get_num_levels();
430 ptr += copy_to_mem(num_levels, ptr);
431 const uint8_t num_raw_items = raw_items ? static_cast<uint8_t>(n_) : 0;
432 ptr += copy_to_mem(num_raw_items, ptr);
433 if (!is_empty()) {
434 if (is_estimation_mode()) {
435 ptr += copy_to_mem(n_, ptr);
436 ptr += sd.serialize(ptr, end_ptr - ptr, &*min_item_, 1);
437 ptr += sd.serialize(ptr, end_ptr - ptr, &*max_item_, 1);
438 }
439 if (raw_items) {
440 ptr += sd.serialize(ptr, end_ptr - ptr, compactors_[0].begin(), num_raw_items);
441 } else {
442 for (const auto& compactor: compactors_) ptr += compactor.serialize(ptr, end_ptr - ptr, sd);
443 }
444 }
445 return bytes;
446}
447
448template<typename T, typename C, typename A>
449template<typename SerDe>
450req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(std::istream& is, const SerDe& sd, const C& comparator, const A& allocator) {
451 const auto preamble_ints = read<uint8_t>(is);
452 const auto serial_version = read<uint8_t>(is);
453 const auto family_id = read<uint8_t>(is);
454 const auto flags_byte = read<uint8_t>(is);
455 const auto k = read<uint16_t>(is);
456 const auto num_levels = read<uint8_t>(is);
457 const auto num_raw_items = read<uint8_t>(is);
458
459 check_k(k);
460 check_preamble_ints(preamble_ints, num_levels);
461 check_serial_version(serial_version);
462 check_family_id(family_id);
463
464 if (!is.good()) throw std::runtime_error("error reading from std::istream");
465 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
466 const bool hra = flags_byte & (1 << flags::IS_HIGH_RANK);
467 if (is_empty) return req_sketch(k, hra, comparator, allocator);
468
469 optional<T> min_item;
470 optional<T> max_item;
471
472 const bool raw_items = flags_byte & (1 << flags::RAW_ITEMS);
473 check_num_levels(num_levels, raw_items);
474 const bool is_level_0_sorted = flags_byte & (1 << flags::IS_LEVEL_ZERO_SORTED);
475 std::vector<Compactor, AllocCompactor> compactors(allocator);
476
477 uint64_t n = 1;
478 if (num_levels > 1) {
479 n = read<uint64_t>(is);
480 // Space to deserialize min and max.
481 // serde::deserialize expects allocated but not initialized storage.
482 typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
483 T* tmp = reinterpret_cast<T*>(&tmp_storage);
484
485 sd.deserialize(is, tmp, 1);
486 // serde call did not throw, repackage and cleanup
487 min_item.emplace(std::move(*tmp));
488 tmp->~T();
489 sd.deserialize(is, tmp, 1);
490 // serde call did not throw, repackage and cleanup
491 max_item.emplace(std::move(*tmp));
492 tmp->~T();
493 }
494
495 if (raw_items) {
496 compactors.push_back(Compactor::deserialize(is, sd, comparator, allocator, is_level_0_sorted, k, num_raw_items, hra));
497 } else {
498 for (size_t i = 0; i < num_levels; ++i) {
499 compactors.push_back(Compactor::deserialize(is, sd, comparator, allocator, i == 0 ? is_level_0_sorted : true, hra,
500 k, static_cast<uint8_t>(i)));
501 }
502 }
503 if (num_levels == 1) {
504 if (compactors[0].get_num_items() == 0) throw std::invalid_argument("Possible corruption: non-empty sketch with no items");
505 const auto begin = compactors[0].begin();
506 const auto end = compactors[0].end();
507 n = compactors[0].get_num_items();
508 auto min_it = begin;
509 auto max_it = begin;
510 for (auto it = begin; it != end; ++it) {
511 if (comparator(*it, *min_it)) min_it = it;
512 if (comparator(*max_it, *it)) max_it = it;
513 }
514 min_item.emplace(*min_it);
515 max_item.emplace(*max_it);
516 }
517
518 if (!is.good()) throw std::runtime_error("error reading from std::istream");
519 return req_sketch(k, hra, n, std::move(min_item), std::move(max_item), std::move(compactors), comparator);
520}
521
522template<typename T, typename C, typename A>
523template<typename SerDe>
524req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(const void* bytes, size_t size, const SerDe& sd, const C& comparator, const A& allocator) {
525 ensure_minimum_memory(size, 8);
526 const char* ptr = static_cast<const char*>(bytes);
527 const char* end_ptr = static_cast<const char*>(bytes) + size;
528
529 uint8_t preamble_ints;
530 ptr += copy_from_mem(ptr, preamble_ints);
531 uint8_t serial_version;
532 ptr += copy_from_mem(ptr, serial_version);
533 uint8_t family_id;
534 ptr += copy_from_mem(ptr, family_id);
535 uint8_t flags_byte;
536 ptr += copy_from_mem(ptr, flags_byte);
537 uint16_t k;
538 ptr += copy_from_mem(ptr, k);
539 uint8_t num_levels;
540 ptr += copy_from_mem(ptr, num_levels);
541 uint8_t num_raw_items;
542 ptr += copy_from_mem(ptr, num_raw_items);
543
544 check_k(k);
545 check_preamble_ints(preamble_ints, num_levels);
546 check_serial_version(serial_version);
547 check_family_id(family_id);
548
549 const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
550 const bool hra = flags_byte & (1 << flags::IS_HIGH_RANK);
551 if (is_empty) return req_sketch(k, hra, comparator, allocator);
552
553 optional<T> min_item;
554 optional<T> max_item;
555
556 const bool raw_items = flags_byte & (1 << flags::RAW_ITEMS);
557 check_num_levels(num_levels, raw_items);
558 const bool is_level_0_sorted = flags_byte & (1 << flags::IS_LEVEL_ZERO_SORTED);
559 std::vector<Compactor, AllocCompactor> compactors(allocator);
560
561 uint64_t n = 1;
562 if (num_levels > 1) {
563 ensure_minimum_memory(end_ptr - ptr, sizeof(n));
564 ptr += copy_from_mem(ptr, n);
565 // Space to deserialize min and max.
566 // serde::deserialize expects allocated but not initialized storage.
567 typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
568 T* tmp = reinterpret_cast<T*>(&tmp_storage);
569
570 ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
571 // serde call did not throw, repackage and cleanup
572 min_item.emplace(std::move(*tmp));
573 tmp->~T();
574 ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
575 // serde call did not throw, repackage and cleanup
576 max_item.emplace(std::move(*tmp));
577 tmp->~T();
578 }
579
580 if (raw_items) {
581 auto pair = Compactor::deserialize(ptr, end_ptr - ptr, sd, comparator, allocator, is_level_0_sorted, k, num_raw_items, hra);
582 compactors.push_back(std::move(pair.first));
583 ptr += pair.second;
584 } else {
585 for (size_t i = 0; i < num_levels; ++i) {
586 auto pair = Compactor::deserialize(ptr, end_ptr - ptr, sd, comparator, allocator, i == 0 ? is_level_0_sorted : true, hra,
587 k, static_cast<uint8_t>(i));
588 compactors.push_back(std::move(pair.first));
589 ptr += pair.second;
590 }
591 }
592 if (num_levels == 1) {
593 if (compactors[0].get_num_items() == 0) throw std::invalid_argument("Possible corruption: non-empty sketch with no items");
594 const auto begin = compactors[0].begin();
595 const auto end = compactors[0].end();
596 n = compactors[0].get_num_items();
597 auto min_it = begin;
598 auto max_it = begin;
599 for (auto it = begin; it != end; ++it) {
600 if (comparator(*it, *min_it)) min_it = it;
601 if (comparator(*max_it, *it)) max_it = it;
602 }
603 min_item.emplace(*min_it);
604 max_item.emplace(*max_it);
605 }
606
607 return req_sketch(k, hra, n, std::move(min_item), std::move(max_item), std::move(compactors), comparator);
608}
609
610template<typename T, typename C, typename A>
611void req_sketch<T, C, A>::grow() {
612 const uint8_t lg_weight = get_num_levels();
613 compactors_.push_back(Compactor(hra_, lg_weight, k_, comparator_, allocator_));
614 update_max_nom_size();
615}
616
617template<typename T, typename C, typename A>
618uint8_t req_sketch<T, C, A>::get_num_levels() const {
619 return static_cast<uint8_t>(compactors_.size());
620}
621
622template<typename T, typename C, typename A>
623void req_sketch<T, C, A>::update_max_nom_size() {
624 max_nom_size_ = 0;
625 for (const auto& compactor: compactors_) max_nom_size_ += compactor.get_nom_capacity();
626}
627
628template<typename T, typename C, typename A>
629void req_sketch<T, C, A>::update_num_retained() {
630 num_retained_ = 0;
631 for (const auto& compactor: compactors_) num_retained_ += compactor.get_num_items();
632}
633
634template<typename T, typename C, typename A>
635void req_sketch<T, C, A>::compress() {
636 for (size_t h = 0; h < compactors_.size(); ++h) {
637 if (compactors_[h].get_num_items() >= compactors_[h].get_nom_capacity()) {
638 if (h == 0) compactors_[0].sort();
639 if (h + 1 >= get_num_levels()) { // at the top?
640 grow(); // add a level, increases max_nom_size
641 }
642 auto pair = compactors_[h].compact(compactors_[h + 1]);
643 num_retained_ -= pair.first;
644 max_nom_size_ += pair.second;
645 if (LAZY_COMPRESSION && num_retained_ < max_nom_size_) break;
646 }
647 }
648}
649
650template<typename T, typename C, typename A>
651string<A> req_sketch<T, C, A>::to_string(bool print_levels, bool print_items) const {
652 // Using a temporary stream for implementation here does not comply with AllocatorAwareContainer requirements.
653 // The stream does not support passing an allocator instance, and alternatives are complicated.
654 std::ostringstream os;
655 os << "### REQ sketch summary:" << std::endl;
656 os << " K : " << k_ << std::endl;
657 os << " High Rank Acc : " << (hra_ ? "true" : "false") << std::endl;
658 os << " Empty : " << (is_empty() ? "true" : "false") << std::endl;
659 os << " Estimation mode: " << (is_estimation_mode() ? "true" : "false") << std::endl;
660 os << " Sorted : " << (compactors_[0].is_sorted() ? "true" : "false") << std::endl;
661 os << " N : " << n_ << std::endl;
662 os << " Levels : " << compactors_.size() << std::endl;
663 os << " Retained items : " << num_retained_ << std::endl;
664 os << " Capacity items : " << max_nom_size_ << std::endl;
665 if (!is_empty()) {
666 os << " Min item : " << *min_item_ << std::endl;
667 os << " Max item : " << *max_item_ << std::endl;
668 }
669 os << "### End sketch summary" << std::endl;
670
671 if (print_levels) {
672 os << "### REQ sketch levels:" << std::endl;
673 os << " index: nominal capacity, actual size" << std::endl;
674 for (uint8_t i = 0; i < compactors_.size(); i++) {
675 os << " " << (unsigned int) i << ": "
676 << compactors_[i].get_nom_capacity() << ", "
677 << compactors_[i].get_num_items() << std::endl;
678 }
679 os << "### End sketch levels" << std::endl;
680 }
681
682 if (print_items) {
683 os << "### REQ sketch data:" << std::endl;
684 unsigned level = 0;
685 for (const auto& compactor: compactors_) {
686 os << " level " << level << ": " << std::endl;
687 for (auto it = compactor.begin(); it != compactor.end(); ++it) {
688 os << " " << *it << std::endl;
689 }
690 ++level;
691 }
692 os << "### End sketch data" << std::endl;
693 }
694 return string<A>(os.str().c_str(), allocator_);
695}
696
697template<typename T, typename C, typename A>
698req_sketch<T, C, A>::req_sketch(uint16_t k, bool hra, uint64_t n,
699 optional<T>&& min_item, optional<T>&& max_item,
700 std::vector<Compactor, AllocCompactor>&& compactors, const C& comparator):
701comparator_(comparator),
702allocator_(compactors.get_allocator()),
703k_(k),
704hra_(hra),
705max_nom_size_(0),
706num_retained_(0),
707n_(n),
708compactors_(std::move(compactors)),
709min_item_(std::move(min_item)),
710max_item_(std::move(max_item)),
711sorted_view_(nullptr)
712{
713 update_max_nom_size();
714 update_num_retained();
715}
716
717template<typename T, typename C, typename A>
718void req_sketch<T, C, A>::check_k(uint16_t k) {
719 if (k < req_constants::MIN_K || k > req_constants::MAX_K || (k & 1) != 0) {
720 throw std::invalid_argument("Possible corruption: k must be even and in the range ["
721 + std::to_string(req_constants::MIN_K) + ", " + std::to_string(req_constants::MAX_K) + "]: " + std::to_string(k));
722 }
723}
724
725template<typename T, typename C, typename A>
726void req_sketch<T, C, A>::check_num_levels(uint8_t num_levels, bool raw_items) {
727 // a non-empty sketch has at least one compactor, and raw items fit in one
728 if (num_levels == 0 || (raw_items && num_levels != 1)) {
729 throw std::invalid_argument("Possible corruption: number of levels " + std::to_string(num_levels)
730 + (raw_items ? " with raw items" : ""));
731 }
732}
733
734template<typename T, typename C, typename A>
735void req_sketch<T, C, A>::check_preamble_ints(uint8_t preamble_ints, uint8_t num_levels) {
736 const uint8_t expected_preamble_ints = num_levels > 1 ? 4 : 2;
737 if (preamble_ints != expected_preamble_ints) {
738 throw std::invalid_argument("Possible corruption: preamble ints must be "
739 + std::to_string(expected_preamble_ints) + ", got " + std::to_string(preamble_ints));
740 }
741}
742
743template<typename T, typename C, typename A>
744void req_sketch<T, C, A>::check_serial_version(uint8_t serial_version) {
745 if (serial_version != SERIAL_VERSION) {
746 throw std::invalid_argument("Possible corruption: serial version mismatch: expected "
747 + std::to_string(SERIAL_VERSION)
748 + ", got " + std::to_string(serial_version));
749 }
750}
751
752template<typename T, typename C, typename A>
753void req_sketch<T, C, A>::check_family_id(uint8_t family_id) {
754 if (family_id != FAMILY) {
755 throw std::invalid_argument("Possible corruption: family mismatch: expected "
756 + std::to_string(FAMILY) + ", got " + std::to_string(family_id));
757 }
758}
759
760template<typename T, typename C, typename A>
761auto req_sketch<T, C, A>::begin() const -> const_iterator {
762 return const_iterator(compactors_.begin(), compactors_.end());
763}
764
765template<typename T, typename C, typename A>
766auto req_sketch<T, C, A>::end() const -> const_iterator {
767 return const_iterator(compactors_.end(), compactors_.end());
768}
769
770template<typename T, typename C, typename A>
771void req_sketch<T, C, A>::setup_sorted_view() const {
772 if (sorted_view_ == nullptr) {
773 using AllocSortedView = typename std::allocator_traits<A>::template rebind_alloc<quantiles_sorted_view<T, C, A>>;
774 sorted_view_ = new (AllocSortedView(allocator_).allocate(1)) quantiles_sorted_view<T, C, A>(get_sorted_view());
775 }
776}
777
778template<typename T, typename C, typename A>
779void req_sketch<T, C, A>::reset_sorted_view() {
780 if (sorted_view_ != nullptr) {
781 sorted_view_->~quantiles_sorted_view();
782 using AllocSortedView = typename std::allocator_traits<A>::template rebind_alloc<quantiles_sorted_view<T, C, A>>;
783 AllocSortedView(allocator_).deallocate(sorted_view_, 1);
784 sorted_view_ = nullptr;
785 }
786}
787
788// iterator
789
790template<typename T, typename C, typename A>
791req_sketch<T, C, A>::const_iterator::const_iterator(LevelsIterator begin, LevelsIterator end):
792 levels_it_(begin),
793 levels_end_(end),
794 compactor_it_(begin == end ? nullptr : (*levels_it_).begin())
795{}
796
797template<typename T, typename C, typename A>
798auto req_sketch<T, C, A>::const_iterator::operator++() -> const_iterator& {
799 ++compactor_it_;
800 if (compactor_it_ == (*levels_it_).end()) {
801 ++levels_it_;
802 if (levels_it_ != levels_end_) compactor_it_ = (*levels_it_).begin();
803 }
804 return *this;
805}
806
807template<typename T, typename C, typename A>
808auto req_sketch<T, C, A>::const_iterator::operator++(int) -> const_iterator& {
809 const_iterator tmp(*this);
810 operator++();
811 return tmp;
812}
813
814template<typename T, typename C, typename A>
815bool req_sketch<T, C, A>::const_iterator::operator==(const const_iterator& other) const {
816 if (levels_it_ != other.levels_it_) return false;
817 if (levels_it_ == levels_end_) return true;
818 return compactor_it_ == other.compactor_it_;
819}
820
821template<typename T, typename C, typename A>
822bool req_sketch<T, C, A>::const_iterator::operator!=(const const_iterator& other) const {
823 return !operator==(other);
824}
825
826template<typename T, typename C, typename A>
827auto req_sketch<T, C, A>::const_iterator::operator*() const -> reference {
828 return value_type(*compactor_it_, 1ULL << (*levels_it_).get_lg_weight());
829}
830
831template<typename T, typename C, typename A>
832auto req_sketch<T, C, A>::const_iterator::operator->() const -> pointer {
833 return **this;
834}
835
836} /* namespace datasketches */
837
838#endif
Sorted view for quantiles sketches (REQ, KLL and Quantiles).
Definition quantiles_sorted_view.hpp:38
Relative Error Quantiles Sketch.
Definition req_sketch.hpp:84
Comparator get_comparator() const
Returns an instance of the comparator for this sketch.
Definition req_sketch_impl.hpp:228
quantiles_sorted_view< T, Comparator, Allocator > get_sorted_view() const
Gets the sorted view of this sketch.
Definition req_sketch_impl.hpp:273
static req_sketch deserialize(std::istream &is, const SerDe &sd=SerDe(), const Comparator &comparator=Comparator(), const Allocator &allocator=Allocator())
This method deserializes a sketch from a given stream.
vector_double get_CDF(const T *split_points, uint32_t size, bool inclusive=true) const
Returns an approximation to the Cumulative Distribution Function (CDF), which is the cumulative analo...
Definition req_sketch_impl.hpp:255
uint32_t get_num_retained() const
Returns the number of retained items in the sketch.
Definition req_sketch_impl.hpp:163
vector_double get_PMF(const T *split_points, uint32_t size, bool inclusive=true) const
Returns an approximation to the Probability Mass Function (PMF) of the input stream given a set of sp...
Definition req_sketch_impl.hpp:248
void merge(FwdSk &&other)
Merges another sketch into this one.
Definition req_sketch_impl.hpp:192
typename quantiles_sorted_view< T, Comparator, Allocator >::quantile_return_type quantile_return_type
Quantile return type.
Definition req_sketch.hpp:97
void update(FwdT &&item)
Updates this sketch with the given data item.
Definition req_sketch_impl.hpp:174
void serialize(std::ostream &os, const SerDe &sd=SerDe()) const
This method serializes the sketch into a given stream in a binary form.
Definition req_sketch_impl.hpp:373
string< Allocator > to_string(bool print_levels=false, bool print_items=false) const
Prints a summary of the sketch.
Definition req_sketch_impl.hpp:651
uint16_t get_k() const
Returns configured parameter K.
Definition req_sketch_impl.hpp:143
bool is_empty() const
Returns true if this sketch is empty.
Definition req_sketch_impl.hpp:153
const T & get_max_item() const
Returns the max item of the stream.
Definition req_sketch_impl.hpp:222
double get_rank_upper_bound(double rank, uint8_t num_std_dev) const
Returns an approximate upper bound of the given normalized rank.
Definition req_sketch_impl.hpp:293
double get_rank(const T &item, bool inclusive=true) const
Returns an approximation to the normalized rank of the given item from 0 to 1 inclusive.
Definition req_sketch_impl.hpp:238
static double get_RSE(uint16_t k, double rank, bool hra, uint64_t n)
Returns an a priori estimate of relative standard error (RSE, expressed as a number in [0,...
Definition req_sketch_impl.hpp:298
double get_rank_lower_bound(double rank, uint8_t num_std_dev) const
Returns an approximate lower bound of the given normalized rank.
Definition req_sketch_impl.hpp:288
Allocator get_allocator() const
Returns an instance of the allocator for this sketch.
Definition req_sketch_impl.hpp:233
req_sketch & operator=(const req_sketch &other)
Copy assignment.
Definition req_sketch_impl.hpp:85
quantile_return_type get_quantile(double rank, bool inclusive=true) const
Returns an approximate quantile of the given normalized rank.
Definition req_sketch_impl.hpp:262
const_iterator begin() const
Iterator pointing to the first item in the sketch.
Definition req_sketch_impl.hpp:761
size_t get_serialized_size_bytes(const SerDe &sd=SerDe()) const
Computes size needed to serialize the current state of the sketch.
Definition req_sketch_impl.hpp:338
const_iterator end() const
Iterator pointing to the past-the-end item in the sketch.
Definition req_sketch_impl.hpp:766
bool is_estimation_mode() const
Returns true if this sketch is in estimation mode.
Definition req_sketch_impl.hpp:168
const T & get_min_item() const
Returns the min item of the stream.
Definition req_sketch_impl.hpp:216
bool is_HRA() const
Returns configured parameter High Rank Accuracy.
Definition req_sketch_impl.hpp:148
uint64_t get_n() const
Returns the length of the input stream.
Definition req_sketch_impl.hpp:158
const uint16_t MIN_K
min value of parameter K
Definition kll_sketch.hpp:39
REQ sketch constants.
Definition req_common.hpp:31
const uint8_t INIT_NUM_SECTIONS
initial number of sections
Definition req_common.hpp:37
const uint16_t MAX_K
maximum value of parameter K
Definition req_common.hpp:35
const uint16_t MIN_K
minimum value of parameter K
Definition req_common.hpp:33
DataSketches namespace.
Definition binomial_bounds.hpp:38