usearch 0.22.3 → 1.1.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 (93) hide show
  1. package/.github/workflows/prerelease.yml +3 -3
  2. package/.releaserc +10 -2
  3. package/.vscode/launch.json +10 -10
  4. package/.vscode/settings.json +9 -1
  5. package/.vscode/tasks.json +4 -4
  6. package/CMakeLists.txt +6 -1
  7. package/Cargo.toml +2 -2
  8. package/README.md +35 -41
  9. package/build.rs +2 -2
  10. package/c/CMakeLists.txt +48 -0
  11. package/c/README.md +34 -0
  12. package/c/lib.cpp +83 -72
  13. package/c/test.c +20 -20
  14. package/c/usearch.h +132 -32
  15. package/conanfile.py +1 -1
  16. package/cpp/CMakeLists.txt +3 -1
  17. package/cpp/README.md +16 -16
  18. package/cpp/bench.cpp +50 -126
  19. package/cpp/test.cpp +164 -80
  20. package/docs/benchmarks.md +26 -5
  21. package/docs/c/reference.rst +3 -3
  22. package/docs/compilation.md +31 -49
  23. package/docs/cpp/index.rst +1 -1
  24. package/docs/cpp/reference.rst +4 -4
  25. package/golang/lib.go +275 -141
  26. package/golang/lib_test.go +105 -0
  27. package/include/usearch/index.hpp +1681 -1656
  28. package/include/usearch/index_dense.hpp +1400 -0
  29. package/include/usearch/index_plugins.hpp +1263 -0
  30. package/java/README.md +1 -1
  31. package/java/cloud/unum/usearch/Index.java +10 -10
  32. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
  33. package/java/test/IndexTest.java +1 -1
  34. package/javascript/README.md +6 -6
  35. package/javascript/lib.cpp +156 -55
  36. package/javascript/test.js +26 -11
  37. package/javascript/usearch.d.ts +102 -0
  38. package/javascript/usearch.js +2 -118
  39. package/objc/USearchObjective.mm +57 -31
  40. package/objc/include/USearchObjective.h +32 -22
  41. package/package.json +5 -3
  42. package/python/README.md +23 -14
  43. package/python/lib.cpp +468 -444
  44. package/python/scripts/bench.ipynb +3 -2
  45. package/python/scripts/bench.py +4 -4
  46. package/python/scripts/index_faiss.py +5 -5
  47. package/python/scripts/join.py +4 -4
  48. package/python/scripts/test.py +152 -61
  49. package/python/scripts/test_jit.py +10 -12
  50. package/python/scripts/test_sparse.py +32 -0
  51. package/python/usearch/client.py +26 -28
  52. package/python/usearch/eval.py +73 -23
  53. package/python/usearch/index.py +520 -228
  54. package/python/usearch/io.py +57 -25
  55. package/python/usearch/numba.py +4 -4
  56. package/python/usearch/server.py +10 -10
  57. package/rust/lib.cpp +34 -30
  58. package/rust/lib.hpp +14 -12
  59. package/rust/lib.rs +8 -7
  60. package/setup.py +5 -2
  61. package/simsimd/.github/workflows/prerelease.yml +73 -0
  62. package/simsimd/.github/workflows/release.yml +110 -0
  63. package/simsimd/.github/workflows/update_version.sh +3 -0
  64. package/simsimd/.releaserc +77 -0
  65. package/simsimd/.vscode/launch.json +27 -1
  66. package/simsimd/.vscode/settings.json +22 -1
  67. package/simsimd/.vscode/tasks.json +6 -0
  68. package/simsimd/CMakeLists.txt +3 -2
  69. package/simsimd/README.md +82 -46
  70. package/simsimd/bench.cxx +63 -24
  71. package/simsimd/include/simsimd/simsimd.h +88 -66
  72. package/simsimd/include/simsimd/simsimd_chem.h +126 -0
  73. package/simsimd/package.json +8 -0
  74. package/simsimd/pyproject.toml +42 -0
  75. package/simsimd/python/python.c +81 -0
  76. package/simsimd/python/run.sh +1 -0
  77. package/simsimd/python/test.c +109 -0
  78. package/simsimd/python/test.py +23 -0
  79. package/simsimd/setup.py +44 -0
  80. package/swift/Index+Sugar.swift +75 -23
  81. package/swift/README.md +26 -2
  82. package/swift/Test.swift +19 -7
  83. package/swift/USearch.swift +2 -2
  84. package/tsconfig.json +8 -0
  85. package/wasm/CMakeLists.txt +1 -0
  86. package/wasmer.toml +1 -1
  87. package/wolfram/CMakeLists.txt +1 -0
  88. package/wolfram/lib.cpp +11 -13
  89. package/golang/usearch.h +0 -9
  90. package/golang/usearch_par_test.go +0 -40
  91. package/golang/usearch_test.go +0 -82
  92. package/include/usearch/index_punned_dense.hpp +0 -1001
  93. package/include/usearch/index_punned_helpers.hpp +0 -676
@@ -1,1001 +0,0 @@
1
- #pragma once
2
- #include <stdlib.h> // `aligned_alloc`
3
-
4
- #include <functional> // `std::function`
5
- #include <numeric> // `std::iota`
6
- #include <shared_mutex> // `std::shared_mutex`
7
- #include <thread> // `std::thread`
8
- #include <vector> // `std::vector`
9
-
10
- #include <usearch/index.hpp>
11
- #include <usearch/index_punned_helpers.hpp>
12
-
13
- #include <tsl/robin_map.h>
14
-
15
- namespace unum {
16
- namespace usearch {
17
-
18
- struct cos_f8_t {
19
- using scalar_t = f8_bits_t;
20
- std::size_t dimensions;
21
-
22
- inline cos_f8_t(std::size_t dims) noexcept : dimensions(dims) {}
23
- inline metric_kind_t kind() const noexcept { return metric_kind_t::cos_k; }
24
- inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
25
- std::int32_t ab{}, a2{}, b2{};
26
- #if USEARCH_USE_OPENMP
27
- #pragma omp simd reduction(+ : ab, a2, b2)
28
- #elif defined(USEARCH_DEFINED_CLANG)
29
- #pragma clang loop vectorize(enable)
30
- #elif defined(USEARCH_DEFINED_GCC)
31
- #pragma GCC ivdep
32
- #endif
33
- for (std::size_t i = 0; i != dimensions; i++) {
34
- std::int16_t ai{a[i]};
35
- std::int16_t bi{b[i]};
36
- ab += ai * bi;
37
- a2 += square(ai);
38
- b2 += square(bi);
39
- }
40
- return (ab != 0) ? (1.f - ab / (std::sqrt(a2) * std::sqrt(b2))) : 0;
41
- }
42
- };
43
-
44
- struct l2sq_f8_t {
45
- using scalar_t = f8_bits_t;
46
- std::size_t dimensions;
47
-
48
- inline l2sq_f8_t(std::size_t dims) noexcept : dimensions(dims) {}
49
- inline metric_kind_t kind() const noexcept { return metric_kind_t::l2sq_k; }
50
- inline scalar_kind_t scalar_kind() const noexcept { return scalar_kind_t::f8_k; }
51
- inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
52
- std::int32_t ab_deltas_sq{};
53
- #if USEARCH_USE_OPENMP
54
- #pragma omp simd reduction(+ : ab_deltas_sq)
55
- #elif defined(USEARCH_DEFINED_CLANG)
56
- #pragma clang loop vectorize(enable)
57
- #elif defined(USEARCH_DEFINED_GCC)
58
- #pragma GCC ivdep
59
- #endif
60
- for (std::size_t i = 0; i != dimensions; i++)
61
- ab_deltas_sq += square(std::int16_t(a[i]) - std::int16_t(b[i]));
62
- return ab_deltas_sq;
63
- }
64
- };
65
-
66
- struct index_punned_dense_metric_t {
67
- using scalar_t = byte_t;
68
- using result_t = punned_distance_t;
69
- using view_t = punned_vector_view_t;
70
- using stl_func_t = std::function<punned_distance_t(punned_vector_view_t, punned_vector_view_t)>;
71
-
72
- stl_func_t func_;
73
- metric_kind_t kind_ = metric_kind_t::unknown_k;
74
- scalar_kind_t scalar_kind_ = scalar_kind_t::unknown_k;
75
- isa_t isa_ = isa_t::auto_k;
76
-
77
- index_punned_dense_metric_t() = default;
78
-
79
- template <typename typed_at, typename scalar_at = typename typed_at::scalar_t>
80
- index_punned_dense_metric_t(typed_at typed)
81
- : index_punned_dense_metric_t(typed.kind(), isa_t::auto_k, scalar_at{}, typed) {}
82
-
83
- template <typename typed_at, typename scalar_at>
84
- index_punned_dense_metric_t(metric_kind_t kind, isa_t isa, scalar_at, typed_at metric) {
85
- using scalar_t = scalar_at;
86
- using typed_view_t = span_gt<scalar_t const>;
87
- func_ = [=](view_t a, view_t b) -> result_t {
88
- std::size_t dims = a.size() / sizeof(scalar_t);
89
- typed_view_t a_typed{(scalar_t const*)a.data(), dims};
90
- typed_view_t b_typed{(scalar_t const*)b.data(), dims};
91
- return metric(a_typed, b_typed);
92
- };
93
- if (std::is_same<scalar_at, f8_bits_t>())
94
- scalar_kind_ = scalar_kind_t::f8_k;
95
- else if (std::is_same<scalar_at, f16_bits_t>())
96
- scalar_kind_ = scalar_kind_t::f16_k;
97
- else
98
- scalar_kind_ = common_scalar_kind<scalar_at>();
99
- kind_ = kind;
100
- isa_ = isa;
101
- }
102
-
103
- inline metric_kind_t kind() const noexcept { return kind_; }
104
- inline scalar_kind_t scalar_kind() const noexcept { return scalar_kind_; }
105
- inline result_t operator()(view_t a, view_t b) const { return func_(a, b); }
106
- };
107
-
108
- constexpr std::size_t default_removals_cycle() { return 64; }
109
-
110
- template <typename label_at, typename std::enable_if<std::is_integral<label_at>::value>::type* = nullptr>
111
- label_at default_free_value() {
112
- return std::numeric_limits<label_at>::max();
113
- }
114
-
115
- template <typename label_at, typename std::enable_if<std::is_same<label_at, uint40_t>::value>::type* = nullptr>
116
- uint40_t default_free_value() {
117
- return uint40_t::max();
118
- }
119
-
120
- template < //
121
- typename label_at, //
122
- typename std::enable_if<!std::is_integral<label_at>::value && //
123
- !std::is_same<label_at, uint40_t>::value>::type* = nullptr>
124
- label_at default_free_value() {
125
- return label_at();
126
- }
127
-
128
- /**
129
- * @brief Oversimplified type-punned index for equidimensional vectors
130
- * with automatic @b down-casting, hardware-specific @b SIMD metrics,
131
- * and ability to @b remove existing vectors, common in Semantic Caching
132
- * applications.
133
- */
134
- template <typename label_at = std::int64_t, typename id_at = std::uint32_t> //
135
- class index_punned_dense_gt {
136
- public:
137
- using label_t = label_at;
138
- using id_t = id_at;
139
- using distance_t = punned_distance_t;
140
- /// @brief Punned metric object.
141
- using metric_t = index_punned_dense_metric_t;
142
-
143
- private:
144
- /// @brief Schema: input buffer, bytes in input buffer, output buffer.
145
- using cast_t = std::function<bool(byte_t const*, std::size_t, byte_t*)>;
146
- /// @brief Punned index.
147
- using index_t = index_gt<metric_t, label_t, id_t, aligned_allocator_t, memory_mapping_allocator_t>;
148
- using index_allocator_t = aligned_allocator_gt<index_t, 64>;
149
-
150
- using member_iterator_t = typename index_t::member_iterator_t;
151
- using member_citerator_t = typename index_t::member_citerator_t;
152
- using member_ref_t = typename index_t::member_ref_t;
153
- using member_cref_t = typename index_t::member_cref_t;
154
-
155
- /// @brief Number of unique dimensions in the vectors.
156
- std::size_t dimensions_ = 0;
157
- /// @brief Similar to `dimensions_`, but different for fraction byte-length scalars.
158
- std::size_t scalar_words_ = 0;
159
- std::size_t expansion_add_ = 0;
160
- std::size_t expansion_search_ = 0;
161
- index_t* typed_ = nullptr;
162
-
163
- std::size_t casted_vector_bytes_ = 0;
164
- mutable std::vector<byte_t> cast_buffer_;
165
- struct casts_t {
166
- cast_t from_b1x8;
167
- cast_t from_f8;
168
- cast_t from_f16;
169
- cast_t from_f32;
170
- cast_t from_f64;
171
-
172
- cast_t to_b1x8;
173
- cast_t to_f8;
174
- cast_t to_f16;
175
- cast_t to_f32;
176
- cast_t to_f64;
177
- } casts_;
178
-
179
- metric_t root_metric_;
180
-
181
- mutable std::vector<std::size_t> available_threads_;
182
- mutable std::mutex available_threads_mutex_;
183
-
184
- using shared_mutex_t = std::mutex; // TODO: Find an OS-compatible solution
185
- using shared_lock_t = std::unique_lock<shared_mutex_t>;
186
- using unique_lock_t = std::unique_lock<shared_mutex_t>;
187
-
188
- mutable shared_mutex_t labeled_lookup_mutex_;
189
- tsl::robin_map<label_t, id_t> labeled_lookup_;
190
-
191
- mutable std::mutex free_ids_mutex_;
192
- ring_gt<id_t> free_ids_;
193
- label_t free_label_;
194
-
195
- public:
196
- using search_result_t = typename index_t::search_result_t;
197
- using add_result_t = typename index_t::add_result_t;
198
- using serialization_result_t = typename index_t::serialization_result_t;
199
- using join_result_t = typename index_t::join_result_t;
200
- using stats_t = typename index_t::stats_t;
201
- using match_t = typename index_t::match_t;
202
-
203
- index_punned_dense_gt() = default;
204
- index_punned_dense_gt(index_punned_dense_gt&& other)
205
- : dimensions_(std::move(other.dimensions_)), //
206
- scalar_words_(std::move(other.scalar_words_)), //
207
- expansion_add_(std::move(other.expansion_add_)), //
208
- expansion_search_(std::move(other.expansion_search_)), //
209
- typed_(exchange(other.typed_, nullptr)), //
210
- casted_vector_bytes_(std::move(other.casted_vector_bytes_)), //
211
- cast_buffer_(std::move(other.cast_buffer_)), //
212
- casts_(std::move(other.casts_)), //
213
- root_metric_(std::move(other.root_metric_)), //
214
- available_threads_(std::move(other.available_threads_)), //
215
- labeled_lookup_(std::move(other.labeled_lookup_)), //
216
- free_ids_(std::move(other.free_ids_)), //
217
- free_label_(std::move(other.free_label_)) {} //
218
-
219
- index_punned_dense_gt& operator=(index_punned_dense_gt&& other) {
220
- swap(other);
221
- return *this;
222
- }
223
-
224
- /**
225
- * @brief Swaps the contents of this index with another index.
226
- * @param other The other index to swap with.
227
- */
228
- void swap(index_punned_dense_gt& other) {
229
- std::swap(dimensions_, other.dimensions_);
230
- std::swap(scalar_words_, other.scalar_words_);
231
- std::swap(expansion_add_, other.expansion_add_);
232
- std::swap(expansion_search_, other.expansion_search_);
233
- std::swap(typed_, other.typed_);
234
- std::swap(casted_vector_bytes_, other.casted_vector_bytes_);
235
- std::swap(cast_buffer_, other.cast_buffer_);
236
- std::swap(casts_, other.casts_);
237
- std::swap(root_metric_, other.root_metric_);
238
- std::swap(available_threads_, other.available_threads_);
239
- std::swap(labeled_lookup_, other.labeled_lookup_);
240
- std::swap(free_ids_, other.free_ids_);
241
- std::swap(free_label_, other.free_label_);
242
- }
243
-
244
- ~index_punned_dense_gt() {
245
- if (typed_)
246
- typed_->~index_t();
247
- index_allocator_t{}.deallocate(typed_, 1);
248
- typed_ = nullptr;
249
- }
250
-
251
- static index_config_t optimize(index_config_t config) { return index_t::optimize(config); }
252
-
253
- /**
254
- * @brief Constructs an instance of ::index_punned_dense_gt.
255
- * @param[in] dimensions The of dimensions per vector.
256
- * @param[in] metric One of the default supported metric @b kinds for distance measurements.
257
- * @param[in] config The index configuration (optional).
258
- * @param[in] accuracy The scalar kind used for internal representations (optional).
259
- * @param[in] expansion_add The expansion factor for adding vectors (optional).
260
- * @param[in] expansion_search The expansion factor for searching vectors (optional).
261
- * @param[in] free_label The label used for freed vectors (optional).
262
- * @return An instance of ::index_punned_dense_gt.
263
- */
264
- static index_punned_dense_gt make( //
265
- std::size_t dimensions, metric_kind_t metric, //
266
- index_config_t config = {}, //
267
- scalar_kind_t accuracy = scalar_kind_t::f32_k, //
268
- std::size_t expansion_add = default_expansion_add(), //
269
- std::size_t expansion_search = default_expansion_search(), //
270
- label_t free_label = default_free_value<label_t>()) {
271
-
272
- return make_( //
273
- dimensions, accuracy, //
274
- config, expansion_add, expansion_search, //
275
- make_metric_(metric, dimensions, accuracy), make_casts_(accuracy), free_label);
276
- }
277
-
278
- /**
279
- * @brief Constructs an instance of ::index_punned_dense_gt.
280
- * @param[in] dimensions The of dimensions per vector.
281
- * @param[in] metric The @b ad-hoc metric wrapped for third-party distance measures.
282
- * @param[in] config The index configuration (optional).
283
- * @param[in] accuracy The scalar kind used for internal representations (optional).
284
- * @param[in] expansion_add The expansion factor for adding vectors (optional).
285
- * @param[in] expansion_search The expansion factor for searching vectors (optional).
286
- * @param[in] free_label The label used for freed vectors (optional).
287
- * @return An instance of ::index_punned_dense_gt.
288
- */
289
- static index_punned_dense_gt make( //
290
- std::size_t dimensions, metric_t metric, //
291
- index_config_t config = {}, //
292
- scalar_kind_t accuracy = scalar_kind_t::f32_k, //
293
- std::size_t expansion_add = default_expansion_add(), //
294
- std::size_t expansion_search = default_expansion_search(), //
295
- label_t free_label = default_free_value<label_t>()) {
296
-
297
- return make_( //
298
- dimensions, accuracy, //
299
- config, expansion_add, expansion_search, //
300
- metric_t(metric), make_casts_(accuracy), free_label);
301
- }
302
-
303
- std::size_t dimensions() const { return dimensions_; }
304
- std::size_t scalar_words() const { return scalar_words_; }
305
- std::size_t connectivity() const { return typed_->connectivity(); }
306
- std::size_t size() const { return typed_->size() - free_ids_.size(); }
307
- std::size_t capacity() const { return typed_->capacity(); }
308
- std::size_t max_level() const noexcept { return typed_->max_level(); }
309
- index_config_t const& config() const { return typed_->config(); }
310
- index_limits_t const& limits() const { return typed_->limits(); }
311
-
312
- metric_t const& metric() const { return root_metric_; }
313
- std::size_t expansion_add() const { return expansion_add_; }
314
- std::size_t expansion_search() const { return expansion_search_; }
315
- void change_expansion_add(std::size_t n) { expansion_add_ = n; }
316
- void change_expansion_search(std::size_t n) { expansion_search_ = n; }
317
-
318
- member_citerator_t cbegin() const { return typed_->cbegin(); }
319
- member_citerator_t cend() const { return typed_->cend(); }
320
- member_citerator_t begin() const { return typed_->begin(); }
321
- member_citerator_t end() const { return typed_->end(); }
322
- member_iterator_t begin() { return typed_->begin(); }
323
- member_iterator_t end() { return typed_->end(); }
324
-
325
- stats_t stats() const { return typed_->stats(); }
326
- stats_t stats(std::size_t level) const { return typed_->stats(level); }
327
-
328
- std::size_t memory_usage() const {
329
- return typed_->memory_usage(0) + //
330
- typed_->tape_allocator().total_wasted() + //
331
- typed_->tape_allocator().total_reserved();
332
- }
333
-
334
- // clang-format off
335
- add_result_t add(label_t label, b1x8_t const* vector) { return add_(label, vector, casts_.from_b1x8); }
336
- add_result_t add(label_t label, f8_bits_t const* vector) { return add_(label, vector, casts_.from_f8); }
337
- add_result_t add(label_t label, f16_t const* vector) { return add_(label, vector, casts_.from_f16); }
338
- add_result_t add(label_t label, f32_t const* vector) { return add_(label, vector, casts_.from_f32); }
339
- add_result_t add(label_t label, f64_t const* vector) { return add_(label, vector, casts_.from_f64); }
340
-
341
- add_result_t add(label_t label, b1x8_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_b1x8); }
342
- add_result_t add(label_t label, f8_bits_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f8); }
343
- add_result_t add(label_t label, f16_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f16); }
344
- add_result_t add(label_t label, f32_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f32); }
345
- add_result_t add(label_t label, f64_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f64); }
346
-
347
- search_result_t search(b1x8_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_b1x8); }
348
- search_result_t search(f8_bits_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f8); }
349
- search_result_t search(f16_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f16); }
350
- search_result_t search(f32_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f32); }
351
- search_result_t search(f64_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f64); }
352
-
353
- search_result_t search(b1x8_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_b1x8); }
354
- search_result_t search(f8_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f8); }
355
- search_result_t search(f16_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f16); }
356
- search_result_t search(f32_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f32); }
357
- search_result_t search(f64_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f64); }
358
-
359
- bool get(label_t label, b1x8_t* vector) const { return get_(label, vector, casts_.to_b1x8); }
360
- bool get(label_t label, f8_bits_t* vector) const { return get_(label, vector, casts_.to_f8); }
361
- bool get(label_t label, f16_t* vector) const { return get_(label, vector, casts_.to_f16); }
362
- bool get(label_t label, f32_t* vector) const { return get_(label, vector, casts_.to_f32); }
363
- bool get(label_t label, f64_t* vector) const { return get_(label, vector, casts_.to_f64); }
364
- // clang-format on
365
-
366
- search_result_t empty_search_result() const { return search_result_t{*typed_}; }
367
-
368
- /**
369
- * @brief Reserves memory for the index and the labeled lookup.
370
- * @return `true` if the memory reservation was successful, `false` otherwise.
371
- */
372
- bool reserve(index_limits_t limits) {
373
- {
374
- unique_lock_t lock(labeled_lookup_mutex_);
375
- labeled_lookup_.reserve(limits.members);
376
- }
377
- return typed_->reserve(limits);
378
- }
379
-
380
- /**
381
- * @brief Clears the whole index, reclaiming the memory.
382
- */
383
- void clear() {
384
- unique_lock_t lookup_lock(labeled_lookup_mutex_);
385
- std::unique_lock<std::mutex> free_lock(free_ids_mutex_);
386
- typed_->clear();
387
- labeled_lookup_.clear();
388
- free_ids_.clear();
389
- }
390
-
391
- /**
392
- * @brief Saves the index to a file.
393
- * @param[in] path The path to the file.
394
- * @return Outcome descriptor explictly convertable to boolean.
395
- */
396
- serialization_result_t save(char const* path) const { return typed_->save(path); }
397
-
398
- /**
399
- * @brief Parses the index from file to RAM.
400
- * @param[in] path The path to the file.
401
- * @return Outcome descriptor explictly convertable to boolean.
402
- */
403
- serialization_result_t load(char const* path) {
404
- serialization_result_t result = typed_->load(path);
405
- if (result)
406
- reindex_labels_();
407
- return result;
408
- }
409
-
410
- /**
411
- * @brief Parses the index from file, without loading it into RAM.
412
- * @param[in] path The path to the file.
413
- * @return Outcome descriptor explictly convertable to boolean.
414
- */
415
- serialization_result_t view(char const* path) {
416
- serialization_result_t result = typed_->view(path);
417
- if (result)
418
- reindex_labels_();
419
- return result;
420
- }
421
-
422
- /**
423
- * @brief Checks if a vector with specidied label is present.
424
- * @return `true` if the label is present in the index, `false` otherwise.
425
- */
426
- bool contains(label_t label) const {
427
- shared_lock_t lock(labeled_lookup_mutex_);
428
- return labeled_lookup_.contains(label);
429
- }
430
-
431
- struct labeling_result_t {
432
- error_t error{};
433
- std::size_t completed{};
434
-
435
- explicit operator bool() const noexcept { return !error; }
436
- labeling_result_t failed(error_t message) noexcept {
437
- error = std::move(message);
438
- return std::move(*this);
439
- }
440
- };
441
-
442
- /**
443
- * @brief Removes an entry with the specified label from the index.
444
- * @param[in] label The label of the entry to remove.
445
- * @return The ::labeling_result_t indicating the result of the removal operation.
446
- * If the removal was successful, `result.completed` will be `true`.
447
- * If the label was not found in the index, `result.completed` will be `false`.
448
- * If an error occurred during the removal operation, `result.error` will contain an error message.
449
- */
450
- labeling_result_t remove(label_t label) {
451
- labeling_result_t result;
452
-
453
- unique_lock_t lookup_lock(labeled_lookup_mutex_);
454
- auto id_terator = labeled_lookup_.find(label);
455
- if (id_terator == labeled_lookup_.end())
456
- return result;
457
-
458
- // Grow the removed entries ring, if needed
459
- std::unique_lock<std::mutex> free_lock(free_ids_mutex_);
460
- if (free_ids_.size() == free_ids_.capacity())
461
- if (!free_ids_.reserve((std::max<std::size_t>)(free_ids_.capacity() * 2, 64ul)))
462
- return result.failed("Can't allocate memory for a free-list");
463
-
464
- // A removed entry would be:
465
- // - present in `free_ids_`
466
- // - missing in the `labeled_lookup_`
467
- // - marked in the `typed_` index with a `free_label_`
468
- id_t id = id_terator->second;
469
- free_ids_.push(id);
470
- labeled_lookup_.erase(id_terator);
471
- typed_->at(id).label = free_label_;
472
- result.completed = true;
473
-
474
- return result;
475
- }
476
-
477
- /**
478
- * @brief Removes multiple entries with the specified labels from the index.
479
- * @param[in] labels_begin The beginning of the labels range.
480
- * @param[in] labels_end The ending of the labels range.
481
- * @return The ::labeling_result_t indicating the result of the removal operation.
482
- * `result.completed` will contain the number of labels that were successfully removed.
483
- * `result.error` will contain an error message if an error occurred during the removal operation.
484
- */
485
- template <typename labels_iterator_at>
486
- labeling_result_t remove(labels_iterator_at&& labels_begin, labels_iterator_at&& labels_end) {
487
-
488
- labeling_result_t result;
489
- unique_lock_t lookup_lock(labeled_lookup_mutex_);
490
- std::unique_lock<std::mutex> free_lock(free_ids_mutex_);
491
-
492
- // Grow the removed entries ring, if needed
493
- std::size_t count_requests = std::distance(labels_begin, labels_end);
494
- if (!free_ids_.reserve(free_ids_.size() + count_requests))
495
- return result.failed("Can't allocate memory for a free-list");
496
-
497
- // Remove them one-by-one
498
- for (auto label_it = labels_begin; label_it != labels_end; ++label_it) {
499
- label_t label = *label_it;
500
- auto id_terator = labeled_lookup_.find(label);
501
- if (id_terator == labeled_lookup_.end())
502
- continue;
503
-
504
- // A removed entry would be:
505
- // - present in `free_ids_`
506
- // - missing in the `labeled_lookup_`
507
- // - marked in the `typed_` index with a `free_label_`
508
- id_t id = id_terator->second;
509
- free_ids_.push(id);
510
- labeled_lookup_.erase(id_terator);
511
- typed_->at(id).label = free_label_;
512
- result.completed += 1;
513
- }
514
-
515
- return result;
516
- }
517
-
518
- /**
519
- * @brief Renames an entry with the specified label to a new label.
520
- * @param[in] from The current label of the entry to rename.
521
- * @param[in] to The new label to assign to the entry.
522
- * @return The ::labeling_result_t indicating the result of the rename operation.
523
- * If the rename was successful, `result.completed` will be `true`.
524
- * If the entry with the current label was not found, `result.completed` will be `false`.
525
- */
526
- labeling_result_t rename(label_t from, label_t to) {
527
- labeling_result_t result;
528
- unique_lock_t lookup_lock(labeled_lookup_mutex_);
529
- auto id_terator = labeled_lookup_.find(from);
530
- if (id_terator == labeled_lookup_.end())
531
- return result;
532
-
533
- id_t id = id_terator->second;
534
- labeled_lookup_.erase(id_terator);
535
- labeled_lookup_.emplace(to, id);
536
- typed_->at(id).label = to;
537
- result.completed = true;
538
- return result;
539
- }
540
-
541
- /**
542
- * @brief Exports a range of labels for the vectors present in the index.
543
- * @param[out] labels Pointer to the array where the labels will be exported.
544
- * @param[in] offset The number of labels to skip. Useful for pagination.
545
- * @param[in] limit The maximum number of labels to export, that can fit in ::labels.
546
- */
547
- void export_labels(label_t* labels, std::size_t offset, std::size_t limit) const {
548
- shared_lock_t lock(labeled_lookup_mutex_);
549
- auto it = labeled_lookup_.begin();
550
- offset = (std::min)(offset, labeled_lookup_.size());
551
- std::advance(it, offset);
552
- for (; it != labeled_lookup_.end() && limit; ++it, ++labels, --limit)
553
- *labels = it->first;
554
- }
555
-
556
- /**
557
- * @brief Adapts the Male-Optimal Stable Marriage algorithm for unequal sets
558
- * to perform fast one-to-one matching between two large collections
559
- * of vectors, using approximate nearest neighbors search.
560
- *
561
- * @param[inout] first_to_second Container to map ::first labels to ::second.
562
- * @param[inout] second_to_first Container to map ::second labels to ::first.
563
- * @param[in] executor Thread-pool to execute the job in parallel.
564
- * @param[in] progress Callback to report the execution progress.
565
- */
566
- template < //
567
- typename first_to_second_at = dummy_label_to_label_mapping_t, //
568
- typename second_to_first_at = dummy_label_to_label_mapping_t, //
569
- typename executor_at = dummy_executor_t, //
570
- typename progress_at = dummy_progress_t //
571
- >
572
- static join_result_t join( //
573
- index_punned_dense_gt const& first, //
574
- index_punned_dense_gt const& second, //
575
- join_config_t config = {}, //
576
- first_to_second_at&& first_to_second = first_to_second_at{}, //
577
- second_to_first_at&& second_to_first = second_to_first_at{}, //
578
- executor_at&& executor = executor_at{}, //
579
- progress_at&& progress = progress_at{}) noexcept {
580
-
581
- return index_t::join( //
582
- *first.typed_, *second.typed_, config, //
583
- std::forward<first_to_second_at>(first_to_second), //
584
- std::forward<second_to_first_at>(second_to_first), //
585
- std::forward<executor_at>(executor), //
586
- std::forward<progress_at>(progress));
587
- }
588
-
589
- struct copy_result_t {
590
- index_punned_dense_gt index;
591
- error_t error;
592
-
593
- explicit operator bool() const noexcept { return !error; }
594
- copy_result_t failed(error_t message) noexcept {
595
- error = std::move(message);
596
- return std::move(*this);
597
- }
598
- };
599
-
600
- /**
601
- * @brief Copies the ::index_punned_dense_gt @b with all the data in it.
602
- * @param config The copy configuration (optional).
603
- * @return A copy of the ::index_punned_dense_gt instance.
604
- */
605
- copy_result_t copy(copy_config_t config = {}) const {
606
- copy_result_t result = fork();
607
- if (!result)
608
- return result;
609
- auto typed_result = typed_->copy(config);
610
- if (!typed_result)
611
- return result.failed(std::move(typed_result.error));
612
- if (!result.index.free_ids_.reserve(free_ids_.size()))
613
- return result.failed(std::move(typed_result.error));
614
- for (std::size_t i = 0; i != free_ids_.size(); ++i)
615
- result.index.free_ids_.push(free_ids_[i]);
616
-
617
- result.index.labeled_lookup_ = labeled_lookup_;
618
- *result.index.typed_ = std::move(typed_result.index);
619
- return result;
620
- }
621
-
622
- /**
623
- * @brief Copies the ::index_punned_dense_gt model @b without any data.
624
- * @return A similarly configured ::index_punned_dense_gt instance.
625
- */
626
- copy_result_t fork() const {
627
- copy_result_t result;
628
- index_punned_dense_gt& other = result.index;
629
-
630
- other.dimensions_ = dimensions_;
631
- other.scalar_words_ = scalar_words_;
632
- other.expansion_add_ = expansion_add_;
633
- other.expansion_search_ = expansion_search_;
634
- other.casted_vector_bytes_ = casted_vector_bytes_;
635
- other.cast_buffer_ = cast_buffer_;
636
- other.casts_ = casts_;
637
-
638
- other.root_metric_ = root_metric_;
639
- other.available_threads_ = available_threads_;
640
-
641
- index_t* raw = index_allocator_t{}.allocate(1);
642
- if (!raw)
643
- return result.failed("Can't allocate the index");
644
-
645
- new (raw) index_t(config(), root_metric_);
646
- other.typed_ = raw;
647
- return result;
648
- }
649
-
650
- struct compaction_result_t {
651
- error_t error{};
652
- std::size_t pruned_edges{};
653
-
654
- explicit operator bool() const noexcept { return !error; }
655
- compaction_result_t failed(error_t message) noexcept {
656
- error = std::move(message);
657
- return std::move(*this);
658
- }
659
- };
660
-
661
- /**
662
- * @brief Performs compaction on the index, pruning links to removed entries.
663
- * @param executor The executor parallel processing. Default ::dummy_executor_t single-threaded.
664
- * @param progress The progress tracker instance to use. Default ::dummy_progress_t reports nothing.
665
- * @return The ::compaction_result_t indicating the result of the compaction operation.
666
- * `result.pruned_edges` will contain the number of edges that were removed.
667
- * `result.error` will contain an error message if an error occurred during the compaction operation.
668
- */
669
- template <typename executor_at = dummy_executor_t, typename progress_at = dummy_progress_t>
670
- compaction_result_t compact(executor_at&& executor = executor_at{}, progress_at&& progress = progress_at{}) {
671
- compaction_result_t result;
672
- std::atomic<std::size_t> pruned_edges;
673
- auto disallow = [&](member_cref_t const& member) noexcept {
674
- bool freed = member.label == free_label_;
675
- pruned_edges += freed;
676
- return freed;
677
- };
678
- typed_->isolate(disallow, std::forward<executor_at>(executor), std::forward<progress_at>(progress));
679
- result.pruned_edges = pruned_edges;
680
- return result;
681
- }
682
-
683
- private:
684
- struct thread_lock_t {
685
- index_punned_dense_gt const& parent;
686
- std::size_t thread_id;
687
-
688
- ~thread_lock_t() { parent.thread_unlock_(thread_id); }
689
- };
690
-
691
- thread_lock_t thread_lock_() const {
692
- available_threads_mutex_.lock();
693
- std::size_t thread_id = available_threads_.back();
694
- available_threads_.pop_back();
695
- available_threads_mutex_.unlock();
696
- return {*this, thread_id};
697
- }
698
-
699
- void thread_unlock_(std::size_t thread_id) const {
700
- available_threads_mutex_.lock();
701
- available_threads_.push_back(thread_id);
702
- available_threads_mutex_.unlock();
703
- }
704
-
705
- template <typename scalar_at>
706
- add_result_t add_(label_t label, scalar_at const* vector, add_config_t config, cast_t const& cast) {
707
- byte_t const* vector_data = reinterpret_cast<byte_t const*>(vector);
708
- std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
709
-
710
- byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
711
- bool casted = cast(vector_data, dimensions_, casted_data);
712
- if (casted)
713
- vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
714
-
715
- // Check if there are some removed entries, whose nodes we can reuse
716
- id_t free_id = default_free_value<id_t>();
717
- {
718
- std::unique_lock<std::mutex> lock(free_ids_mutex_);
719
- free_ids_.try_pop(free_id);
720
- }
721
-
722
- // Perform the insertion or the update
723
- add_result_t result = //
724
- free_id != default_free_value<id_t>() //
725
- ? typed_->update(free_id, label, {vector_data, vector_bytes}, config)
726
- : typed_->add(label, {vector_data, vector_bytes}, config);
727
- {
728
- unique_lock_t lock(labeled_lookup_mutex_);
729
- labeled_lookup_.emplace(label, result.id);
730
- }
731
- return result;
732
- }
733
-
734
- template <typename scalar_at>
735
- search_result_t search_( //
736
- scalar_at const* vector, std::size_t wanted, //
737
- search_config_t config, cast_t const& cast) const {
738
-
739
- byte_t const* vector_data = reinterpret_cast<byte_t const*>(vector);
740
- std::size_t vector_bytes = dimensions_ * sizeof(scalar_at);
741
-
742
- byte_t* casted_data = cast_buffer_.data() + casted_vector_bytes_ * config.thread;
743
- bool casted = cast(vector_data, dimensions_, casted_data);
744
- if (casted)
745
- vector_data = casted_data, vector_bytes = casted_vector_bytes_;
746
-
747
- auto allow = [=](match_t const& match) noexcept { return match.member.label != free_label_; };
748
- return typed_->search({vector_data, vector_bytes}, wanted, config, allow);
749
- }
750
-
751
- id_t lookup_id_(label_t label) const {
752
- shared_lock_t lock(labeled_lookup_mutex_);
753
- return labeled_lookup_.at(label);
754
- }
755
-
756
- void reindex_labels_() {
757
-
758
- // Estimate number of entries first
759
- std::size_t count_total = typed_->size();
760
- std::size_t count_removed = 0;
761
- for (std::size_t i = 0; i != count_total; ++i) {
762
- member_cref_t member = typed_->at(i);
763
- count_removed += member.label == free_label_;
764
- }
765
-
766
- unique_lock_t lock(labeled_lookup_mutex_);
767
- labeled_lookup_.clear();
768
- labeled_lookup_.reserve(count_total - count_removed);
769
- free_ids_.clear();
770
- free_ids_.reserve(count_removed);
771
- for (std::size_t i = 0; i != typed_->size(); ++i) {
772
- member_cref_t member = typed_->at(i);
773
- if (member.label == free_label_)
774
- free_ids_.push(static_cast<id_t>(i));
775
- else
776
- labeled_lookup_.emplace(member.label, static_cast<id_t>(i));
777
- }
778
- }
779
-
780
- template <typename scalar_at> bool get_(label_t label, scalar_at* reconstructed, cast_t const& cast) const {
781
- id_t id;
782
- // Find the matching ID
783
- {
784
- shared_lock_t lock(labeled_lookup_mutex_);
785
- auto it = labeled_lookup_.find(label);
786
- if (it == labeled_lookup_.end())
787
- return false;
788
- id = it->second;
789
- }
790
- // Export the entry
791
- member_cref_t member = typed_->at(id);
792
- byte_t const* punned_vector = reinterpret_cast<byte_t const*>(member.vector.data());
793
- bool casted = cast(punned_vector, dimensions_, (byte_t*)reconstructed);
794
- if (!casted)
795
- std::memcpy(reconstructed, punned_vector, casted_vector_bytes_);
796
- return true;
797
- }
798
-
799
- template <typename scalar_at> add_result_t add_(label_t label, scalar_at const* vector, cast_t const& cast) {
800
- thread_lock_t lock = thread_lock_();
801
- add_config_t add_config;
802
- add_config.thread = lock.thread_id;
803
- return add_(label, vector, add_config, cast);
804
- }
805
-
806
- template <typename scalar_at>
807
- search_result_t search_( //
808
- scalar_at const* vector, std::size_t wanted, //
809
- cast_t const& cast) const {
810
- thread_lock_t lock = thread_lock_();
811
- search_config_t search_config;
812
- search_config.thread = lock.thread_id;
813
- return search_(vector, wanted, search_config, cast);
814
- }
815
-
816
- static index_punned_dense_gt make_( //
817
- std::size_t dimensions, scalar_kind_t scalar_kind, //
818
- index_config_t config, std::size_t expansion_add, std::size_t expansion_search, //
819
- metric_t metric, casts_t casts, label_t free_label) {
820
-
821
- std::size_t hardware_threads = std::thread::hardware_concurrency();
822
- index_punned_dense_gt result;
823
- result.dimensions_ = dimensions;
824
- result.scalar_words_ = count_scalar_words_(dimensions, scalar_kind);
825
- result.expansion_add_ = expansion_add;
826
- result.expansion_search_ = expansion_search;
827
- result.casted_vector_bytes_ = bytes_per_scalar(scalar_kind) * result.scalar_words_;
828
- result.cast_buffer_.resize(hardware_threads * result.casted_vector_bytes_);
829
- result.casts_ = casts;
830
- result.root_metric_ = metric;
831
- result.free_label_ = free_label;
832
-
833
- // Fill the thread IDs.
834
- result.available_threads_.resize(hardware_threads);
835
- std::iota(result.available_threads_.begin(), result.available_threads_.end(), 0ul);
836
-
837
- // Available since C11, but only C++17, so we use the C version.
838
- index_t* raw = index_allocator_t{}.allocate(1);
839
- new (raw) index_t(config, metric);
840
- result.typed_ = raw;
841
- return result;
842
- }
843
-
844
- template <typename to_scalar_at> static casts_t make_casts_() {
845
- casts_t result;
846
-
847
- result.from_b1x8 = cast_gt<b1x8_t, to_scalar_at>{};
848
- result.from_f8 = cast_gt<f8_bits_t, to_scalar_at>{};
849
- result.from_f16 = cast_gt<f16_t, to_scalar_at>{};
850
- result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
851
- result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
852
-
853
- result.to_b1x8 = cast_gt<to_scalar_at, b1x8_t>{};
854
- result.to_f8 = cast_gt<to_scalar_at, f8_bits_t>{};
855
- result.to_f16 = cast_gt<to_scalar_at, f16_t>{};
856
- result.to_f32 = cast_gt<to_scalar_at, f32_t>{};
857
- result.to_f64 = cast_gt<to_scalar_at, f64_t>{};
858
-
859
- return result;
860
- }
861
-
862
- static casts_t make_casts_(scalar_kind_t accuracy) {
863
- switch (accuracy) {
864
- case scalar_kind_t::f64_k: return make_casts_<f64_t>();
865
- case scalar_kind_t::f32_k: return make_casts_<f32_t>();
866
- case scalar_kind_t::f16_k: return make_casts_<f16_t>();
867
- case scalar_kind_t::f8_k: return make_casts_<f8_bits_t>();
868
- case scalar_kind_t::b1x8_k: return make_casts_<b1x8_t>();
869
- default: return {};
870
- }
871
- }
872
-
873
- static std::size_t count_scalar_words_(std::size_t dimensions, scalar_kind_t accuracy) {
874
- switch (accuracy) {
875
- case scalar_kind_t::f64_k: return dimensions;
876
- case scalar_kind_t::f32_k: return dimensions;
877
- case scalar_kind_t::f16_k: return dimensions;
878
- case scalar_kind_t::f8_k: return dimensions;
879
- case scalar_kind_t::b1x8_k: return divide_round_up<CHAR_BIT>(dimensions);
880
- default: return {};
881
- }
882
- }
883
-
884
- static metric_t ip_metric_f32_(std::size_t dimensions) {
885
- (void)dimensions;
886
- #if USEARCH_USE_SIMSIMD
887
- if (hardware_supports(isa_t::avx2_k) && dimensions % 4 == 0)
888
- return {metric_kind_t::ip_k, isa_t::avx2_k, 0.f,
889
- [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32x4avx2(a, b, dimensions); }};
890
- if (hardware_supports(isa_t::sve_k))
891
- return {metric_kind_t::ip_k, isa_t::sve_k, 0.f,
892
- [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32sve(a, b, dimensions); }};
893
- if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
894
- return {metric_kind_t::ip_k, isa_t::neon_k, 0.f,
895
- [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32x4neon(a, b, dimensions); }};
896
- #endif
897
- return ip_gt<f32_t>{};
898
- }
899
-
900
- static metric_t cos_metric_f16_(std::size_t dimensions) {
901
- (void)dimensions;
902
- #if USEARCH_USE_SIMSIMD
903
- if (hardware_supports(isa_t::avx512_k) && dimensions % 16 == 0)
904
- return {metric_kind_t::cos_k, isa_t::avx512_k, simsimd_f16_t(0),
905
- [=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
906
- return 1.f - simsimd_cos_f16x16avx512(a, b, dimensions);
907
- }};
908
- if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
909
- return {metric_kind_t::cos_k, isa_t::neon_k, simsimd_f16_t(0),
910
- [=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
911
- return 1.f - simsimd_cos_f16x4neon(a, b, dimensions);
912
- }};
913
- #endif
914
- return cos_gt<f16_t, f32_t>{};
915
- }
916
-
917
- static metric_t cos_metric_f8_(std::size_t dimensions) {
918
- #if USEARCH_USE_SIMSIMD
919
- if (hardware_supports(isa_t::neon_k) && dimensions % 16 == 0)
920
- return {metric_kind_t::cos_k, isa_t::neon_k, int8_t(0),
921
- [=](int8_t const* a, int8_t const* b) { return 1.f - simsimd_cos_i8x16neon(a, b, dimensions); }};
922
- #endif
923
- return cos_f8_t{dimensions};
924
- }
925
-
926
- static metric_t ip_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
927
- switch (accuracy) {
928
- case scalar_kind_t::f32_k:
929
- // The two most common numeric types for the most common metric have optimized versions
930
- return ip_metric_f32_(dimensions);
931
- case scalar_kind_t::f16_k:
932
- // Dot-product accumulates error, Cosine-distance normalizes it
933
- return cos_metric_f16_(dimensions);
934
-
935
- case scalar_kind_t::f8_k: return cos_metric_f8_(dimensions);
936
- case scalar_kind_t::f64_k: return ip_gt<f64_t>{};
937
- default: return {};
938
- }
939
- }
940
-
941
- static metric_t l2sq_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
942
- switch (accuracy) {
943
- case scalar_kind_t::f8_k: return l2sq_f8_t{dimensions};
944
- case scalar_kind_t::f16_k: return l2sq_gt<f16_t, f32_t>{};
945
- case scalar_kind_t::f32_k: return l2sq_gt<f32_t>{};
946
- case scalar_kind_t::f64_k: return l2sq_gt<f64_t>{};
947
- default: return {};
948
- }
949
- }
950
-
951
- static metric_t cos_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
952
- switch (accuracy) {
953
- case scalar_kind_t::f8_k: return cos_metric_f8_(dimensions);
954
- case scalar_kind_t::f16_k: return cos_metric_f16_(dimensions);
955
- case scalar_kind_t::f32_k: return cos_gt<f32_t>{};
956
- case scalar_kind_t::f64_k: return cos_gt<f64_t>{};
957
- default: return {};
958
- }
959
- }
960
-
961
- static metric_t haversine_metric_(scalar_kind_t accuracy) {
962
- switch (accuracy) {
963
- case scalar_kind_t::f8_k: return haversine_gt<f8_bits_t, f32_t>{};
964
- case scalar_kind_t::f16_k: return haversine_gt<f16_t, f32_t>{};
965
- case scalar_kind_t::f32_k: return haversine_gt<f32_t>{};
966
- case scalar_kind_t::f64_k: return haversine_gt<f64_t>{};
967
- default: return {};
968
- }
969
- }
970
-
971
- static metric_t pearson_metric_(scalar_kind_t accuracy) {
972
- switch (accuracy) {
973
- case scalar_kind_t::f8_k: return pearson_correlation_gt<f8_bits_t, f32_t>{};
974
- case scalar_kind_t::f16_k: return pearson_correlation_gt<f16_t, f32_t>{};
975
- case scalar_kind_t::f32_k: return pearson_correlation_gt<f32_t>{};
976
- case scalar_kind_t::f64_k: return pearson_correlation_gt<f64_t>{};
977
- default: return {};
978
- }
979
- }
980
-
981
- static metric_t make_metric_(metric_kind_t kind, std::size_t dimensions, scalar_kind_t accuracy) {
982
- switch (kind) {
983
- case metric_kind_t::ip_k: return ip_metric_(dimensions, accuracy);
984
- case metric_kind_t::cos_k: return cos_metric_(dimensions, accuracy);
985
- case metric_kind_t::l2sq_k: return l2sq_metric_(dimensions, accuracy);
986
- case metric_kind_t::pearson_k: return pearson_metric_(accuracy);
987
- case metric_kind_t::haversine_k: return haversine_metric_(accuracy);
988
- case metric_kind_t::hamming_k: return hamming_gt<b1x8_t>{};
989
- case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
990
- case metric_kind_t::tanimoto_k: return tanimoto_gt<b1x8_t>{};
991
- case metric_kind_t::sorensen_k: return sorensen_gt<b1x8_t>{};
992
- default: return {};
993
- }
994
- }
995
- };
996
-
997
- using punned_small_t = index_punned_dense_gt<std::uint64_t, std::uint32_t>;
998
- using punned_big_t = index_punned_dense_gt<uuid_t, uint40_t>;
999
-
1000
- } // namespace usearch
1001
- } // namespace unum