usearch 0.4.0 → 0.8.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 (44) hide show
  1. package/.clang-tidy +60 -0
  2. package/.github/workflows/release.yml +42 -16
  3. package/.github/workflows/update_version.sh +1 -0
  4. package/.vscode/launch.json +22 -0
  5. package/.vscode/settings.json +5 -0
  6. package/.vscode/tasks.json +4 -4
  7. package/CITATION.cff +11 -0
  8. package/CMakeLists.txt +108 -95
  9. package/Cargo.toml +1 -1
  10. package/Dockerfile +14 -0
  11. package/README.md +8 -7
  12. package/VERSION +1 -1
  13. package/binding.gyp +2 -2
  14. package/build.rs +2 -2
  15. package/cmake/config.cmake.in +15 -0
  16. package/cmake/pkg-config.pc.in +4 -0
  17. package/cmake/usearchConfigVersion.cmake.in +20 -0
  18. package/conanfile.py +1 -1
  19. package/docs/benchmarks.md +11 -2
  20. package/docs/{Compilation.md → compilation.md} +54 -27
  21. package/include/usearch/usearch.hpp +664 -390
  22. package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +17 -16
  23. package/javascript/lib.cpp +13 -13
  24. package/javascript/test.js +1 -1
  25. package/objc/USearchObjective.mm +46 -29
  26. package/objc/include/USearchObjective.h +18 -26
  27. package/package.json +1 -1
  28. package/pyproject.toml +3 -2
  29. package/python/lib.cpp +179 -120
  30. package/python/{bench.py → scripts/bench.py} +6 -1
  31. package/python/scripts/bench_params.py +112 -0
  32. package/python/{test.py → scripts/test.py} +32 -13
  33. package/python/usearch/client.py +100 -14
  34. package/python/usearch/server.py +104 -23
  35. package/rust/lib.cpp +15 -14
  36. package/rust/lib.hpp +5 -5
  37. package/rust/lib.rs +3 -3
  38. package/setup.py +7 -0
  39. package/src/CMakeLists.txt +107 -0
  40. package/src/bench.cpp +45 -15
  41. package/src/{advanced.hpp → punned.hpp} +227 -199
  42. package/src/test.cpp +62 -0
  43. package/swift/Test.swift +1 -1
  44. package/wolfram/lib.cpp +14 -14
package/src/bench.cpp CHANGED
@@ -29,6 +29,7 @@
29
29
  #include <iostream> // `std::cerr`
30
30
  #include <numeric> // `std::iota`
31
31
  #include <stdexcept> // `std::invalid_argument`
32
+ #include <string> // `std::to_string`
32
33
  #include <thread> // `std::thread::hardware_concurrency()`
33
34
  #include <variant> // `std::monostate`
34
35
 
@@ -37,7 +38,7 @@
37
38
 
38
39
  #include <simsimd/simsimd.h>
39
40
 
40
- #include "advanced.hpp"
41
+ #include "punned.hpp"
41
42
 
42
43
  using namespace unum::usearch;
43
44
  using namespace unum;
@@ -227,7 +228,7 @@ struct running_stats_printer_t {
227
228
  running_stats_printer_t(std::size_t n, char const* msg) {
228
229
  std::printf("%s. %zu items\n", msg, n);
229
230
  total = n;
230
- start_time = std::chrono::high_resolution_clock::now();
231
+ last_printed_time = start_time = std::chrono::high_resolution_clock::now();
231
232
  }
232
233
 
233
234
  ~running_stats_printer_t() {
@@ -279,7 +280,8 @@ void index_many(index_at& native, std::size_t n, vector_id_at const* ids, real_a
279
280
  add_config_t config;
280
281
  config.thread = omp_get_thread_num();
281
282
  config.store_vector = true;
282
- native.add(ids[i], vector_view_t{vectors + dims * i, dims}, config);
283
+ vector_view_t vector{vectors + dims * i, dims};
284
+ native.add(ids[i], vector, config);
283
285
  printer.progress++;
284
286
  if (omp_get_thread_num() == 0)
285
287
  printer.refresh();
@@ -291,16 +293,35 @@ void search_many( //
291
293
  index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted, vector_id_at* ids,
292
294
  real_at* distances) {
293
295
 
294
- running_stats_printer_t printer{n, "Search"};
296
+ std::string name = "Search " + std::to_string(wanted);
297
+ running_stats_printer_t printer{n, name.c_str()};
295
298
 
296
299
  #pragma omp parallel for schedule(static, 32)
297
300
  for (std::size_t i = 0; i < n; ++i) {
298
301
  search_config_t config;
299
302
  config.thread = omp_get_thread_num();
300
- native.search( //
301
- vector_view_t{vectors + dims * i, dims}, wanted, //
302
- ids + wanted * i, distances + wanted * i, //
303
- config);
303
+ vector_view_t vector{vectors + dims * i, dims};
304
+ native.search(vector, wanted, config).dump_to(ids + wanted * i, distances + wanted * i);
305
+ printer.progress++;
306
+ if (omp_get_thread_num() == 0)
307
+ printer.refresh();
308
+ }
309
+ }
310
+
311
+ template <typename index_at, typename vector_id_at, typename real_at>
312
+ void paginate_many( //
313
+ index_at& native, std::size_t n, real_at const* vectors, std::size_t dims, std::size_t wanted,
314
+ vector_id_at const* hints) {
315
+
316
+ std::string name = "Paginate " + std::to_string(wanted);
317
+ running_stats_printer_t printer{n, name.c_str()};
318
+
319
+ #pragma omp parallel for schedule(static, 32)
320
+ for (std::size_t i = 0; i < n; ++i) {
321
+ search_config_t config;
322
+ config.thread = omp_get_thread_num();
323
+ vector_view_t vector{vectors + dims * i, dims};
324
+ native.search_around(hints[i], vector, wanted, config);
304
325
  printer.progress++;
305
326
  if (omp_get_thread_num() == 0)
306
327
  printer.refresh();
@@ -338,6 +359,15 @@ static void single_shot(dataset_at& dataset, index_at& index, bool construct = t
338
359
 
339
360
  std::printf("Recall@1 %.2f %%\n", recall_at_1 * 100.f / dataset.queries_count());
340
361
  std::printf("Recall %.2f %%\n", recall_full * 100.f / dataset.queries_count());
362
+
363
+ // Paginate
364
+ std::vector<vector_id_t> hints(dataset.queries_count());
365
+ for (std::size_t i = 0; i != hints.size(); ++i)
366
+ hints[i] = dataset.neighborhood(i)[0];
367
+ paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 10, hints.data());
368
+ paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 100, hints.data());
369
+ paginate_many(index, dataset.queries_count(), dataset.query(0), dataset.dimensions(), 1000, hints.data());
370
+
341
371
  std::printf("------------\n");
342
372
  std::printf("\n");
343
373
  }
@@ -390,9 +420,9 @@ struct args_t {
390
420
  std::string path_neighbors;
391
421
  std::string path_output = "last.usearch";
392
422
 
393
- std::size_t connectivity = config_t::connectivity_default_k;
394
- std::size_t expansion_add = config_t::expansion_add_default_k;
395
- std::size_t expansion_search = config_t::expansion_search_default_k;
423
+ std::size_t connectivity = default_connectivity();
424
+ std::size_t expansion_add = default_expansion_add();
425
+ std::size_t expansion_search = default_expansion_search();
396
426
  std::size_t threads = std::thread::hardware_concurrency();
397
427
 
398
428
  std::size_t vectors_to_skip = 0;
@@ -415,7 +445,7 @@ template <typename index_at, typename dataset_at> //
415
445
  index_at type_punned_index_for_metric(dataset_at& dataset, args_t const& args, config_t config, accuracy_t accuracy) {
416
446
  if (args.metric_l2) {
417
447
  std::printf("-- Metric: Euclidean\n");
418
- return index_at::l2(dataset.dimensions(), accuracy, config);
448
+ return index_at::l2sq(dataset.dimensions(), accuracy, config);
419
449
  } else if (args.metric_cos) {
420
450
  std::printf("-- Metric: Angular\n");
421
451
  return index_at::cos(dataset.dimensions(), accuracy, config);
@@ -486,7 +516,7 @@ void run_big_or_small(dataset_at& dataset, args_t const& args, config_t config)
486
516
  run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
487
517
  }
488
518
  } else
489
- run_type_punned<auto_index_gt<std::size_t, neighbor_id_at>>(dataset, args, config);
519
+ run_type_punned<punned_gt<std::size_t, neighbor_id_at>>(dataset, args, config);
490
520
  }
491
521
 
492
522
  void report_alternative_setups() {
@@ -563,7 +593,7 @@ int main(int argc, char** argv) {
563
593
  option("--f8quant").set(args.quantize_f8).doc("Enable `f8_t` quantization")),
564
594
  ( //
565
595
  option("--ip").set(args.metric_ip).doc("Choose Inner Product metric") |
566
- option("--l2").set(args.metric_l2).doc("Choose L2 Euclidean metric") |
596
+ option("--l2sq").set(args.metric_l2).doc("Choose L2 Euclidean metric") |
567
597
  option("--cos").set(args.metric_cos).doc("Choose Angular metric") |
568
598
  option("--haversine").set(args.metric_haversine).doc("Choose Haversine metric")),
569
599
  option("-h", "--help").set(args.help).doc("Print this help information on this tool and exit"));
@@ -577,7 +607,7 @@ int main(int argc, char** argv) {
577
607
  exit(0);
578
608
  }
579
609
 
580
- // Instead of relying on `multithreaded` from "advanced.hpp" we will use OpenMP
610
+ // Instead of relying on `multithreaded` from "punned.hpp" we will use OpenMP
581
611
  // to better estimate statistics between tasks batches, without having to recreate
582
612
  // the threads.
583
613
  omp_set_dynamic(true);