| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <functional> |
| #include <string> |
| #include <sstream> |
| #include <stdexcept> |
| #include <variant> |
| #include <vector> |
| |
| #include <nanobind/nanobind.h> |
| #include <nanobind/ndarray.h> |
| #include <nanobind/stl/variant.h> |
| #include <nanobind/stl/vector.h> |
| #include <nanobind/stl/string.h> |
| |
| #include "kll_sketch.hpp" |
| |
| namespace nb = nanobind; |
| |
| namespace datasketches { |
| |
| namespace vector_of_kll_constants { |
| static const uint32_t DEFAULT_K = kll_constants::DEFAULT_K; |
| static const uint32_t DEFAULT_D = 1; |
| } |
| |
| // Wrapper class for Numpy compatibility |
| template <typename T, typename C = std::less<T>> |
| class vector_of_kll_sketches { |
| public: |
| explicit vector_of_kll_sketches(uint32_t k = vector_of_kll_constants::DEFAULT_K, uint32_t d = vector_of_kll_constants::DEFAULT_D); |
| vector_of_kll_sketches(const vector_of_kll_sketches& other); |
| vector_of_kll_sketches(vector_of_kll_sketches&& other) noexcept; |
| vector_of_kll_sketches<T, C>& operator=(const vector_of_kll_sketches& other); |
| vector_of_kll_sketches<T, C>& operator=(vector_of_kll_sketches&& other); |
| |
| // container parameters |
| inline uint32_t get_k() const; |
| inline uint32_t get_d() const; |
| |
| template<typename V> |
| using Array1D = nb::ndarray<V, nb::numpy, nb::ndim<1>>; |
| |
| template<typename V> |
| using Array2D = nb::ndarray<V, nb::numpy, nb::ndim<2>, nb::c_contig>; |
| |
| // sketch updates/merges |
| void update(nb::ndarray<T>& items, char order); |
| void merge(const vector_of_kll_sketches<T>& other); |
| |
| template<typename V> |
| using ArrInputType = std::variant<nb::ndarray<>, nb::list, V>; |
| |
| // returns a single sketch combining all data in the array |
| kll_sketch<T, C> collapse(ArrInputType<int>& isk) const; |
| |
| // sketch queries returning an array of results |
| Array1D<bool> is_empty() const; |
| Array1D<uint64_t> get_n() const; |
| Array1D<bool> is_estimation_mode() const; |
| Array1D<T> get_min_values() const; |
| Array1D<T> get_max_values() const; |
| Array1D<uint32_t> get_num_retained() const; |
| Array2D<T> get_quantiles(ArrInputType<double>& ranks, ArrInputType<int>& isk) const; |
| Array2D<double> get_ranks(ArrInputType<T>& values, ArrInputType<int>& isk) const; |
| Array2D<double> get_pmf(ArrInputType<T>& split_points, ArrInputType<int>& isk) const; |
| Array2D<double> get_cdf(ArrInputType<T>& split_points, ArrInputType<int>& isk) const; |
| |
| // human-readable output |
| std::string to_string(bool print_levels = false, bool print_items = false) const; |
| |
| // binary output/input |
| nb::list serialize(ArrInputType<int>& isk); |
| // note: deserialize() replaces the sketch at the specified |
| // index. Not a static method. |
| void deserialize(const nb::bytes& sk_bytes, uint32_t idx); |
| |
| private: |
| template<typename TT> |
| Array1D<TT> input_to_vec(ArrInputType<TT>& input) const; |
| |
| Array1D<uint32_t> get_indices(Array1D<int>& isk) const; |
| |
| template<typename TT> |
| Array1D<TT> make_ndarray(size_t size) const; |
| |
| template<typename TT> |
| Array2D<TT> make_ndarray(size_t rows, size_t cols) const; |
| |
| const uint32_t k_; // kll sketch k parameter |
| const uint32_t d_; // number of dimensions (here: sketches) to hold |
| std::vector<kll_sketch<T, C>> sketches_; |
| }; |
| |
| template<typename T, typename C> |
| vector_of_kll_sketches<T, C>::vector_of_kll_sketches(uint32_t k, uint32_t d): |
| k_(k), |
| d_(d) |
| { |
| // check d is valid (k is checked by kll_sketch) |
| if (d < 1) { |
| throw std::invalid_argument("D must be >= 1: " + std::to_string(d)); |
| } |
| |
| sketches_.reserve(d); |
| // spawn the sketches |
| for (uint32_t i = 0; i < d; i++) { |
| sketches_.emplace_back(k); |
| } |
| } |
| |
| template<typename T, typename C> |
| vector_of_kll_sketches<T, C>::vector_of_kll_sketches(const vector_of_kll_sketches& other) : |
| k_(other.k_), |
| d_(other.d_), |
| sketches_(other.sketches_) |
| {} |
| |
| template<typename T, typename C> |
| vector_of_kll_sketches<T, C>::vector_of_kll_sketches(vector_of_kll_sketches&& other) noexcept : |
| k_(other.k_), |
| d_(other.d_), |
| sketches_(std::move(other.sketches_)) |
| {} |
| |
| template<typename T, typename C> |
| vector_of_kll_sketches<T, C>& vector_of_kll_sketches<T, C>::operator=(const vector_of_kll_sketches& other) { |
| vector_of_kll_sketches<T, C> copy(other); |
| k_ = copy.k_; |
| d_ = copy.d_; |
| std::swap(sketches_, copy.sketches_); |
| return *this; |
| } |
| |
| template<typename T, typename C> |
| vector_of_kll_sketches<T, C>& vector_of_kll_sketches<T, C>::operator=(vector_of_kll_sketches&& other) { |
| k_ = other.k_; |
| d_ = other.d_; |
| std::swap(sketches_, other.sketches_); |
| return *this; |
| } |
| |
| template<typename T, typename C> |
| uint32_t vector_of_kll_sketches<T, C>::get_k() const { |
| return k_; |
| } |
| |
| template<typename T, typename C> |
| uint32_t vector_of_kll_sketches<T, C>::get_d() const { |
| return d_; |
| } |
| |
| template<typename T, typename C> |
| template<typename TT> |
| auto vector_of_kll_sketches<T, C>::make_ndarray(size_t size) const -> Array1D<TT> { |
| TT* data = new TT[size]; |
| |
| nb::capsule owner(data, [](void *p) noexcept { |
| delete[] static_cast<TT*>(p); |
| }); |
| |
| return Array1D<TT>(data, {size}, owner); |
| } |
| |
| template<typename T, typename C> |
| template<typename TT> |
| auto vector_of_kll_sketches<T, C>::make_ndarray(size_t rows, size_t cols) const -> Array2D<TT> { |
| TT* data = new TT[rows * cols]; |
| |
| nb::capsule owner(data, [](void *p) noexcept { |
| delete[] static_cast<TT*>(p); |
| }); |
| |
| return Array2D<TT>(data, {rows, cols}, owner); |
| } |
| |
| template<typename T, typename C> |
| template<typename TT> |
| auto vector_of_kll_sketches<T, C>::input_to_vec(ArrInputType<TT>& input) const -> Array1D<TT> { |
| if (std::holds_alternative<nb::ndarray<>>(input)) { |
| nb::ndarray<> arr = std::get<nb::ndarray<>>(input); |
| return Array1D<TT>(arr); |
| } else if (std::holds_alternative<nb::list>(input)) { |
| nb::list list = std::get<nb::list>(input); |
| Array1D<TT> output = make_ndarray<TT>(list.size()); |
| for (size_t i = 0; i < list.size(); ++i) |
| output(i) = nb::cast<TT>(list[i]); |
| return output; |
| } else if (std::holds_alternative<TT>(input)) { |
| Array1D<TT> output = make_ndarray<TT>(1); |
| output(0) = std::get<TT>(input); |
| return output; |
| } else { |
| nb::raise_type_error("Unknown input type"); |
| } |
| } |
| |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_indices(Array1D<int>& isk) const -> Array1D<uint32_t> { |
| auto input = isk.view<nb::ndim<1>>(); |
| size_t num_input = input.shape(0); |
| Array1D<uint32_t> output; |
| |
| if (num_input == 1) { |
| if (input(0) == -1) { |
| output = make_ndarray<uint32_t>(d_); |
| for (uint32_t i = 0; i < d_; ++i) { |
| output(i) = i; |
| } |
| } else { |
| output = make_ndarray<uint32_t>(1); |
| output(0) = static_cast<uint32_t>(input(0)); |
| } |
| } else { |
| output = make_ndarray<uint32_t>(num_input); |
| for (uint32_t i = 0; i < num_input; ++i) { |
| const uint32_t idx = static_cast<uint32_t>(input(i)); |
| if (idx < d_) { |
| output(i) = idx; |
| } else { |
| throw std::invalid_argument("request for invalid dimensions >= d (" |
| + std::to_string(d_) +"): "+ std::to_string(idx)); |
| } |
| } |
| } |
| return output; |
| } |
| |
| // Checks if each sketch is empty or not |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::is_empty() const -> Array1D<bool> { |
| auto vals = make_ndarray<bool>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].is_empty(); |
| } |
| return vals; |
| } |
| |
| // Updates each sketch with values |
| // Currently: all values must be present |
| // TODO: allow subsets of sketches to be updated |
| template<typename T, typename C> |
| void vector_of_kll_sketches<T, C>::update(nb::ndarray<T>& items, char order) { |
| size_t ndim = items.ndim(); |
| |
| if (items.shape(ndim-1) != d_) { |
| throw std::invalid_argument("input data must have rows with " + std::to_string(d_) |
| + " elements. Found: " + std::to_string(items.shape(ndim-1))); |
| } |
| |
| if (ndim == 1) { |
| // 1D case: single value to update per sketch |
| const T* data = items.data(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| sketches_[i].update(data[i]); |
| } |
| } |
| else if (ndim == 2) { |
| // 2D case: multiple values to update per sketch |
| // We could speedthis up by using raw array access and pre-computing an offset for the |
| // row/column, but if we use the wrong ordering the computation would be incorrect. |
| // By using a view and dereferencing by (row, column) each time we ensure correct |
| // processing at the cost of an extra multiply each derference. Using a mismatched |
| // ordering versus the actual data storage will be potentially slower but will still |
| // produce correct output. |
| auto data = items.template view<nb::ndim<2>>(); |
| if (order == 'F' || order == 'f') { // Fortran-style (column-major) order |
| for (uint32_t j = 0; j < d_; ++j) { |
| const size_t offset = j * d_; |
| for (uint32_t i = 0; i < items.shape(0); ++i) { |
| sketches_[j].update(data(i, j)); |
| } |
| } |
| } else { // nb::c_contig or nb::any_contig |
| for (uint32_t i = 0; i < items.shape(0); ++i) { |
| const size_t offset = i * items.shape(0); |
| for (uint32_t j = 0; j < d_; ++j) { |
| sketches_[j].update(data(i, j)); |
| } |
| } |
| } |
| } |
| else { |
| throw std::invalid_argument("Update input must be 2 or fewer dimensions : " + std::to_string(ndim)); |
| } |
| } |
| |
| // Merges two arrays of sketches |
| // Currently: all values must be present |
| template<typename T, typename C> |
| void vector_of_kll_sketches<T, C>::merge(const vector_of_kll_sketches<T>& other) { |
| if (d_ != other.get_d()) { |
| throw std::invalid_argument("Must have same number of dimensions to merge: " + std::to_string(d_) |
| + " vs " + std::to_string(other.d_)); |
| } else { |
| for (uint32_t i = 0; i < d_; ++i) { |
| sketches_[i].merge(other.sketches_[i]); |
| } |
| } |
| } |
| |
| template<typename T, typename C> |
| kll_sketch<T, C> vector_of_kll_sketches<T, C>::collapse(ArrInputType<int>& isk) const { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> index_arr = get_indices(indices); |
| auto inds = index_arr.view(); |
| |
| kll_sketch<T, C> result(k_); |
| for (size_t idx = 0; idx < inds.shape(0); ++idx) { |
| result.merge(sketches_[inds(idx)]); |
| } |
| return result; |
| } |
| |
| // Number of updates for each sketch |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_n() const -> Array1D<uint64_t> { |
| auto vals = make_ndarray<uint64_t>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].get_n(); |
| } |
| return vals; |
| } |
| |
| // Number of retained values for each sketch |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_num_retained() const -> Array1D<uint32_t> { |
| auto vals = make_ndarray<uint32_t>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].get_num_retained(); |
| } |
| return vals; |
| } |
| |
| // Gets the minimum value of each sketch |
| // TODO: allow subsets of sketches |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_min_values() const -> Array1D<T> { |
| //std::vector<T> vals(d_); |
| auto vals = make_ndarray<T>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].get_min_item(); |
| } |
| return vals; |
| } |
| |
| // Gets the maximum value of each sketch |
| // TODO: allow subsets of sketches |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_max_values() const -> Array1D<T> { |
| auto vals = make_ndarray<T>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].get_max_item(); |
| } |
| return vals; |
| } |
| |
| // Summary of each sketch as one long string |
| // Users should use .split('\n\n') when calling it to build a list of each |
| // sketch's summary |
| template<typename T, typename C> |
| std::string vector_of_kll_sketches<T, C>::to_string(bool print_levels, bool print_items) const { |
| std::ostringstream ss; |
| for (uint32_t i = 0; i < d_; ++i) { |
| // all streams into 1 string, for compatibility with Python's str() behavior |
| // users will need to split by \n\n, e.g., str(kll).split('\n\n') |
| if (i > 0) ss << "\n"; |
| ss << sketches_[i].to_string(print_levels, print_items); |
| } |
| return ss.str(); |
| } |
| |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::is_estimation_mode() const -> Array1D<bool> { |
| auto vals = make_ndarray<bool>(d_); |
| auto view = vals.view(); |
| for (uint32_t i = 0; i < d_; ++i) { |
| view(i) = sketches_[i].is_estimation_mode(); |
| } |
| return vals; |
| } |
| |
| // Value of sketch(es) corresponding to some quantile(s) |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_quantiles(ArrInputType<double>& ranks, |
| ArrInputType<int>& isk) const -> Array2D<T> { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> inds = get_indices(indices); |
| size_t num_sketches = inds.size(); |
| |
| Array1D<double> ranks_arr = input_to_vec<double>(ranks); |
| size_t num_quantiles = ranks_arr.size(); |
| |
| auto quants = make_ndarray<T>(num_sketches, num_quantiles); |
| auto view = quants.view(); |
| auto ranks_view = ranks_arr.view(); |
| for (uint32_t i = 0; i < num_sketches; ++i) { |
| for (size_t j = 0; j < num_quantiles; ++j) { |
| view(i, j) = sketches_[inds(i)].get_quantile(ranks_view(j)); |
| } |
| } |
| return quants; |
| } |
| |
| // Value of sketch(es) corresponding to some rank(s) |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_ranks(ArrInputType<T>& values, |
| ArrInputType<int>& isk) const -> Array2D<double> { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> inds = get_indices(indices); |
| size_t num_sketches = inds.size(); |
| |
| Array1D<T> values_arr = input_to_vec<T>(values); |
| size_t num_ranks = values_arr.size(); |
| auto vals = values_arr.view(); |
| |
| auto ranks = make_ndarray<double>(num_sketches, num_ranks); |
| auto view = ranks.view(); |
| for (uint32_t i = 0; i < num_sketches; ++i) { |
| const size_t offset = i * num_ranks; |
| for (size_t j = 0; j < num_ranks; ++j) { |
| view(i, j) = sketches_[inds(i)].get_rank(vals(j)); |
| } |
| } |
| return ranks; |
| } |
| |
| // PMF(s) of sketch(es) |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_pmf(ArrInputType<T>& split_points, |
| ArrInputType<int>& isk) const -> Array2D<double> { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> inds = get_indices(indices); |
| size_t num_sketches = inds.size(); |
| |
| Array1D<T> splits_arr = input_to_vec<T>(split_points); |
| size_t num_splits = splits_arr.size(); |
| |
| auto pmfs = make_ndarray<double>(num_sketches, num_splits + 1); |
| auto view = pmfs.view(); |
| for (uint32_t i = 0; i < num_sketches; ++i) { |
| auto pmf = sketches_[inds(i)].get_PMF(splits_arr.data(), num_splits); |
| for (size_t j = 0; j <= num_splits; ++j) { |
| view(i, j) = pmf[j]; |
| } |
| } |
| return pmfs; |
| } |
| |
| // CDF(s) of sketch(es) |
| template<typename T, typename C> |
| auto vector_of_kll_sketches<T, C>::get_cdf(ArrInputType<T>& split_points, |
| ArrInputType<int>& isk) const -> Array2D<double> { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> inds = get_indices(indices); |
| size_t num_sketches = inds.size(); |
| |
| Array1D<T> splits_arr = input_to_vec<T>(split_points); |
| size_t num_splits = splits_arr.size(); |
| |
| auto cdfs = make_ndarray<double>(num_sketches, num_splits + 1); |
| auto view = cdfs.view(); |
| for (uint32_t i = 0; i < num_sketches; ++i) { |
| auto cdf = sketches_[inds(i)].get_CDF(splits_arr.data(), num_splits); |
| for (size_t j = 0; j <= num_splits; ++j) { |
| view(i, j) = cdf[j]; |
| } |
| } |
| return cdfs; |
| } |
| |
| template<typename T, typename C> |
| void vector_of_kll_sketches<T, C>::deserialize(const nb::bytes& sk_bytes, |
| uint32_t idx) { |
| if (idx >= d_) { |
| throw std::invalid_argument("request for invalid dimensions >= d (" |
| + std::to_string(d_) +"): "+ std::to_string(idx)); |
| } |
| // load the sketch into the proper index |
| sketches_[idx] = std::move(kll_sketch<T>::deserialize(sk_bytes.c_str(), sk_bytes.size())); |
| } |
| |
| template<typename T, typename C> |
| nb::list vector_of_kll_sketches<T, C>::serialize(ArrInputType<int>& isk) { |
| Array1D<int> indices = input_to_vec<int>(isk); |
| Array1D<uint32_t> inds = get_indices(indices); |
| const size_t num_sketches = inds.size(); |
| |
| nb::list list; |
| for (uint32_t i = 0; i < num_sketches; ++i) { |
| auto serResult = sketches_[inds(i)].serialize(); |
| list.append(nb::bytes((char*)serResult.data(), serResult.size())); |
| } |
| |
| return list; |
| } |
| |
| } // namespace datasketches |
| |
| template<typename T> |
| void bind_vector_of_kll_sketches(nb::module_ &m, const char* name) { |
| using namespace datasketches; |
| |
| nb::class_<vector_of_kll_sketches<T>>(m, name) |
| .def(nb::init<uint32_t, uint32_t>(), nb::arg("k")=vector_of_kll_constants::DEFAULT_K, |
| nb::arg("d")=vector_of_kll_constants::DEFAULT_D, |
| "Creates a new Vector of KLL Sketches instance with the given values of k and d.\n\n" |
| ":param k: The value of k for every sketch in the vector\n:type k: int\n" |
| ":param d: The number of sketches in the vector\n:type d: int" |
| ) |
| .def("__copy__", [](const vector_of_kll_sketches<T>& sk){ return vector_of_kll_sketches<T>(sk); }) |
| // allow user to retrieve k or d, in case it's instantiated w/ defaults |
| .def_prop_ro("k", &vector_of_kll_sketches<T>::get_k, |
| "The value of `k` of the sketch(es)") |
| .def_prop_ro("d", &vector_of_kll_sketches<T>::get_d, |
| "The number of sketches") |
| .def("update", &vector_of_kll_sketches<T>::update, nb::arg("items"), nb::arg("order") = "C", |
| "Updates the sketch(es) with value(s). Must be a 1D array of size equal to the number of sketches. Can also be 2D array of shape (n_updates, n_sketches). If a sketch does not have a value to update, use np.nan. " |
| " Order 'F' specifies a column-major (Fortran style) matrix; any other value assumes row-major (C style) matrix.") |
| .def("__str__", [](const vector_of_kll_sketches<T>& sk) { return sk.to_string(); }, |
| "Produces a string summary of all sketches. Users should split the returned string by '\\n\\n'") |
| .def("to_string", &vector_of_kll_sketches<T>::to_string, nb::arg("print_levels")=false, |
| nb::arg("print_items")=false, |
| "Produces a string summary of all sketches. Users should split the returned string by '\\n\\n'") |
| .def("is_empty", &vector_of_kll_sketches<T>::is_empty, |
| "Returns whether the sketch(es) is(are) empty of not") |
| .def("get_n", &vector_of_kll_sketches<T>::get_n, |
| "Returns the number of values seen by the sketch(es)") |
| .def("get_num_retained", &vector_of_kll_sketches<T>::get_num_retained, |
| "Returns the number of values retained by the sketch(es)") |
| .def("is_estimation_mode", &vector_of_kll_sketches<T>::is_estimation_mode, |
| "Returns whether the sketch(es) is(are) in estimation mode") |
| .def("get_min_values", &vector_of_kll_sketches<T>::get_min_values, |
| "Returns the minimum value(s) of the sketch(es)") |
| .def("get_max_values", &vector_of_kll_sketches<T>::get_max_values, |
| "Returns the maximum value(s) of the sketch(es)") |
| .def("get_quantiles", &vector_of_kll_sketches<T>::get_quantiles, nb::arg("ranks"), |
| nb::arg("isk")=-1, |
| "Returns the value(s) associated with the specified quantile(s) for the specified sketch(es). `ranks` can be a float between 0 and 1 (inclusive), or a list/array of values. `isk` specifies which sketch(es) to return the value(s) for (default: all sketches)") |
| .def("get_ranks", &vector_of_kll_sketches<T>::get_ranks, |
| nb::arg("value"), nb::arg("isk")=-1, |
| "Returns the value(s) associated with the specified rank(s) for the specified sketch(es). `values` can be an int between 0 and the number of values retained, or a list/array of values. `isk` specifies which sketch(es) to return the value(s) for (default: all sketches)") |
| .def("get_pmf", &vector_of_kll_sketches<T>::get_pmf, nb::arg("split_points"), nb::arg("isk")=-1, |
| "Returns the probability mass function (PMF) at `split_points` of the specified sketch(es). `split_points` should be a list/array of floats between 0 and 1 (inclusive). `isk` specifies which sketch(es) to return the PMF for (default: all sketches)") |
| .def("get_cdf", &vector_of_kll_sketches<T>::get_cdf, nb::arg("split_points"), nb::arg("isk")=-1, |
| "Returns the cumulative distribution function (CDF) at `split_points` of the specified sketch(es). `split_points` should be a list/array of floats between 0 and 1 (inclusive). `isk` specifies which sketch(es) to return the CDF for (default: all sketches)") |
| .def_static("get_normalized_rank_error", |
| [](uint16_t k, bool pmf) { return kll_sketch<T>::get_normalized_rank_error(k, pmf); }, |
| nb::arg("k"), nb::arg("as_pmf"), "Returns the normalized rank error") |
| .def("serialize", &vector_of_kll_sketches<T>::serialize, nb::arg("isk")=-1, |
| "Serializes the specified sketch(es). `isk` can be an int or a list/array of ints (default: all sketches)") |
| .def("deserialize", &vector_of_kll_sketches<T>::deserialize, nb::arg("skBytes"), nb::arg("isk"), |
| "Deserializes the specified sketch. `isk` must be an int.") |
| .def("merge", &vector_of_kll_sketches<T>::merge, nb::arg("array_of_sketches"), |
| "Merges the input array of KLL sketches into the existing array.") |
| .def("collapse", &vector_of_kll_sketches<T>::collapse, nb::arg("isk")=-1, |
| "Returns the result of collapsing all sketches in the array into a single sketch. 'isk' can be an int or a list/array of ints (default: all sketches)") |
| ; |
| } |
| |
| void init_vector_of_kll(nb::module_ &m) { |
| bind_vector_of_kll_sketches<int>(m, "vector_of_kll_ints_sketches"); |
| bind_vector_of_kll_sketches<float>(m, "vector_of_kll_floats_sketches"); |
| } |