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
package/cpp/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # USearch C++ Inteface
1
+ # USearch for C++
2
2
 
3
3
  ## Installation
4
4
 
@@ -18,15 +18,15 @@ This covers 90% of use-cases.
18
18
  ```c++
19
19
  using namespace unum::usearch;
20
20
 
21
- index_gt<cos_gt<float>> index;
21
+ index_gt<metric_cos_gt<float>> index;
22
22
  float vec[3] = {0.1, 0.3, 0.2};
23
23
 
24
24
  index.reserve(10);
25
- index.add(/* label: */ 42, /* vector: */ {&vec[0], 3});
25
+ index.add(/* key: */ 42, /* vector: */ {&vec[0], 3});
26
26
  auto results = index.search(/* query: */ {&vec[0], 3}, 5 /* neighbors */);
27
27
 
28
28
  for (std::size_t i = 0; i != results.size(); ++i)
29
- results[i].element.label, results[i].element.vector, results[i].distance;
29
+ results[i].element.key, results[i].element.vector, results[i].distance;
30
30
  ```
31
31
 
32
32
  The `add` is thread-safe for concurrent index construction.
@@ -44,8 +44,8 @@ index.view("index.usearch"); // Memory-mapping from disk
44
44
  For advanced users, more compile-time abstractions are available.
45
45
 
46
46
  ```cpp
47
- template <typename metric_at = ip_gt<float>, //
48
- typename label_at = std::size_t, // `uint32_t`, `uuid_t`...
47
+ template <typename metric_at = metric_ip_gt<float>, //
48
+ typename key_at = std::size_t, // `uint32_t`, `uuid_t`...
49
49
  typename id_at = std::uint32_t, // `uint40_t`, `uint64_t`...
50
50
  typename scalar_at = float, // `double`, `half`, `char`...
51
51
  typename allocator_at = std::allocator<char>> //
@@ -63,15 +63,15 @@ struct custom_metric_t {
63
63
 
64
64
  The following distances are pre-packaged:
65
65
 
66
- - `cos_gt<scalar_t>` for "Cosine" or "Angular" distance.
67
- - `ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
68
- - `l2sq_gt<scalar_t>` for the squared "L2" or "Euclidean" distance.
69
- - `jaccard_gt<scalar_t>` for "Jaccard" distance between two ordered sets of unique elements.
70
- - `hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
71
- - `tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
72
- - `sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
73
- - `pearson_correlation_gt<scalar_t>` for "Pearson" correlation between probability distributions.
74
- - `haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
66
+ - `metric_cos_gt<scalar_t>` for "Cosine" or "Angular" distance.
67
+ - `metric_ip_gt<scalar_t>` for "Inner Product" or "Dot Product" distance.
68
+ - `metric_l2sq_gt<scalar_t>` for the squared "L2" or "Euclidean" distance.
69
+ - `metric_jaccard_gt<scalar_t>` for "Jaccard" distance between two ordered sets of unique elements.
70
+ - `metric_hamming_gt<scalar_t>` for "Hamming" distance, as the number of shared bits in hashes.
71
+ - `metric_tanimoto_gt<scalar_t>` for "Tanimoto" coefficient for bit-strings.
72
+ - `metric_sorensen_gt<scalar_t>` for "Dice-Sorensen" coefficient for bit-strings.
73
+ - `metric_pearson_gt<scalar_t>` for "Pearson" correlation between probability distributions.
74
+ - `metric_haversine_gt<scalar_t>` for "Haversine" or "Great Circle" distance between coordinates used in GIS applications.
75
75
 
76
76
  ## Multi-Threading
77
77
 
@@ -81,7 +81,7 @@ Instead of spawning additional threads within USearch, we focus on the thread sa
81
81
  ```cpp
82
82
  #pragma omp parallel for
83
83
  for (std::size_t i = 0; i < n; ++i)
84
- native.add(label, span_t{vector, dims}, add_config_t { .thread = omp_get_thread_num() });
84
+ native.add(key, span_t{vector, dims}, index_update_config_t { .thread = omp_get_thread_num() });
85
85
  ```
86
86
 
87
87
  During initialization, we allocate enough temporary memory for all the cores on the machine.
package/cpp/bench.cpp CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @brief A benchmark for the construction speed of the USearch index
3
- * and the resulting accuracy (recall) of the Approximate Nearest Neighbors
3
+ * and the resulting quantization (recall) of the Approximate Nearest Neighbors
4
4
  * Search queries.
5
5
  */
6
6
 
@@ -44,14 +44,13 @@
44
44
 
45
45
  #include <simsimd/simsimd.h>
46
46
 
47
- #include <usearch/index_punned_dense.hpp>
47
+ #include <usearch/index_dense.hpp>
48
48
 
49
49
  using namespace unum::usearch;
50
50
  using namespace unum;
51
51
 
52
- using label_t = std::int64_t;
53
- using vector_id_t = std::uint32_t;
54
- using vector_view_t = span_gt<float const>;
52
+ using compressed_slot_t = std::uint32_t;
53
+ using float_span_t = span_gt<float const>;
55
54
 
56
55
  template <typename element_at>
57
56
  std::size_t offset_of(element_at const* begin, element_at const* end, element_at v) noexcept {
@@ -151,10 +150,10 @@ struct vectors_view_gt {
151
150
  template <typename scalar_at, typename vector_id_at> //
152
151
  struct persisted_dataset_gt {
153
152
  using scalar_t = scalar_at;
154
- using vector_id_t = vector_id_at;
153
+ using compressed_slot_t = vector_id_at;
155
154
  persisted_matrix_gt<scalar_t> vectors_;
156
155
  persisted_matrix_gt<scalar_t> queries_;
157
- persisted_matrix_gt<vector_id_t> neighborhoods_;
156
+ persisted_matrix_gt<compressed_slot_t> neighborhoods_;
158
157
  std::size_t vectors_to_skip_{};
159
158
  std::size_t vectors_to_take_{};
160
159
 
@@ -174,7 +173,7 @@ struct persisted_dataset_gt {
174
173
  std::size_t neighborhood_size() const noexcept { return neighborhoods_.cols; }
175
174
  scalar_t const* vector(std::size_t i) const noexcept { return vectors_.row(i + vectors_to_skip_); }
176
175
  scalar_t const* query(std::size_t i) const noexcept { return queries_.row(i); }
177
- vector_id_t const* neighborhood(std::size_t i) const noexcept { return neighborhoods_.row(i); }
176
+ compressed_slot_t const* neighborhood(std::size_t i) const noexcept { return neighborhoods_.row(i); }
178
177
 
179
178
  std::size_t vectors_count() const noexcept {
180
179
  return vectors_to_take_ ? vectors_to_take_ : (vectors_.rows - vectors_to_skip_);
@@ -187,11 +186,11 @@ struct persisted_dataset_gt {
187
186
  template <typename scalar_at, typename vector_id_at> //
188
187
  struct in_memory_dataset_gt {
189
188
  using scalar_t = scalar_at;
190
- using vector_id_t = vector_id_at;
189
+ using compressed_slot_t = vector_id_at;
191
190
 
192
191
  std::vector<scalar_t> vectors_{};
193
192
  std::vector<scalar_t> queries_{};
194
- std::vector<vector_id_t> neighborhoods_{};
193
+ std::vector<compressed_slot_t> neighborhoods_{};
195
194
  std::size_t dimensions_{};
196
195
  std::size_t vectors_count_{};
197
196
  std::size_t neighborhood_size_{};
@@ -210,13 +209,13 @@ struct in_memory_dataset_gt {
210
209
  std::size_t neighborhood_size() const noexcept { return 1; }
211
210
  scalar_t const* vector(std::size_t i) const noexcept { return vectors_.data() + i * dimensions_; }
212
211
  scalar_t const* query(std::size_t i) const noexcept { return queries_.data() + i * dimensions_; }
213
- vector_id_t const* neighborhood(std::size_t i) const noexcept {
212
+ compressed_slot_t const* neighborhood(std::size_t i) const noexcept {
214
213
  return neighborhoods_.data() + i * neighborhood_size_;
215
214
  }
216
215
 
217
216
  scalar_t* vector(std::size_t i) noexcept { return vectors_.data() + i * dimensions_; }
218
217
  scalar_t* query(std::size_t i) noexcept { return queries_.data() + i * dimensions_; }
219
- vector_id_t* neighborhood(std::size_t i) noexcept { return neighborhoods_.data() + i * neighborhood_size_; }
218
+ compressed_slot_t* neighborhood(std::size_t i) noexcept { return neighborhoods_.data() + i * neighborhood_size_; }
220
219
 
221
220
  vectors_view_gt<scalar_t> vectors_view() const noexcept { return {vector(0), vectors_count(), dimensions()}; }
222
221
  };
@@ -279,7 +278,7 @@ struct running_stats_printer_t {
279
278
  };
280
279
 
281
280
  template <typename index_at, typename vector_id_at, typename real_at>
282
- void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_at const* vectors, std::size_t dims) {
281
+ void index_many(index_at& index, std::size_t n, vector_id_at const* ids, real_at const* vectors, std::size_t dims) {
283
282
 
284
283
  running_stats_printer_t printer{n, "Indexing"};
285
284
 
@@ -287,13 +286,12 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
287
286
  #pragma omp parallel for schedule(static, 32)
288
287
  #endif
289
288
  for (std::size_t i = 0; i < n; ++i) {
290
- add_config_t config;
289
+ index_update_config_t config;
291
290
  #if USEARCH_USE_OPENMP
292
291
  config.thread = omp_get_thread_num();
293
292
  #endif
294
- config.store_vector = true;
295
- vector_view_t vector{vectors + dims * i, dims};
296
- native.add(ids[i], vector, config);
293
+ float_span_t vector{vectors + dims * i, dims};
294
+ index.add(ids[i], vector, config);
297
295
  printer.progress++;
298
296
  if (config.thread == 0)
299
297
  printer.refresh();
@@ -302,7 +300,7 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
302
300
 
303
301
  template <typename index_at, typename vector_id_at, typename real_at>
304
302
  void search_many( //
305
- index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
303
+ index_at& index, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
306
304
  real_at* distances) {
307
305
 
308
306
  std::string name = "Search " + std::to_string(wanted);
@@ -312,12 +310,12 @@ void search_many( //
312
310
  #pragma omp parallel for schedule(static, 32)
313
311
  #endif
314
312
  for (std::size_t i = 0; i < n; ++i) {
315
- search_config_t config;
313
+ index_search_config_t config;
316
314
  #if USEARCH_USE_OPENMP
317
315
  config.thread = omp_get_thread_num();
318
316
  #endif
319
- vector_view_t vector{vectors + dims * i, dims};
320
- native.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
317
+ float_span_t vector{vectors + dims * i, dims};
318
+ index.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
321
319
  printer.progress++;
322
320
  if (config.thread == 0)
323
321
  printer.refresh();
@@ -326,22 +324,20 @@ void search_many( //
326
324
 
327
325
  template <typename dataset_at, typename index_at> //
328
326
  static void single_shot(dataset_at& dataset, index_at& index, bool construct = true) {
329
- using label_t = typename index_at::label_t;
330
327
  using distance_t = typename index_at::distance_t;
331
- using join_result_t = typename index_at::join_result_t;
332
- constexpr label_t missing_label = std::numeric_limits<label_t>::max();
328
+ constexpr default_key_t missing_key = std::numeric_limits<default_key_t>::max();
333
329
 
334
330
  std::printf("\n");
335
331
  std::printf("------------\n");
336
332
  if (construct) {
337
333
  // Perform insertions, evaluate speed
338
- std::vector<label_t> ids(dataset.vectors_count());
334
+ std::vector<default_key_t> ids(dataset.vectors_count());
339
335
  std::iota(ids.begin(), ids.end(), 0);
340
336
  index_many(index, dataset.vectors_count(), ids.data(), dataset.vector(0), dataset.dimensions());
341
337
  }
342
338
 
343
339
  // Perform search, evaluate speed
344
- std::vector<label_t> found_neighbors(dataset.queries_count() * dataset.neighborhood_size());
340
+ std::vector<default_key_t> found_neighbors(dataset.queries_count() * dataset.neighborhood_size());
345
341
  std::vector<distance_t> found_distances(dataset.queries_count() * dataset.neighborhood_size());
346
342
  search_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), dataset.neighborhood_size(),
347
343
  found_neighbors.data(), found_distances.data());
@@ -352,39 +348,39 @@ static void single_shot(dataset_at& dataset, index_at& index, bool construct = t
352
348
  auto expected = dataset.neighborhood(i);
353
349
  auto received = found_neighbors.data() + i * dataset.neighborhood_size();
354
350
  recall_at_1 += expected[0] == received[0];
355
- recall_full += contains(received, received + dataset.neighborhood_size(), label_t{expected[0]});
351
+ recall_full += contains(received, received + dataset.neighborhood_size(), default_key_t{expected[0]});
356
352
  }
357
353
 
358
354
  std::printf("Recall@1 %.2f %%\n", recall_at_1 * 100.f / dataset.queries_count());
359
355
  std::printf("Recall %.2f %%\n", recall_full * 100.f / dataset.queries_count());
360
356
 
361
357
  // Perform joins
362
- std::vector<label_t> man_to_woman(dataset.vectors_count());
363
- std::vector<label_t> woman_to_man(dataset.vectors_count());
358
+ std::vector<default_key_t> man_to_woman(dataset.vectors_count());
359
+ std::vector<default_key_t> woman_to_man(dataset.vectors_count());
364
360
  std::size_t join_attempts = 0;
365
361
  {
366
362
  index_at& men = index;
367
363
  index_at women = index.copy().index;
368
- std::fill(man_to_woman.begin(), man_to_woman.end(), missing_label);
369
- std::fill(woman_to_man.begin(), woman_to_man.end(), missing_label);
364
+ std::fill(man_to_woman.begin(), man_to_woman.end(), missing_key);
365
+ std::fill(woman_to_man.begin(), woman_to_man.end(), missing_key);
370
366
  {
371
367
  executor_default_t executor(index.limits().threads());
372
368
  running_stats_printer_t printer{1, "Join"};
373
- join_result_t result = index_at::join( //
374
- men, women, join_config_t{executor.size()}, //
375
- man_to_woman.data(), woman_to_man.data(), //
369
+ join_result_t result = join( //
370
+ men, women, index_join_config_t{executor.size()}, //
371
+ man_to_woman.data(), woman_to_man.data(), //
376
372
  executor, [&](std::size_t progress, std::size_t total) {
377
373
  if (progress % 1000 == 0)
378
374
  printer.print(progress, total);
379
375
  });
380
- join_attempts = result.cycles;
376
+ join_attempts = result.visited_members;
381
377
  }
382
378
  }
383
379
  // Evaluate join quality
384
380
  std::size_t recall_join = 0, unmatched_count = 0;
385
381
  for (std::size_t i = 0; i != index.size(); ++i) {
386
- recall_join += man_to_woman[i] == static_cast<label_t>(i);
387
- unmatched_count += man_to_woman[i] == missing_label;
382
+ recall_join += man_to_woman[i] == static_cast<default_key_t>(i);
383
+ unmatched_count += man_to_woman[i] == missing_key;
388
384
  }
389
385
  std::printf("Recall Joins %.2f %%\n", recall_join * 100.f / index.size());
390
386
  std::printf("Unmatched %.2f %% (%zu items)\n", unmatched_count * 100.f / index.size(), unmatched_count);
@@ -453,10 +449,9 @@ struct args_t {
453
449
  bool help = false;
454
450
 
455
451
  bool big = false;
456
- bool native = false;
457
452
 
458
453
  bool quantize_f16 = false;
459
- bool quantize_f8 = false;
454
+ bool quantize_i8 = false;
460
455
  bool quantize_b1 = false;
461
456
 
462
457
  bool metric_ip = false;
@@ -483,11 +478,11 @@ struct args_t {
483
478
  return metric_kind_t::ip_k;
484
479
  }
485
480
 
486
- scalar_kind_t accuracy() const noexcept {
481
+ scalar_kind_t quantization() const noexcept {
487
482
  if (quantize_f16)
488
483
  return scalar_kind_t::f16_k;
489
- if (quantize_f8)
490
- return scalar_kind_t::f8_k;
484
+ if (quantize_i8)
485
+ return scalar_kind_t::i8_k;
491
486
  if (quantize_b1)
492
487
  return scalar_kind_t::b1x8_k;
493
488
  return scalar_kind_t::f32_k;
@@ -497,15 +492,16 @@ struct args_t {
497
492
  template <typename index_at, typename dataset_at> //
498
493
  void run_punned(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
499
494
 
500
- scalar_kind_t accuracy = args.accuracy();
501
- std::printf("-- Accuracy: %s\n", scalar_kind_name(accuracy));
495
+ scalar_kind_t quantization = args.quantization();
496
+ std::printf("-- Quantization: %s\n", scalar_kind_name(quantization));
502
497
 
503
498
  metric_kind_t kind = args.metric();
504
499
  std::printf("-- Metric: %s\n", metric_kind_name(kind));
505
500
 
506
- index_at index = index_at::make(dataset.dimensions(), kind, config, accuracy);
501
+ metric_punned_t metric(dataset.dimensions(), kind, quantization);
502
+ index_at index = index_at::make(metric, config);
507
503
  index.reserve(limits);
508
- std::printf("-- Hardware acceleration: %s\n", isa_name(index.metric().isa_));
504
+ std::printf("-- Hardware acceleration: %s\n", isa_name(index.metric().isa_kind()));
509
505
  std::printf("Will benchmark in-memory\n");
510
506
 
511
507
  single_shot(dataset, index, true);
@@ -535,74 +531,6 @@ void run_typed(dataset_at& dataset, args_t const& args, index_config_t config, i
535
531
  single_shot(dataset, index_view, false);
536
532
  }
537
533
 
538
- template <typename neighbor_id_at, typename dataset_at> //
539
- void run_big_or_small(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
540
- if (args.native) {
541
- if (args.metric_cos) {
542
- std::printf("-- Metric: Angular\n");
543
- run_typed<index_gt<cos_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
544
- } else if (args.metric_l2) {
545
- std::printf("-- Metric: Euclidean\n");
546
- run_typed<index_gt<l2sq_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
547
- } else if (args.metric_haversine) {
548
- std::printf("-- Metric: Haversine\n");
549
- run_typed<index_gt<haversine_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
550
- } else {
551
- std::printf("-- Metric: Inner Product\n");
552
- run_typed<index_gt<ip_gt<float>, label_t, neighbor_id_at>>(dataset, args, config, limits);
553
- }
554
- } else
555
- run_punned<index_punned_dense_gt<label_t, neighbor_id_at>>(dataset, args, config, limits);
556
- }
557
-
558
- void report_alternative_setups() {
559
- using set_member_t = std::uint32_t;
560
- using sets_index_t = index_gt<jaccard_gt<set_member_t>, label_t, std::uint32_t>;
561
- set_member_t set_a[] = {10, 12, 15};
562
- set_member_t set_b[] = {11, 12, 15, 16};
563
- sets_index_t index;
564
- index.reserve(100);
565
- index.add(10, span_gt<set_member_t const>{&set_a[0], 3});
566
- index.add(11, span_gt<set_member_t const>{&set_b[0], 4});
567
- exit(0);
568
- }
569
-
570
- void report_expected_losses(persisted_dataset_gt<float, vector_id_t> const& dataset) {
571
-
572
- auto vec1 = dataset.vector(0);
573
- auto vec2 = dataset.query(0);
574
- std::vector<f16_t> vec1f16(dataset.dimensions());
575
- std::vector<f16_t> vec2f16(dataset.dimensions());
576
- std::transform(vec1, vec1 + dataset.dimensions(), vec1f16.data(), [](float v) { return v; });
577
- std::transform(vec2, vec2 + dataset.dimensions(), vec2f16.data(), [](float v) { return v; });
578
- std::vector<f8_bits_t> vec1f8(dataset.dimensions());
579
- std::vector<f8_bits_t> vec2f8(dataset.dimensions());
580
- std::transform(vec1, vec1 + dataset.dimensions(), vec1f8.data(), [](float v) { return v; });
581
- std::transform(vec2, vec2 + dataset.dimensions(), vec2f8.data(), [](float v) { return v; });
582
-
583
- #if 0
584
- auto ip_default = ip_gt<float>{}(vec1, vec2, dataset.dimensions());
585
- auto ip_f16 = ip_gt<f16_t, float>{}(vec1f16.data(), vec2f16.data(), dataset.dimensions());
586
- auto ip_f8 = ip_gt<f8_bits_t, float>{}(vec1f8.data(), vec2f8.data(), dataset.dimensions());
587
-
588
- #if defined(__AVX512F__)
589
- auto ip_f16avx512 = 1.f - simsimd_dot_f16x16avx512((simsimd_f16_t const*)vec1f16.data(),
590
- (simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
591
- #endif
592
- #if defined(__ARM_FEATURE_SVE)
593
- auto ip_sve = 1.f - simsimd_dot_f32sve(vec1, vec2, dataset.dimensions());
594
- auto ip_f16sve = 1.f - simsimd_dot_f16sve((simsimd_f16_t const*)vec1f16.data(),
595
- (simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
596
- #endif
597
- #if defined(__ARM_NEON)
598
- auto ip_neon = 1.f - simsimd_dot_f32x4neon(vec1, vec2, dataset.dimensions());
599
- auto ip_f16neon = 1.f - simsimd_dot_f16x8neon((simsimd_f16_t const*)vec1f16.data(),
600
- (simsimd_f16_t const*)vec2f16.data(), dataset.dimensions());
601
- #endif
602
- exit(0);
603
- #endif
604
- }
605
-
606
534
  int main(int argc, char** argv) {
607
535
 
608
536
  // Print backtrace if something goes wrong.
@@ -624,9 +552,8 @@ int main(int argc, char** argv) {
624
552
  (option("--rows-skip") & value("integer", args.vectors_to_skip)).doc("Number of vectors to skip"),
625
553
  (option("--rows-take") & value("integer", args.vectors_to_take)).doc("Number of vectors to take"),
626
554
  ( //
627
- option("--native").set(args.native).doc("Use raw templates instead of type-punned classes") |
628
555
  option("-f16", "--f16quant").set(args.quantize_f16).doc("Enable `f16_t` quantization") |
629
- option("-f8", "--f8quant").set(args.quantize_f8).doc("Enable `f8_t` quantization") |
556
+ option("-i8", "--i8quant").set(args.quantize_i8).doc("Enable `i8_t` quantization") |
630
557
  option("-b1", "--b1quant").set(args.quantize_b1).doc("Enable `b1x8_t` quantization")),
631
558
  ( //
632
559
  option("--ip").set(args.metric_ip).doc("Choose Inner Product metric") |
@@ -648,7 +575,7 @@ int main(int argc, char** argv) {
648
575
  }
649
576
 
650
577
  #if USEARCH_USE_OPENMP
651
- // Instead of relying on `multithreaded` from "index_punned_dense.hpp" we will use OpenMP
578
+ // Instead of relying on `multithreaded` from "index_dense.hpp" we will use OpenMP
652
579
  // to better estimate statistics between tasks batches, without having to recreate
653
580
  // the threads.
654
581
  omp_set_dynamic(true);
@@ -661,7 +588,7 @@ int main(int argc, char** argv) {
661
588
  std::printf("-- Query vectors path: %s\n", args.path_queries.c_str());
662
589
  std::printf("-- Ground truth neighbors path: %s\n", args.path_neighbors.c_str());
663
590
 
664
- persisted_dataset_gt<float, vector_id_t> dataset{
591
+ persisted_dataset_gt<float, compressed_slot_t> dataset{
665
592
  args.path_vectors.c_str(), //
666
593
  args.path_queries.c_str(), //
667
594
  args.path_neighbors.c_str(), //
@@ -676,27 +603,24 @@ int main(int argc, char** argv) {
676
603
  // report_alternative_setups();
677
604
  // report_expected_losses(dataset);
678
605
 
679
- index_config_t config;
680
- config.connectivity = args.connectivity;
681
- // config.expansion_add = args.expansion_add;
682
- // config.expansion_search = args.expansion_search;
606
+ index_dense_config_t config(args.connectivity, args.expansion_add, args.expansion_search);
683
607
  index_limits_t limits;
684
608
  limits.threads_add = limits.threads_search = args.threads;
685
609
  limits.members = dataset.vectors_count();
686
610
 
687
611
  std::printf("- Index: \n");
688
612
  std::printf("-- Connectivity: %zu\n", config.connectivity);
689
- // std::printf("-- Expansion @ Add: %zu\n", config.expansion_add);
690
- // std::printf("-- Expansion @ Search: %zu\n", config.expansion_search);
613
+ std::printf("-- Expansion @ Add: %zu\n", config.expansion_add);
614
+ std::printf("-- Expansion @ Search: %zu\n", config.expansion_search);
691
615
 
692
616
  if (args.big)
693
617
  #ifdef USEARCH_64BIT_ENV
694
- run_big_or_small<uint40_t>(dataset, args, config, limits);
618
+ run_punned<index_dense_gt<default_key_t, uint40_t>>(dataset, args, config, limits);
695
619
  #else
696
620
  std::printf("Error: Don't use 40 bit identifiers in 32bit environment\n");
697
621
  #endif
698
622
  else
699
- run_big_or_small<std::uint32_t>(dataset, args, config, limits);
623
+ run_punned<index_dense_gt<default_key_t, std::uint32_t>>(dataset, args, config, limits);
700
624
 
701
625
  return 0;
702
626
  }