usearch 0.9.7 → 0.11.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.
Files changed (41) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/.gitmodules +3 -0
  3. package/.vscode/settings.json +2 -0
  4. package/Cargo.toml +1 -1
  5. package/Package.swift +1 -0
  6. package/README.md +1 -1
  7. package/VERSION +1 -1
  8. package/binding.gyp +4 -3
  9. package/build.gradle +1 -1
  10. package/build.rs +1 -0
  11. package/conanfile.py +1 -1
  12. package/docs/compilation.md +6 -3
  13. package/include/usearch/usearch.hpp +168 -45
  14. package/package.json +1 -1
  15. package/python/lib.cpp +42 -23
  16. package/python/scripts/bench.py +2 -2
  17. package/python/scripts/test.py +1 -1
  18. package/python/usearch/index.py +298 -0
  19. package/robin-map/.codecov.yml +5 -0
  20. package/robin-map/.github/workflows/ci.yml +136 -0
  21. package/robin-map/CMakeLists.txt +86 -0
  22. package/robin-map/LICENSE +21 -0
  23. package/robin-map/README.md +483 -0
  24. package/robin-map/cmake/tsl-robin-mapConfig.cmake.in +9 -0
  25. package/robin-map/doxygen.conf +2483 -0
  26. package/robin-map/include/tsl/robin_growth_policy.h +406 -0
  27. package/robin-map/include/tsl/robin_hash.h +1639 -0
  28. package/robin-map/include/tsl/robin_map.h +807 -0
  29. package/robin-map/include/tsl/robin_set.h +660 -0
  30. package/robin-map/tests/CMakeLists.txt +26 -0
  31. package/robin-map/tests/custom_allocator_tests.cpp +138 -0
  32. package/robin-map/tests/main.cpp +26 -0
  33. package/robin-map/tests/policy_tests.cpp +97 -0
  34. package/robin-map/tests/robin_map_tests.cpp +1451 -0
  35. package/robin-map/tests/robin_set_tests.cpp +164 -0
  36. package/robin-map/tests/utils.h +443 -0
  37. package/robin-map/tsl-robin-map.natvis +78 -0
  38. package/setup.py +4 -2
  39. package/src/CMakeLists.txt +1 -0
  40. package/src/punned.hpp +56 -6
  41. package/src/test.cpp +32 -8
@@ -69,6 +69,7 @@ include(clipp)
69
69
  set(USEARCH_PUNNED_INCLUDE_DIRS
70
70
  "${CMAKE_CURRENT_SOURCE_DIR}/../include"
71
71
  "${CMAKE_CURRENT_SOURCE_DIR}/../fp16/include"
72
+ "${CMAKE_CURRENT_SOURCE_DIR}/../robin-map/include"
72
73
  "${CMAKE_CURRENT_SOURCE_DIR}/../simsimd/include"
73
74
  "${CMAKE_CURRENT_SOURCE_DIR}/"
74
75
  )
package/src/punned.hpp CHANGED
@@ -1,13 +1,15 @@
1
1
  #pragma once
2
2
  #include <stdlib.h> // `aligned_alloc`
3
3
 
4
- #include <functional> // `std::function`
5
- #include <numeric> // `std::iota`
6
- #include <stdexcept> // `std::invalid_argument`
7
- #include <thread> // `std::thread`
8
- #include <vector> // `std::vector`
4
+ #include <functional> // `std::function`
5
+ #include <numeric> // `std::iota`
6
+ #include <shared_mutex> // `std::shared_mutex`
7
+ #include <stdexcept> // `std::invalid_argument`
8
+ #include <thread> // `std::thread`
9
+ #include <vector> // `std::vector`
9
10
 
10
11
  #include <fp16/fp16.h>
12
+ #include <tsl/robin_map.h>
11
13
 
12
14
  #include <usearch/usearch.hpp>
13
15
 
@@ -401,6 +403,8 @@ class punned_gt {
401
403
 
402
404
  using member_iterator_t = typename index_t::member_iterator_t;
403
405
  using member_citerator_t = typename index_t::member_citerator_t;
406
+ using member_ref_t = typename index_t::member_ref_t;
407
+ using member_cref_t = typename index_t::member_cref_t;
404
408
 
405
409
  std::size_t dimensions_ = 0;
406
410
  std::size_t casted_vector_bytes_ = 0;
@@ -414,6 +418,10 @@ class punned_gt {
414
418
  cast_t from_f16{};
415
419
  cast_t from_f32{};
416
420
  cast_t from_f64{};
421
+ cast_t to_f8{};
422
+ cast_t to_f16{};
423
+ cast_t to_f32{};
424
+ cast_t to_f64{};
417
425
  } casts_;
418
426
 
419
427
  punned_stateful_metric_t root_metric_;
@@ -421,6 +429,13 @@ class punned_gt {
421
429
  mutable std::vector<std::size_t> available_threads_;
422
430
  mutable std::mutex available_threads_mutex_;
423
431
 
432
+ using shared_mutex_t = std::mutex; // TODO: Find an OS-compatible solution
433
+ using shared_lock_t = std::unique_lock<shared_mutex_t>;
434
+ using unique_lock_t = std::unique_lock<shared_mutex_t>;
435
+
436
+ mutable shared_mutex_t lookup_table_mutex_;
437
+ tsl::robin_map<label_t, id_t> lookup_table_;
438
+
424
439
  public:
425
440
  using search_results_t = typename index_t::search_results_t;
426
441
  using add_result_t = typename index_t::add_result_t;
@@ -455,6 +470,8 @@ class punned_gt {
455
470
  std::size_t capacity() const { return typed_->capacity(); }
456
471
  config_t const& config() const { return typed_->config(); }
457
472
  void clear() { return typed_->clear(); }
473
+ void change_expansion_add(std::size_t n) noexcept { typed_->change_expansion_add(n); }
474
+ void change_expansion_search(std::size_t n) noexcept { typed_->change_expansion_search(n); }
458
475
 
459
476
  member_citerator_t cbegin() const noexcept { return typed_->cbegin(); }
460
477
  member_citerator_t cend() const noexcept { return typed_->cend(); }
@@ -474,6 +491,10 @@ class punned_gt {
474
491
  void view(char const* path) { typed_->view(path); }
475
492
  void reserve(std::size_t capacity) { typed_->reserve(capacity); }
476
493
 
494
+ std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const noexcept {
495
+ return typed_->memory_usage(allocator_entry_bytes);
496
+ }
497
+
477
498
  // clang-format off
478
499
  add_result_t add(label_t label, f8_bits_t const* vector) { return add_(label, vector, casts_.from_f8); }
479
500
  add_result_t add(label_t label, f16_bits_t const* vector) { return add_(label, vector, casts_.from_f16); }
@@ -505,6 +526,11 @@ class punned_gt {
505
526
  search_results_t search_around(label_t hint, f32_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f32); }
506
527
  search_results_t search_around(label_t hint, f64_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f64); }
507
528
 
529
+ void reconstruct(label_t label, f8_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f8); }
530
+ void reconstruct(label_t label, f16_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f16); }
531
+ void reconstruct(label_t label, f32_t* vector) const { return reconstruct_(label, vector, casts_.to_f32); }
532
+ void reconstruct(label_t label, f64_t* vector) const { return reconstruct_(label, vector, casts_.to_f64); }
533
+
508
534
  static punned_gt ip(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make_(dimensions, accuracy, ip_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
509
535
  static punned_gt l2sq(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, config_t config = {}) { return make_(dimensions, accuracy, l2_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
510
536
  static punned_gt cos(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f32_k, config_t config = {}) { return make_(dimensions, accuracy, cos_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
@@ -583,7 +609,12 @@ class punned_gt {
583
609
  if (casted)
584
610
  vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
585
611
 
586
- return typed_->add(label, {vector_data, vector_bytes}, config);
612
+ add_result_t result = typed_->add(label, {vector_data, vector_bytes}, config);
613
+ {
614
+ unique_lock_t lock(lookup_table_mutex_);
615
+ lookup_table_.emplace(label, result.id);
616
+ }
617
+ return result;
587
618
  }
588
619
 
589
620
  template <typename scalar_at>
@@ -618,6 +649,21 @@ class punned_gt {
618
649
  return typed_->search_around(static_cast<id_t>(hint), {vector_data, vector_bytes}, wanted, config);
619
650
  }
620
651
 
652
+ id_t lookup_id_(label_t label) const {
653
+ shared_lock_t lock(lookup_table_mutex_);
654
+ return lookup_table_.at(label);
655
+ }
656
+
657
+ template <typename scalar_at> void reconstruct_(label_t label, scalar_at* reconstructed, cast_t const& cast) const {
658
+ id_t id = lookup_id_(label);
659
+ member_citerator_t iterator = typed_->cbegin() + id;
660
+ member_cref_t member = *iterator;
661
+ byte_t const* casted_vector = reinterpret_cast<byte_t const*>(member.vector.data());
662
+ bool casted = cast(casted_vector, casted_vector_bytes_, (byte_t*)reconstructed);
663
+ if (!casted)
664
+ std::memcpy(reconstructed, casted_vector, casted_vector_bytes_);
665
+ }
666
+
621
667
  template <typename scalar_at> add_result_t add_(label_t label, scalar_at const* vector, cast_t const& cast) {
622
668
  thread_lock_t lock = thread_lock_();
623
669
  add_config_t add_config;
@@ -680,6 +726,10 @@ class punned_gt {
680
726
  result.from_f16 = cast_gt<f16_bits_t, to_scalar_at>{};
681
727
  result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
682
728
  result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
729
+ result.to_f8 = cast_gt<to_scalar_at, f8_bits_t>{};
730
+ result.to_f16 = cast_gt<to_scalar_at, f16_bits_t>{};
731
+ result.to_f32 = cast_gt<to_scalar_at, f32_t>{};
732
+ result.to_f64 = cast_gt<to_scalar_at, f64_t>{};
683
733
  return result;
684
734
  }
685
735
 
package/src/test.cpp CHANGED
@@ -12,7 +12,7 @@ using namespace unum;
12
12
  using point_id_t = std::int64_t;
13
13
 
14
14
  template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
15
- using span_t = span_gt<scalar_at const>;
15
+ using view_t = span_gt<scalar_at const>;
16
16
  using distance_t = typename index_at::distance_t;
17
17
 
18
18
  scalar_at vec[3] = {10, 20, 15};
@@ -20,12 +20,12 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
20
20
  scalar_at vec_b[3] = {16, 17, 18};
21
21
 
22
22
  index.reserve(10);
23
- index.add(42, span_t{&vec[0], 3ul});
23
+ index.add(42, view_t{&vec[0], 3ul});
24
24
 
25
25
  // Default approximate search
26
26
  point_id_t matched_labels[10] = {0};
27
27
  distance_t matched_distances[10] = {0};
28
- std::size_t matched_count = index.search(span_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
28
+ std::size_t matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
29
29
 
30
30
  assert(matched_count == 1);
31
31
  assert(matched_labels[0] == 42);
@@ -34,12 +34,12 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
34
34
  // Add more entries
35
35
  search_config_t search_config;
36
36
  search_config.exact = true;
37
- index.add(43, span_t{&vec_a[0], 3ul});
38
- index.add(44, span_t{&vec_b[0], 3ul});
37
+ index.add(43, view_t{&vec_a[0], 3ul});
38
+ index.add(44, view_t{&vec_b[0], 3ul});
39
39
  assert(index.size() == 3);
40
40
 
41
41
  // Perform exact search
42
- matched_count = index.search(span_t{&vec[0], 3ul}, 5, search_config).dump_to(matched_labels, matched_distances);
42
+ matched_count = index.search(view_t{&vec[0], 3ul}, 5, search_config).dump_to(matched_labels, matched_distances);
43
43
 
44
44
  // Validate scans
45
45
  std::size_t count = 0;
@@ -54,17 +54,38 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
54
54
  // Search again over reconstructed index
55
55
  index.save("tmp.usearch");
56
56
  index.load("tmp.usearch");
57
- matched_count = index.search(span_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
57
+ matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
58
58
  assert(matched_count == 3);
59
59
  assert(matched_labels[0] == 42);
60
60
  assert(std::abs(matched_distances[0]) < 0.01);
61
61
 
62
62
  // Search again over mapped index
63
63
  index.view("tmp.usearch");
64
- matched_count = index.search(span_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
64
+ matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
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);
71
+ }
72
+
73
+ template <typename scalar_at, typename index_at> void test3d_punned(index_at&& index) {
74
+ using view_t = span_gt<scalar_at const>;
75
+ using span_t = span_gt<scalar_at>;
76
+ using distance_t = typename index_at::distance_t;
77
+
78
+ scalar_at vec[3] = {10, 20, 15};
79
+
80
+ index.reserve(10);
81
+ index.add(42, view_t{&vec[0], 3ul});
82
+
83
+ // Reconstruct
84
+ scalar_at vec_reconstructed[3] = {0, 0, 0};
85
+ index.reconstruct(42, span_t{&vec_reconstructed[0], 3ul});
86
+ assert(vec_reconstructed[0] == vec[0]);
87
+ assert(vec_reconstructed[1] == vec[1]);
88
+ assert(vec_reconstructed[2] == vec[2]);
68
89
  }
69
90
 
70
91
  int main(int, char**) {
@@ -81,5 +102,8 @@ int main(int, char**) {
81
102
  test3d<double>(punned_small_t::cos(3));
82
103
  test3d<double>(punned_small_t::l2sq(3));
83
104
 
105
+ test3d_punned<float>(punned_small_t::cos(3));
106
+ test3d_punned<float>(punned_small_t::l2sq(3));
107
+
84
108
  return 0;
85
109
  }