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((static_cast<uint64_t>(num_hashes) * num_buckets < 1<<30) ? static_cast<size_t>(num_hashes) * num_buckets : 0, 0, _allocator),
42hash_seeds(_allocator) {
43 if (num_buckets < 3) {
44 throw std::invalid_argument(
"Using fewer than 3 buckets incurs relative error greater than 1.");
49 if (
static_cast<uint64_t
>(num_buckets) * num_hashes >= 1 << 30) {
50 throw std::invalid_argument(
"These parameters generate a sketch that exceeds 2^30 elements."
51 "Try reducing either the number of buckets or the number of hash functions.");
54 std::default_random_engine rng(_seed);
55 std::uniform_int_distribution<uint64_t> extra_hash_seeds(0, std::numeric_limits<uint64_t>::max());
56 hash_seeds.reserve(num_hashes);
58 for (uint64_t i=0; i < num_hashes; ++i) {
59 hash_seeds.push_back(extra_hash_seeds(rng) + _seed);
63template<
typename W,
typename A>
68template<
typename W,
typename A>
73template<
typename W,
typename A>
78template<
typename W,
typename A>
80 return exp(1.0) /
static_cast<double>(_num_buckets);
83template<
typename W,
typename A>
88template<
typename W,
typename A>
94 if (relative_error < 0.) {
95 throw std::invalid_argument(
"Relative error must be at least 0.");
97 return static_cast<uint32_t
>(ceil(exp(1.0) / relative_error));
100template<
typename W,
typename A>
107 if (confidence < 0. || confidence > 1.0) {
108 throw std::invalid_argument(
"Confidence must be between 0 and 1.0 (inclusive).");
110 return std::min<uint8_t>(ceil(log(1.0 / (1.0 - confidence))), UINT8_MAX);
113template<
typename W,
typename A>
115void count_min_sketch<W,A>::foreach_hash_location(
const void* item,
size_t size, F callback)
const {
130 uint64_t bucket_index;
132 uint64_t hash_seed_index = 0;
133 for (
const auto &it: hash_seeds) {
135 MurmurHash3_x64_128(item, size, it, hashes);
136 uint64_t hash = hashes.h1;
137 bucket_index = hash % _num_buckets;
138 callback((hash_seed_index * _num_buckets) + bucket_index);
139 hash_seed_index += 1;
143template<
typename W,
typename A>
146template<
typename W,
typename A>
149template<
typename W,
typename A>
151 if (item.empty()) {
return 0; }
155template<
typename W,
typename A>
160 W estimate = std::numeric_limits<W>::max();
161 foreach_hash_location(item, size, [
this, &estimate](uint64_t h) {
162 estimate = std::min(estimate, _sketch_array[h]);
167template<
typename W,
typename A>
169 update(&item,
sizeof(item), weight);
172template<
typename W,
typename A>
174 update(&item,
sizeof(item), weight);
177template<
typename W,
typename A>
179 if (item.empty()) {
return; }
180 update(item.c_str(), item.length(), weight);
183template<
typename W,
typename A>
189 _total_weight += weight >= 0 ? weight : -weight;
190 foreach_hash_location(item, size, [
this, weight](uint64_t h) {
191 _sketch_array[h] += weight;
195template<
typename W,
typename A>
198template<
typename W,
typename A>
201template<
typename W,
typename A>
203 if (item.empty()) {
return 0; }
207template<
typename W,
typename A>
212template<
typename W,
typename A>
215template<
typename W,
typename A>
218template<
typename W,
typename A>
220 if (item.empty()) {
return 0; }
224template<
typename W,
typename A>
229template<
typename W,
typename A>
234 if (
this == &other_sketch) {
throw std::invalid_argument(
"Cannot merge a sketch with itself." ); }
236 bool acceptable_config =
240 if (!acceptable_config) {
throw std::invalid_argument(
"Incompatible sketch configuration." ); }
243 auto it = _sketch_array.begin();
244 auto other_it = other_sketch.
begin();
245 while (it != _sketch_array.end()) {
254template<
typename W,
typename A>
256 return _sketch_array.begin();
259template<
typename W,
typename A>
261return _sketch_array.end();
264template<
typename W,
typename A>
267 os.write(
static_cast<const char*
>(data), size);
271template<
typename Sink,
typename T>
272static inline void write_count_min_value(Sink& sink,
size_t& bytes_written,
const T& value) {
273 sink(&value,
sizeof(value));
274 bytes_written +=
sizeof(value);
277template<
typename W,
typename A>
278template<
typename Sink>
280 size_t bytes_written = 0;
284 const uint8_t preamble_longs = PREAMBLE_LONGS_SHORT;
285 const uint8_t ser_ver = SERIAL_VERSION_1;
286 const uint8_t family_id = FAMILY_ID;
287 const uint8_t flags_byte = (
is_empty() ? 1 << flags::IS_EMPTY : 0);
288 const uint32_t unused32 = NULL_32;
289 write_count_min_value(sink, bytes_written, preamble_longs);
290 write_count_min_value(sink, bytes_written, ser_ver);
291 write_count_min_value(sink, bytes_written, family_id);
292 write_count_min_value(sink, bytes_written, flags_byte);
293 write_count_min_value(sink, bytes_written, unused32);
296 const uint32_t nbuckets = _num_buckets;
297 const uint8_t nhashes = _num_hashes;
298 const uint16_t seed_hash(compute_seed_hash(_seed));
299 const uint8_t unused8 = NULL_8;
300 write_count_min_value(sink, bytes_written, nbuckets);
301 write_count_min_value(sink, bytes_written, nhashes);
302 write_count_min_value(sink, bytes_written, seed_hash);
303 write_count_min_value(sink, bytes_written, unused8);
304 if (
is_empty()) {
return bytes_written; }
307 const W t_weight = _total_weight;
308 write_count_min_value(sink, bytes_written, t_weight);
311 const size_t sketch_array_bytes =
sizeof(W) * _sketch_array.size();
312 if (sketch_array_bytes > 0) {
313 sink(_sketch_array.data(), sketch_array_bytes);
314 bytes_written += sketch_array_bytes;
317 return bytes_written;
320template<
typename W,
typename A>
324 const auto preamble_longs = read<uint8_t>(is);
325 const auto serial_version = read<uint8_t>(is);
326 const auto family_id = read<uint8_t>(is);
327 const auto flags_byte = read<uint8_t>(is);
330 check_header_validity(preamble_longs, serial_version, family_id, flags_byte);
333 const auto nbuckets = read<uint32_t>(is);
334 const auto nhashes = read<uint8_t>(is);
335 const auto seed_hash = read<uint16_t>(is);
338 if (seed_hash != compute_seed_hash(seed)) {
339 throw std::invalid_argument(
"Incompatible seed hashes: " + std::to_string(seed_hash) +
", "
340 + std::to_string(compute_seed_hash(seed)));
342 count_min_sketch c(nhashes, nbuckets, seed, allocator);
343 const bool is_empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
344 if (is_empty == 1) {
return c; }
347 const auto weight = read<W>(is);
348 c._total_weight += weight;
349 read(is, c._sketch_array.data(),
sizeof(W) * c._sketch_array.size());
354template<
typename W,
typename A>
357 const size_t preamble_longs = PREAMBLE_LONGS_SHORT;
361 return (preamble_longs *
sizeof(uint64_t)) + (
is_empty() ? 0 :
sizeof(W) * (1 + _num_buckets * _num_hashes));
364template<
typename W,
typename A>
366 vector_bytes bytes(header_size_bytes + get_serialized_size_bytes(), 0, _allocator);
367 uint8_t *ptr = bytes.data() + header_size_bytes;
368 serialize_to([&ptr](
const void* data,
size_t size) {
369 ptr += copy_to_mem(data, ptr, size);
375template<
typename W,
typename A>
377 ensure_minimum_memory(size, PREAMBLE_LONGS_SHORT *
sizeof(uint64_t));
379 const char* ptr =
static_cast<const char*
>(bytes);
382 uint8_t preamble_longs;
383 ptr += copy_from_mem(ptr, preamble_longs);
384 uint8_t serial_version;
385 ptr += copy_from_mem(ptr, serial_version);
387 ptr += copy_from_mem(ptr, family_id);
389 ptr += copy_from_mem(ptr, flags_byte);
390 ptr +=
sizeof(uint32_t);
392 check_header_validity(preamble_longs, serial_version, family_id, flags_byte);
398 ptr += copy_from_mem(ptr, nbuckets);
399 ptr += copy_from_mem(ptr, nhashes);
400 ptr += copy_from_mem(ptr, seed_hash);
401 ptr +=
sizeof(uint8_t);
403 if (seed_hash != compute_seed_hash(seed)) {
404 throw std::invalid_argument(
"Incompatible seed hashes: " + std::to_string(seed_hash) +
", "
405 + std::to_string(compute_seed_hash(seed)));
408 const bool is_empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
409 if (is_empty) {
return c; }
412 ensure_minimum_memory(size, PREAMBLE_LONGS_SHORT *
sizeof(uint64_t) +
sizeof(W) * (1 + c._sketch_array.size()));
416 ptr += copy_from_mem(ptr, weight);
417 c._total_weight += weight;
420 for (
size_t i = 0; i<c._num_buckets*c._num_hashes; ++i) {
421 ptr += copy_from_mem(ptr, c._sketch_array[i]);
426template<
typename W,
typename A>
428 return _total_weight == 0;
431template<
typename W,
typename A>
434 uint64_t num_nonzero = 0;
435 for (
const auto entry: _sketch_array) {
436 if (entry !=
static_cast<W
>(0.0)) { ++num_nonzero; }
441 std::ostringstream os;
442 os <<
"### Count Min sketch summary:" << std::endl;
443 os <<
" num hashes : " <<
static_cast<uint32_t
>(_num_hashes) << std::endl;
444 os <<
" num buckets : " << _num_buckets << std::endl;
445 os <<
" capacity bins : " << _sketch_array.size() << std::endl;
446 os <<
" filled bins : " << num_nonzero << std::endl;
447 os <<
" pct filled : " << std::setprecision(3) << (num_nonzero * 100.0) / _sketch_array.size() <<
"%" << std::endl;
448 os <<
"### End sketch summary" << std::endl;
450 return string<A>(os.str().c_str(), _allocator);
453template<
typename W,
typename A>
454void count_min_sketch<W,A>::check_header_validity(uint8_t preamble_longs, uint8_t serial_version, uint8_t family_id, uint8_t flags_byte) {
455 const bool empty = (flags_byte & (1 << flags::IS_EMPTY)) > 0;
457 const uint8_t sw = (empty ? 1 : 0) + (2 * serial_version) + (4 * family_id) + (32 * (preamble_longs & 0x3F));
469 std::ostringstream os;
470 os <<
"Possible sketch corruption. Inconsistent state: "
471 <<
"preamble_longs = " <<
static_cast<uint32_t
>(preamble_longs)
472 <<
", empty = " << (empty ?
"true" :
"false")
473 <<
", serialization_version = " <<
static_cast<uint32_t
>(serial_version);
474 throw std::invalid_argument(os.str());
C++ implementation of the CountMin sketch data structure of Cormode and Muthukrishnan.
Definition count_min.hpp:39
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:265
const_iterator end() const
Iterator pointing to the past-the-end item in the sketch.
Definition count_min_impl.hpp:260
double get_relative_error() const
Definition count_min_impl.hpp:79
uint32_t get_num_buckets() const
Definition count_min_impl.hpp:69
size_t serialize_to(Sink &&sink) const
This method serializes the sketch by passing binary fragments to a callback.
Definition count_min_impl.hpp:279
bool is_empty() const
Returns true if this sketch is empty.
Definition count_min_impl.hpp:427
W get_total_weight() const
Definition count_min_impl.hpp:84
uint8_t get_num_hashes() const
Definition count_min_impl.hpp:64
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:208
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:225
W get_estimate(uint64_t item) const
Suggests the number of buckets required to achieve the given relative error.
Definition count_min_impl.hpp:144
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:74
void merge(const count_min_sketch &other_sketch)
Merges another count_min_sketch into this count_min_sketch.
Definition count_min_impl.hpp:230
void update(const void *item, size_t size, W weight)
Update this sketch with given data of any type.
Definition count_min_impl.hpp:184
string< Allocator > to_string() const
Returns a string describing the sketch.
Definition count_min_impl.hpp:432
size_t get_serialized_size_bytes() const
Computes size needed to serialize the current state of the sketch.
Definition count_min_impl.hpp:355
const_iterator begin() const
Iterator pointing to the first item in the sketch.
Definition count_min_impl.hpp:255
DataSketches namespace.
Definition binomial_bounds.hpp:38