usearch 0.10.0 → 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 +133 -43
  14. package/package.json +1 -1
  15. package/python/lib.cpp +36 -24
  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 +50 -6
  41. package/src/test.cpp +29 -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;
@@ -511,6 +526,11 @@ class punned_gt {
511
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); }
512
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); }
513
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
+
514
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); }
515
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); }
516
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); }
@@ -589,7 +609,12 @@ class punned_gt {
589
609
  if (casted)
590
610
  vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
591
611
 
592
- 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;
593
618
  }
594
619
 
595
620
  template <typename scalar_at>
@@ -624,6 +649,21 @@ class punned_gt {
624
649
  return typed_->search_around(static_cast<id_t>(hint), {vector_data, vector_bytes}, wanted, config);
625
650
  }
626
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
+
627
667
  template <typename scalar_at> add_result_t add_(label_t label, scalar_at const* vector, cast_t const& cast) {
628
668
  thread_lock_t lock = thread_lock_();
629
669
  add_config_t add_config;
@@ -686,6 +726,10 @@ class punned_gt {
686
726
  result.from_f16 = cast_gt<f16_bits_t, to_scalar_at>{};
687
727
  result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
688
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>{};
689
733
  return result;
690
734
  }
691
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,14 +54,14 @@ 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);
@@ -70,6 +70,24 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
70
70
  assert(index.stats().max_edges > 0);
71
71
  }
72
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]);
89
+ }
90
+
73
91
  int main(int, char**) {
74
92
 
75
93
  test3d<float>(index_gt<cos_gt<float>, point_id_t, std::uint32_t, float>{});
@@ -84,5 +102,8 @@ int main(int, char**) {
84
102
  test3d<double>(punned_small_t::cos(3));
85
103
  test3d<double>(punned_small_t::l2sq(3));
86
104
 
105
+ test3d_punned<float>(punned_small_t::cos(3));
106
+ test3d_punned<float>(punned_small_t::l2sq(3));
107
+
87
108
  return 0;
88
109
  }