20#ifndef COUNT_MIN_IMPL_HPP_
21#define COUNT_MIN_IMPL_HPP_
28#include "MurmurHash3.h"
29#include "count_min.hpp"
30#include "memory_operations.hpp"
34template<
typename W,
typename A>
37_num_hashes(num_hashes),
38_num_buckets(num_buckets),
39_sketch_array((num_hashes*num_buckets < 1<<30) ? num_hashes*num_buckets : 0, 0, _allocator),
42 if (num_buckets < 3) {
43 throw std::invalid_argument(
"Using fewer than 3 buckets incurs relative error greater than 1.");
48 if (num_buckets * num_hashes >= 1 << 30) {
49 throw std::invalid_argument(
"These parameters generate a sketch that exceeds 2^30 elements."
50 "Try reducing either the number of buckets or the number of hash functions.");
53 std::default_random_engine rng(_seed);
54 std::uniform_int_distribution<uint64_t> extra_hash_seeds(0, std::numeric_limits<uint64_t>::max());
55 hash_seeds.reserve(num_hashes);
57 for (uint64_t i=0; i < num_hashes; ++i) {
58 hash_seeds.push_back(extra_hash_seeds(rng) + _seed);
62template<
typename W,
typename A>
67template<
typename W,
typename A>
72template<
typename W,
typename A>
77template<
typename W,
typename A>
79 return exp(1.0) /
static_cast<double>(_num_buckets);
82template<
typename W,
typename A>
87template<
typename W,
typename A>
93 if (relative_error < 0.) {
94 throw std::invalid_argument(
"Relative error must be at least 0.");
96 return static_cast<uint32_t
>(ceil(exp(1.0) / relative_error));
99template<
typename W,
typename A>
106 if (confidence < 0. || confidence > 1.0) {
107 throw std::invalid_argument(
"Confidence must be between 0 and 1.0 (inclusive).");
109 return std::min<uint8_t>(ceil(log(1.0 / (1.0 - confidence))), UINT8_MAX);
112template<
typename W,
typename A>
129 uint64_t bucket_index;
131 uint64_t hash_seed_index = 0;
132 for (
const auto &it: hash_seeds) {
134 MurmurHash3_x64_128(item, size, it, hashes);
135 uint64_t hash = hashes.h1;
136 bucket_index = hash % _num_buckets;
137 callback((hash_seed_index * _num_buckets) + bucket_index);
138 hash_seed_index += 1;
142template<
typename W,
typename A>
145template<
typename W,
typename A>
148template<
typename W,
typename A>
150 if (item.empty()) {
return 0; }
151 return get_estimate(item.c_str(), item.length());
154template<
typename W,
typename A>
159 W estimate = std::numeric_limits<W>::max();
160 foreach_hash_location(item, size, [
this, &estimate](uint64_t h) {
161 estimate = std::min(estimate, _sketch_array[h]);
166template<
typename W,
typename A>
168 update(&item,
sizeof(item), weight);
171template<
typename W,
typename A>
173 update(&item,
sizeof(item), weight);
176template<
typename W,
typename A>
178 if (item.empty()) {
return; }
179 update(item.c_str(), item.length(), weight);
182template<
typename W,
typename A>
188 _total_weight += weight >= 0 ? weight : -weight;
189 foreach_hash_location(item, size, [
this, weight](uint64_t h) {
190 _sketch_array[h] += weight;
194template<
typename W,
typename A>
197template<
typename W,
typename A>
200template<
typename W,
typename A>
202 if (item.empty()) {
return 0; }
203 return get_upper_bound(item.c_str(), item.length());
206template<
typename W,
typename A>
208 return static_cast<W
>(get_estimate(item, size) + get_relative_error() * get_total_weight());
211template<
typename W,
typename A>
214template<
typename W,
typename A>
217template<
typename W,
typename A>
219 if (item.empty()) {
return 0; }
220 return get_lower_bound(item.c_str(), item.length());
223template<
typename W,
typename A>
225 return get_estimate(item, size);
228template<
typename W,
typename A>
233 if (
this == &other_sketch) {
throw std::invalid_argument(
"Cannot merge a sketch with itself." ); }
235 bool acceptable_config =
238 (get_seed() == other_sketch.
get_seed());
239 if (!acceptable_config) {
throw std::invalid_argument(
"Incompatible sketch configuration." ); }
242 auto it = _sketch_array.begin();
243 auto other_it = other_sketch.
begin();
244 while (it != _sketch_array.end()) {
253template<
typename W,
typename A>
255 return _sketch_array.begin();
258template<
typename W,
typename A>
260return _sketch_array.end();
263template<
typename W,
typename A>
267 const uint8_t preamble_longs = PREAMBLE_LONGS_SHORT;
268 const uint8_t ser_ver = SERIAL_VERSION_1;
269 const uint8_t family_id = FAMILY_ID;
270 const uint8_t flags_byte = (is_empty() ? 1 << flags::IS_EMPTY : 0);
271 const uint32_t unused32 = NULL_32;
272 write(os, preamble_longs);
274 write(os, family_id);
275 write(os, flags_byte);
279 const uint32_t nbuckets = _num_buckets;
280 const uint8_t nhashes = _num_hashes;
281 const uint16_t seed_hash(compute_seed_hash(_seed));
282 const uint8_t unused8 = NULL_8;
285 write(os, seed_hash);
287 if (is_empty()) {
return; }
290 write(os, _total_weight);
293 auto it = _sketch_array.begin();
294 while (it != _sketch_array.end()) {
300template<
typename W,
typename A>
304 const auto preamble_longs = read<uint8_t>(is);
305 const auto serial_version = read<uint8_t>(is);
306 const auto family_id = read<uint8_t>(is);
307 const auto flags_byte = read<uint8_t>(is);
310 check_header_validity(preamble_longs, serial_version, family_id, flags_byte);
313 const auto nbuckets = read<uint32_t>(is);
314 const auto nhashes = read<uint8_t>(is);
315 const auto seed_hash = read<uint16_t>(is);
318 if (seed_hash != compute_seed_hash(seed)) {
319 throw std::invalid_argument(
"Incompatible seed hashes: " + std::to_string(seed_hash) +
", "
320 + std::to_string(compute_seed_hash(seed)));
322 count_min_sketch c(nhashes, nbuckets, seed, allocator);
323 const bool is_empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
324 if (is_empty == 1) {
return c; }
327 const auto weight = read<W>(is);
328 c._total_weight += weight;
329 read(is, c._sketch_array.data(),
sizeof(W) * c._sketch_array.size());
334template<
typename W,
typename A>
337 const size_t preamble_longs = PREAMBLE_LONGS_SHORT;
341 return (preamble_longs *
sizeof(uint64_t)) + (is_empty() ? 0 :
sizeof(W) * (1 + _num_buckets * _num_hashes));
344template<
typename W,
typename A>
346 vector_bytes bytes(header_size_bytes + get_serialized_size_bytes(), 0, _allocator);
347 uint8_t *ptr = bytes.data() + header_size_bytes;
350 const uint8_t preamble_longs = PREAMBLE_LONGS_SHORT;
351 ptr += copy_to_mem(preamble_longs, ptr);
352 const uint8_t ser_ver = SERIAL_VERSION_1;
353 ptr += copy_to_mem(ser_ver, ptr);
354 const uint8_t family_id = FAMILY_ID;
355 ptr += copy_to_mem(family_id, ptr);
356 const uint8_t flags_byte = (is_empty() ? 1 << flags::IS_EMPTY : 0);
357 ptr += copy_to_mem(flags_byte, ptr);
358 const uint32_t unused32 = NULL_32;
359 ptr += copy_to_mem(unused32, ptr);
362 const uint32_t nbuckets = _num_buckets;
363 const uint8_t nhashes = _num_hashes;
364 const uint16_t seed_hash(compute_seed_hash(_seed));
365 const uint8_t null_characters_8 = NULL_8;
366 ptr += copy_to_mem(nbuckets, ptr);
367 ptr += copy_to_mem(nhashes, ptr);
368 ptr += copy_to_mem(seed_hash, ptr);
369 ptr += copy_to_mem(null_characters_8, ptr);
370 if (is_empty()) {
return bytes; }
373 const W t_weight = _total_weight;
374 ptr += copy_to_mem(t_weight, ptr);
377 auto it = _sketch_array.begin();
378 while (it != _sketch_array.end()) {
379 ptr += copy_to_mem(*it, ptr);
386template<
typename W,
typename A>
388 ensure_minimum_memory(size, PREAMBLE_LONGS_SHORT *
sizeof(uint64_t));
390 const char* ptr =
static_cast<const char*
>(bytes);
393 uint8_t preamble_longs;
394 ptr += copy_from_mem(ptr, preamble_longs);
395 uint8_t serial_version;
396 ptr += copy_from_mem(ptr, serial_version);
398 ptr += copy_from_mem(ptr, family_id);
400 ptr += copy_from_mem(ptr, flags_byte);
401 ptr +=
sizeof(uint32_t);
403 check_header_validity(preamble_longs, serial_version, family_id, flags_byte);
409 ptr += copy_from_mem(ptr, nbuckets);
410 ptr += copy_from_mem(ptr, nhashes);
411 ptr += copy_from_mem(ptr, seed_hash);
412 ptr +=
sizeof(uint8_t);
414 if (seed_hash != compute_seed_hash(seed)) {
415 throw std::invalid_argument(
"Incompatible seed hashes: " + std::to_string(seed_hash) +
", "
416 + std::to_string(compute_seed_hash(seed)));
418 count_min_sketch c(nhashes, nbuckets, seed, allocator);
419 const bool is_empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
420 if (is_empty) {
return c; }
422 ensure_minimum_memory(size,
sizeof(W) * (1 + nbuckets * nhashes));
426 ptr += copy_from_mem(ptr, weight);
427 c._total_weight += weight;
430 for (
size_t i = 0; i<c._num_buckets*c._num_hashes; ++i) {
431 ptr += copy_from_mem(ptr, c._sketch_array[i]);
436template<
typename W,
typename A>
438 return _total_weight == 0;
441template<
typename W,
typename A>
444 uint64_t num_nonzero = 0;
445 for (
const auto entry: _sketch_array) {
446 if (entry !=
static_cast<W
>(0.0)) { ++num_nonzero; }
451 std::ostringstream os;
452 os <<
"### Count Min sketch summary:" << std::endl;
453 os <<
" num hashes : " <<
static_cast<uint32_t
>(_num_hashes) << std::endl;
454 os <<
" num buckets : " << _num_buckets << std::endl;
455 os <<
" capacity bins : " << _sketch_array.size() << std::endl;
456 os <<
" filled bins : " << num_nonzero << std::endl;
457 os <<
" pct filled : " << std::setprecision(3) << (num_nonzero * 100.0) / _sketch_array.size() <<
"%" << std::endl;
458 os <<
"### End sketch summary" << std::endl;
460 return string<A>(os.str().c_str(), _allocator);
463template<
typename W,
typename A>
465 const bool empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
467 const uint8_t sw = (empty ? 1 : 0) + (2 * serial_version) + (4 * family_id) + (32 * (preamble_longs & 0x3F));
479 std::ostringstream os;
480 os <<
"Possible sketch corruption. Inconsistent state: "
481 <<
"preamble_longs = " <<
static_cast<uint32_t
>(preamble_longs)
482 <<
", empty = " << (empty ?
"true" :
"false")
483 <<
", serialization_version = " << static_cast<uint32_t>(serial_version);
484 throw std::invalid_argument(os.str());
C++ implementation of the CountMin sketch data structure of Cormode and Muthukrishnan.
Definition count_min.hpp:37
static count_min_sketch deserialize(std::istream &is, uint64_t seed=DEFAULT_SEED, const Allocator &allocator=Allocator())
This method deserializes a sketch from a given stream.
void serialize(std::ostream &os) const
This method serializes the sketch into a given stream in a binary form.
Definition count_min_impl.hpp:264
static uint32_t suggest_num_buckets(double relative_error)
Suggests the number of buckets required to achieve the given relative error.
Definition count_min_impl.hpp:88
const_iterator end() const
Iterator pointing to the past-the-end item in the sketch.
Definition count_min_impl.hpp:259
static uint8_t suggest_num_hashes(double confidence)
Suggests the number of hash functions required to achieve the given confidence.
Definition count_min_impl.hpp:100
double get_relative_error() const
Definition count_min_impl.hpp:78
uint32_t get_num_buckets() const
Definition count_min_impl.hpp:68
bool is_empty() const
Returns true if this sketch is empty.
Definition count_min_impl.hpp:437
W get_total_weight() const
Definition count_min_impl.hpp:83
uint8_t get_num_hashes() const
Definition count_min_impl.hpp:63
W get_upper_bound(const void *item, size_t size) const
Query the sketch for the upper bound of a given item.
Definition count_min_impl.hpp:207
W get_lower_bound(const void *item, size_t size) const
Query the sketch for the lower bound of a given item.
Definition count_min_impl.hpp:224
W get_estimate(uint64_t item) const
Specific get_estimate function for uint64_t type see generic get_estimate function.
Definition count_min_impl.hpp:143
count_min_sketch(uint8_t num_hashes, uint32_t num_buckets, uint64_t seed=DEFAULT_SEED, const Allocator &allocator=Allocator())
Creates an instance of the sketch given parameters _num_hashes, _num_buckets and hash seed,...
Definition count_min_impl.hpp:35
uint64_t get_seed() const
Definition count_min_impl.hpp:73
void merge(const count_min_sketch &other_sketch)
Merges another count_min_sketch into this count_min_sketch.
Definition count_min_impl.hpp:229
void update(const void *item, size_t size, W weight)
Update this sketch with given data of any type.
Definition count_min_impl.hpp:183
string< Allocator > to_string() const
Returns a string describing the sketch.
Definition count_min_impl.hpp:442
size_t get_serialized_size_bytes() const
Computes size needed to serialize the current state of the sketch.
Definition count_min_impl.hpp:335
const_iterator begin() const
Iterator pointing to the first item in the sketch.
Definition count_min_impl.hpp:254
DataSketches namespace.
Definition binomial_bounds.hpp:38