usearch 0.9.7 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "0.9.7"
3
+ version = "0.10.0"
4
4
  authors = ["Ashot Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.7
1
+ 0.10.0
package/conanfile.py CHANGED
@@ -4,7 +4,7 @@ from conans import ConanFile
4
4
  class USearchConan(ConanFile):
5
5
 
6
6
  name = 'USearch'
7
- version = '0.9.7'
7
+ version = '0.10.0'
8
8
  license = 'Apache 2.0'
9
9
  url = 'https://github.com/unum-cloud/usearch'
10
10
  description = 'Smaller & Faster Single-File Vector Search Engine from Unum'
@@ -781,6 +781,7 @@ template <typename scalar_at> class span_gt {
781
781
  constexpr std::size_t default_connectivity() { return 16; }
782
782
  constexpr std::size_t default_expansion_add() { return 128; }
783
783
  constexpr std::size_t default_expansion_search() { return 64; }
784
+ constexpr std::size_t default_allocator_entry_bytes() { return 64; }
784
785
 
785
786
  /**
786
787
  * @brief Configuration settings for the index construction.
@@ -1416,6 +1417,7 @@ class index_gt {
1416
1417
  std::size_t nodes;
1417
1418
  std::size_t edges;
1418
1419
  std::size_t max_edges;
1420
+ std::size_t allocated_bytes;
1419
1421
  };
1420
1422
 
1421
1423
  stats_t stats() const noexcept {
@@ -1428,6 +1430,7 @@ class index_gt {
1428
1430
  for (level_t level = 0; level != node.level(); ++level)
1429
1431
  edges += neighbors_(node, level).size();
1430
1432
 
1433
+ result.allocated_bytes += node_bytes_(node);
1431
1434
  result.edges += edges;
1432
1435
  result.max_edges += max_edges;
1433
1436
  }
@@ -1437,16 +1440,46 @@ class index_gt {
1437
1440
  stats_t stats(std::size_t level) const noexcept {
1438
1441
  stats_t result{};
1439
1442
  result.nodes = size();
1443
+
1444
+ std::size_t neighbors_bytes = !level ? pre_.neighbors_base_bytes : pre_.neighbors_bytes;
1440
1445
  for (std::size_t i = 0; i != result.nodes; ++i) {
1441
1446
  node_t node = node_with_id_(i);
1442
- if (node.level() >= level)
1443
- result.edges += neighbors_(node, level).size();
1447
+ if (node.level() < level)
1448
+ continue;
1449
+
1450
+ result.edges += neighbors_(node, level).size();
1451
+ result.allocated_bytes += node_head_bytes_() + node_vector_bytes_(node) + neighbors_bytes;
1444
1452
  }
1453
+
1445
1454
  std::size_t max_edges_per_node = level ? config_.connectivity : base_level_multiple_() * config_.connectivity;
1446
1455
  result.max_edges = result.nodes * max_edges_per_node;
1447
1456
  return result;
1448
1457
  }
1449
1458
 
1459
+ /**
1460
+ * @brief A relatively accurate lower bound on the amount of memory consumed by the system.
1461
+ * In practice it's error will be below 10%.
1462
+ */
1463
+ std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const noexcept {
1464
+ std::size_t total = 0;
1465
+ if (viewed_file_descriptor_ == 0) {
1466
+ stats_t s = stats();
1467
+ total += s.allocated_bytes;
1468
+ total += s.nodes * allocator_entry_bytes;
1469
+ }
1470
+
1471
+ // Temporary data-structures, proportional to the number of nodes:
1472
+ total += nodes_.capacity() * sizeof(node_t) + allocator_entry_bytes;
1473
+
1474
+ // Temporary data-structures, proportional to the number of threads:
1475
+ std::size_t per_thread = (std::max)(config_.expansion_search, config_.expansion_add) * sizeof(candidate_t);
1476
+ total += thread_contexts_.capacity() * (sizeof(thread_context_t) + per_thread) + allocator_entry_bytes * 3;
1477
+ return total;
1478
+ }
1479
+
1480
+ void change_expansion_add(std::size_t n) noexcept { config_.expansion_add = n; }
1481
+ void change_expansion_search(std::size_t n) noexcept { config_.expansion_search = n; }
1482
+
1450
1483
  #pragma endregion
1451
1484
 
1452
1485
  #pragma region Serialization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.9.7",
3
+ "version": "0.10.0",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
package/python/lib.cpp CHANGED
@@ -405,6 +405,9 @@ template <typename index_at> void save_index(index_at const& index, std::string
405
405
  template <typename index_at> void load_index(index_at& index, std::string const& path) { index.load(path.c_str()); }
406
406
  template <typename index_at> void view_index(index_at& index, std::string const& path) { index.view(path.c_str()); }
407
407
  template <typename index_at> void clear_index(index_at& index) { index.clear(); }
408
+ template <typename index_at> std::size_t get_expansion_add(index_at const &index) { return index.config().expansion_add; }
409
+ template <typename index_at> std::size_t get_expansion_search(index_at const &index) { return index.config().expansion_search; }
410
+
408
411
  // clang-format on
409
412
 
410
413
  template <typename element_at> bool has_duplicates(element_at const* begin, element_at const* end) {
@@ -522,6 +525,10 @@ PYBIND11_MODULE(index, m) {
522
525
  i.def_property_readonly("capacity", &punned_py_t::capacity);
523
526
  i.def_property_readonly( //
524
527
  "dtype", [](punned_py_t const& index) -> std::string { return accuracy_name(index.accuracy()); });
528
+ i.def_property_readonly("memory_usage", &punned_py_t::memory_usage);
529
+
530
+ i.def_property("expansion_add", &get_expansion_add<punned_py_t>, &punned_py_t::change_expansion_add);
531
+ i.def_property("expansion_search", &get_expansion_search<punned_py_t>, &punned_py_t::change_expansion_search);
525
532
 
526
533
  i.def("save", &save_index<punned_py_t>, py::arg("path"));
527
534
  i.def("load", &load_index<punned_py_t>, py::arg("path"));
package/src/punned.hpp CHANGED
@@ -455,6 +455,8 @@ class punned_gt {
455
455
  std::size_t capacity() const { return typed_->capacity(); }
456
456
  config_t const& config() const { return typed_->config(); }
457
457
  void clear() { return typed_->clear(); }
458
+ void change_expansion_add(std::size_t n) noexcept { typed_->change_expansion_add(n); }
459
+ void change_expansion_search(std::size_t n) noexcept { typed_->change_expansion_search(n); }
458
460
 
459
461
  member_citerator_t cbegin() const noexcept { return typed_->cbegin(); }
460
462
  member_citerator_t cend() const noexcept { return typed_->cend(); }
@@ -474,6 +476,10 @@ class punned_gt {
474
476
  void view(char const* path) { typed_->view(path); }
475
477
  void reserve(std::size_t capacity) { typed_->reserve(capacity); }
476
478
 
479
+ std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const noexcept {
480
+ return typed_->memory_usage(allocator_entry_bytes);
481
+ }
482
+
477
483
  // clang-format off
478
484
  add_result_t add(label_t label, f8_bits_t const* vector) { return add_(label, vector, casts_.from_f8); }
479
485
  add_result_t add(label_t label, f16_bits_t const* vector) { return add_(label, vector, casts_.from_f16); }
package/src/test.cpp CHANGED
@@ -65,6 +65,9 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
65
65
  assert(matched_count == 3);
66
66
  assert(matched_labels[0] == 42);
67
67
  assert(std::abs(matched_distances[0]) < 0.01);
68
+
69
+ assert(index.memory_usage() > 0);
70
+ assert(index.stats().max_edges > 0);
68
71
  }
69
72
 
70
73
  int main(int, char**) {