usearch 0.11.1 → 0.12.1

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.
package/src/bench.cpp CHANGED
@@ -252,8 +252,8 @@ struct running_stats_printer_t {
252
252
  constexpr std::size_t bars_len_k = 60;
253
253
 
254
254
  float percentage = progress * 1.f / total;
255
- int lpad = (int)(percentage * bars_len_k);
256
- int rpad = bars_len_k - lpad;
255
+ int left_pad = (int)(percentage * bars_len_k);
256
+ int right_pad = bars_len_k - left_pad;
257
257
 
258
258
  std::size_t count_new = progress - last_printed_progress;
259
259
  timestamp_t time_new = std::chrono::high_resolution_clock::now();
@@ -261,8 +261,8 @@ struct running_stats_printer_t {
261
261
  std::chrono::duration_cast<std::chrono::nanoseconds>(time_new - last_printed_time).count();
262
262
  float vectors_per_second = count_new * 1e9 / duration;
263
263
 
264
- std::printf("\r%3.3f%% [%.*s%*s] %.0f vectors/s, finished %zu/%zu", percentage * 100.f, lpad, bars_k, rpad, "",
265
- vectors_per_second, progress, total);
264
+ std::printf("\r%3.3f%% [%.*s%*s] %.0f vectors/s, finished %zu/%zu", percentage * 100.f, left_pad, bars_k,
265
+ right_pad, "", vectors_per_second, progress, total);
266
266
  std::fflush(stdout);
267
267
 
268
268
  last_printed_progress = progress;
@@ -442,7 +442,7 @@ struct args_t {
442
442
  };
443
443
 
444
444
  template <typename index_at, typename dataset_at> //
445
- index_at type_punned_index_for_metric(dataset_at& dataset, args_t const& args, config_t config, accuracy_t accuracy) {
445
+ index_at punned_index_for_metric(dataset_at& dataset, args_t const& args, index_config_t config, accuracy_t accuracy) {
446
446
  if (args.metric_l2) {
447
447
  std::printf("-- Metric: Euclidean\n");
448
448
  return index_at::l2sq(dataset.dimensions(), accuracy, config);
@@ -459,7 +459,7 @@ index_at type_punned_index_for_metric(dataset_at& dataset, args_t const& args, c
459
459
  }
460
460
 
461
461
  template <typename index_at, typename dataset_at> //
462
- void run_type_punned(dataset_at& dataset, args_t const& args, config_t config) {
462
+ void run_punned(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
463
463
 
464
464
  accuracy_t accuracy = accuracy_t::f32_k;
465
465
  if (args.quantize_f16)
@@ -469,7 +469,8 @@ void run_type_punned(dataset_at& dataset, args_t const& args, config_t config) {
469
469
 
470
470
  std::printf("-- Accuracy: %s\n", accuracy_name(accuracy));
471
471
 
472
- index_at index{type_punned_index_for_metric<index_at>(dataset, args, config, accuracy)};
472
+ index_at index{punned_index_for_metric<index_at>(dataset, args, config, accuracy)};
473
+ index.reserve(limits);
473
474
  std::printf("-- Hardware acceleration: %s\n", isa_name(index.acceleration()));
474
475
  std::printf("Will benchmark in-memory\n");
475
476
 
@@ -484,9 +485,10 @@ void run_type_punned(dataset_at& dataset, args_t const& args, config_t config) {
484
485
  }
485
486
 
486
487
  template <typename index_at, typename dataset_at> //
487
- void run_typed(dataset_at& dataset, args_t const& args, config_t config) {
488
+ void run_typed(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
488
489
 
489
490
  index_at index(config);
491
+ index.reserve(limits);
490
492
  std::printf("Will benchmark in-memory\n");
491
493
 
492
494
  single_shot(dataset, index, true);
@@ -500,23 +502,23 @@ void run_typed(dataset_at& dataset, args_t const& args, config_t config) {
500
502
  }
501
503
 
502
504
  template <typename neighbor_id_at, typename dataset_at> //
503
- void run_big_or_small(dataset_at& dataset, args_t const& args, config_t config) {
505
+ void run_big_or_small(dataset_at& dataset, args_t const& args, index_config_t config, index_limits_t limits) {
504
506
  if (args.native) {
505
507
  if (args.metric_cos) {
506
508
  std::printf("-- Metric: Angular\n");
507
- run_typed<index_gt<cos_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
509
+ run_typed<index_gt<cos_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
508
510
  } else if (args.metric_l2) {
509
511
  std::printf("-- Metric: Euclidean\n");
510
- run_typed<index_gt<l2sq_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
512
+ run_typed<index_gt<l2sq_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
511
513
  } else if (args.metric_haversine) {
512
514
  std::printf("-- Metric: Haversine\n");
513
- run_typed<index_gt<haversine_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
515
+ run_typed<index_gt<haversine_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
514
516
  } else {
515
517
  std::printf("-- Metric: Inner Product\n");
516
- run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
518
+ run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config, limits);
517
519
  }
518
520
  } else
519
- run_type_punned<punned_gt<std::size_t, neighbor_id_at>>(dataset, args, config);
521
+ run_punned<punned_gt<std::size_t, neighbor_id_at>>(dataset, args, config, limits);
520
522
  }
521
523
 
522
524
  void report_alternative_setups() {
@@ -634,12 +636,13 @@ int main(int argc, char** argv) {
634
636
  // report_alternative_setups();
635
637
  // report_expected_losses(dataset);
636
638
 
637
- config_t config;
639
+ index_config_t config;
638
640
  config.connectivity = args.connectivity;
639
641
  config.expansion_add = args.expansion_add;
640
642
  config.expansion_search = args.expansion_search;
641
- config.max_threads_add = config.max_threads_search = args.threads;
642
- config.max_elements = dataset.vectors_count();
643
+ index_limits_t limits;
644
+ limits.threads_add = limits.threads_search = args.threads;
645
+ limits.elements = dataset.vectors_count();
643
646
 
644
647
  std::printf("- Index: \n");
645
648
  std::printf("-- Connectivity: %zu\n", config.connectivity);
@@ -647,9 +650,9 @@ int main(int argc, char** argv) {
647
650
  std::printf("-- Expansion @ Search: %zu\n", config.expansion_search);
648
651
 
649
652
  if (args.big)
650
- run_big_or_small<uint40_t>(dataset, args, config);
653
+ run_big_or_small<uint40_t>(dataset, args, config, limits);
651
654
  else
652
- run_big_or_small<std::uint32_t>(dataset, args, config);
655
+ run_big_or_small<std::uint32_t>(dataset, args, config, limits);
653
656
 
654
657
  return 0;
655
658
  }
package/src/punned.hpp CHANGED
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
  #include <stdlib.h> // `aligned_alloc`
3
3
 
4
+ #include <cstring> // `std::strncmp`
4
5
  #include <functional> // `std::function`
5
6
  #include <numeric> // `std::iota`
6
7
  #include <shared_mutex> // `std::shared_mutex`
@@ -37,7 +38,7 @@ using punned_distance_t = float;
37
38
  using punned_metric_t = punned_distance_t (*)(byte_t const*, byte_t const*, std::size_t, std::size_t);
38
39
 
39
40
  template <typename metric_at>
40
- punned_distance_t punned_metric(byte_t const* a, byte_t const* b, std::size_t a_bytes, std::size_t b_bytes) {
41
+ inline punned_distance_t punned_metric(byte_t const* a, byte_t const* b, std::size_t a_bytes, std::size_t b_bytes) {
41
42
  using scalar_t = typename metric_at::scalar_t;
42
43
  return metric_at{}((scalar_t const*)a, (scalar_t const*)b, a_bytes / sizeof(scalar_t), b_bytes / sizeof(scalar_t));
43
44
  }
@@ -168,7 +169,7 @@ class f8_bits_t {
168
169
  inline f16_bits_t::f16_bits_t(f8_bits_t v) : f16_bits_t(float(v)) {}
169
170
 
170
171
  struct cos_f8_t {
171
- punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const {
172
+ inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const {
172
173
  std::int32_t ab{}, a2{}, b2{};
173
174
  for (std::size_t i = 0; i != n; i++) {
174
175
  std::int16_t ai{a[i]};
@@ -182,7 +183,7 @@ struct cos_f8_t {
182
183
  };
183
184
 
184
185
  struct l2sq_f8_t {
185
- punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const {
186
+ inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const {
186
187
  std::int32_t ab_deltas_sq{};
187
188
  for (std::size_t i = 0; i != n; i++)
188
189
  ab_deltas_sq += square(std::int16_t(a[i]) - std::int16_t(b[i]));
@@ -268,48 +269,77 @@ inline char const* accuracy_name(accuracy_t accuracy) {
268
269
  }
269
270
 
270
271
  inline bool str_equals(char const* begin, std::size_t len, char const* other_begin) {
271
- std::size_t other_len = strlen(other_begin);
272
- return len == other_len && strncmp(begin, other_begin, len) == 0;
272
+ std::size_t other_len = std::strlen(other_begin);
273
+ return len == other_len && std::strncmp(begin, other_begin, len) == 0;
273
274
  }
274
275
 
275
- inline accuracy_t accuracy_from_name(char const* name, std::size_t len) {
276
- accuracy_t accuracy;
276
+ inline expected_gt<accuracy_t> accuracy_from_name(char const* name, std::size_t len) {
277
+ expected_gt<accuracy_t> parsed;
277
278
  if (str_equals(name, len, "f32"))
278
- accuracy = accuracy_t::f32_k;
279
+ parsed.result = accuracy_t::f32_k;
279
280
  else if (str_equals(name, len, "f64"))
280
- accuracy = accuracy_t::f64_k;
281
+ parsed.result = accuracy_t::f64_k;
281
282
  else if (str_equals(name, len, "f16"))
282
- accuracy = accuracy_t::f16_k;
283
+ parsed.result = accuracy_t::f16_k;
283
284
  else if (str_equals(name, len, "f8"))
284
- accuracy = accuracy_t::f8_k;
285
+ parsed.result = accuracy_t::f8_k;
285
286
  else
286
- throw std::invalid_argument("Unknown type, choose: f32, f16, f64, f8");
287
- return accuracy;
287
+ parsed.failed("Unknown type, choose: f32, f16, f64, f8");
288
+ return parsed;
289
+ }
290
+
291
+ inline expected_gt<common_metric_kind_t> common_metric_from_name(char const* name, std::size_t len) {
292
+ expected_gt<common_metric_kind_t> parsed;
293
+ if (str_equals(name, len, "l2sq") || str_equals(name, len, "euclidean_sq")) {
294
+ parsed.result = common_metric_kind_t::l2sq_k;
295
+ } else if (str_equals(name, len, "ip") || str_equals(name, len, "inner") || str_equals(name, len, "dot")) {
296
+ parsed.result = common_metric_kind_t::ip_k;
297
+ } else if (str_equals(name, len, "cos") || str_equals(name, len, "angular")) {
298
+ parsed.result = common_metric_kind_t::cos_k;
299
+ } else if (str_equals(name, len, "haversine")) {
300
+ parsed.result = common_metric_kind_t::haversine_k;
301
+ } else if (str_equals(name, len, "pearson")) {
302
+ parsed.result = common_metric_kind_t::pearson_k;
303
+ } else if (str_equals(name, len, "hamming")) {
304
+ parsed.result = common_metric_kind_t::bitwise_hamming_k;
305
+ } else if (str_equals(name, len, "tanimoto")) {
306
+ parsed.result = common_metric_kind_t::bitwise_tanimoto_k;
307
+ } else if (str_equals(name, len, "sorensen")) {
308
+ parsed.result = common_metric_kind_t::bitwise_sorensen_k;
309
+ } else
310
+ parsed.failed(
311
+ "Unknown distance, choose: l2sq, ip, cos, haversine, jaccard, pearson, hamming, tanimoto, sorensen");
312
+ return parsed;
288
313
  }
289
314
 
290
315
  template <typename index_at>
291
- inline index_at index_from_name( //
292
- char const* name, std::size_t len, std::size_t dimensions, accuracy_t accuracy, config_t const& config) {
316
+ expected_gt<index_at> make_punned( //
317
+ common_metric_kind_t metric, std::size_t dimensions, accuracy_t accuracy, index_config_t const& config) {
293
318
 
294
- if (str_equals(name, len, "l2sq") || str_equals(name, len, "euclidean_sq")) {
319
+ expected_gt<index_at> parsed;
320
+ if (metric == common_metric_kind_t::l2sq_k) {
295
321
  if (dimensions == 0)
296
- throw std::invalid_argument("The number of dimensions must be positive");
297
- return index_at::l2sq(dimensions, accuracy, config);
298
- } else if (str_equals(name, len, "ip") || str_equals(name, len, "inner") || str_equals(name, len, "dot")) {
322
+ parsed.failed("The number of dimensions must be positive");
323
+ else
324
+ parsed.result = index_at::l2sq(dimensions, accuracy, config);
325
+ } else if (metric == common_metric_kind_t::ip_k) {
299
326
  if (dimensions == 0)
300
- throw std::invalid_argument("The number of dimensions must be positive");
301
- return index_at::ip(dimensions, accuracy, config);
302
- } else if (str_equals(name, len, "cos") || str_equals(name, len, "angular")) {
327
+ parsed.failed("The number of dimensions must be positive");
328
+ else
329
+ parsed.result = index_at::ip(dimensions, accuracy, config);
330
+ } else if (metric == common_metric_kind_t::cos_k) {
303
331
  if (dimensions == 0)
304
- throw std::invalid_argument("The number of dimensions must be positive");
305
- return index_at::cos(dimensions, accuracy, config);
306
- } else if (str_equals(name, len, "haversine")) {
332
+ parsed.failed("The number of dimensions must be positive");
333
+ else
334
+ parsed.result = index_at::cos(dimensions, accuracy, config);
335
+ } else if (metric == common_metric_kind_t::haversine_k) {
307
336
  if (dimensions != 2 && dimensions != 0)
308
- throw std::invalid_argument("The number of dimensions must be equal to two");
309
- return index_at::haversine(accuracy, config);
337
+ parsed.failed("The number of dimensions must be equal to two");
338
+ else
339
+ parsed.result = index_at::haversine(accuracy, config);
310
340
  } else
311
- throw std::invalid_argument("Unknown distance, choose: l2sq, ip, cos, hamming, jaccard");
312
- return {};
341
+ parsed.failed("Type-punned index only supports: l2sq, ip, cos, haversine");
342
+ return parsed;
313
343
  }
314
344
 
315
345
  enum class isa_t {
@@ -353,7 +383,7 @@ inline bool supports_arm_sve() {
353
383
  }
354
384
 
355
385
  template <typename from_scalar_at, typename to_scalar_at> struct cast_gt {
356
- bool operator()(byte_t const* input, std::size_t bytes_in_input, byte_t* output) const {
386
+ inline bool operator()(byte_t const* input, std::size_t bytes_in_input, byte_t* output) const {
357
387
  from_scalar_at const* typed_input = reinterpret_cast<from_scalar_at const*>(input);
358
388
  to_scalar_at* typed_output = reinterpret_cast<to_scalar_at*>(output);
359
389
  std::transform( //
@@ -437,8 +467,9 @@ class punned_gt {
437
467
  tsl::robin_map<label_t, id_t> lookup_table_;
438
468
 
439
469
  public:
440
- using search_results_t = typename index_t::search_results_t;
470
+ using search_result_t = typename index_t::search_result_t;
441
471
  using add_result_t = typename index_t::add_result_t;
472
+ using serialization_result_t = typename index_t::serialization_result_t;
442
473
  using stats_t = typename index_t::stats_t;
443
474
 
444
475
  punned_gt() = default;
@@ -462,36 +493,37 @@ class punned_gt {
462
493
  std::swap(available_threads_, other.available_threads_);
463
494
  }
464
495
 
465
- static config_t optimize(config_t config) noexcept { return index_t::optimize(config); }
496
+ static index_config_t optimize(index_config_t config) { return index_t::optimize(config); }
466
497
 
467
498
  std::size_t dimensions() const { return dimensions_; }
468
499
  std::size_t connectivity() const { return typed_->connectivity(); }
469
500
  std::size_t size() const { return typed_->size(); }
470
501
  std::size_t capacity() const { return typed_->capacity(); }
471
- config_t const& config() const { return typed_->config(); }
502
+ index_config_t const& config() const { return typed_->config(); }
503
+ index_limits_t const& limits() const { return typed_->limits(); }
472
504
  void clear() { return typed_->clear(); }
473
- void change_expansion_add(std::size_t n) noexcept { typed_->change_expansion_add(n); }
474
- void change_expansion_search(std::size_t n) noexcept { typed_->change_expansion_search(n); }
505
+ void change_expansion_add(std::size_t n) { typed_->change_expansion_add(n); }
506
+ void change_expansion_search(std::size_t n) { typed_->change_expansion_search(n); }
475
507
 
476
- member_citerator_t cbegin() const noexcept { return typed_->cbegin(); }
477
- member_citerator_t cend() const noexcept { return typed_->cend(); }
478
- member_citerator_t begin() const noexcept { return typed_->begin(); }
479
- member_citerator_t end() const noexcept { return typed_->end(); }
480
- member_iterator_t begin() noexcept { return typed_->begin(); }
481
- member_iterator_t end() noexcept { return typed_->end(); }
508
+ member_citerator_t cbegin() const { return typed_->cbegin(); }
509
+ member_citerator_t cend() const { return typed_->cend(); }
510
+ member_citerator_t begin() const { return typed_->begin(); }
511
+ member_citerator_t end() const { return typed_->end(); }
512
+ member_iterator_t begin() { return typed_->begin(); }
513
+ member_iterator_t end() { return typed_->end(); }
482
514
 
483
- stats_t stats() const noexcept { return typed_->stats(); }
484
- stats_t stats(std::size_t level) const noexcept { return typed_->stats(level); }
515
+ stats_t stats() const { return typed_->stats(); }
516
+ stats_t stats(std::size_t level) const { return typed_->stats(level); }
485
517
 
486
518
  accuracy_t accuracy() const { return accuracy_; }
487
519
  isa_t acceleration() const { return acceleration_; }
488
520
 
489
- void save(char const* path) const { typed_->save(path); }
490
- void load(char const* path) { typed_->load(path); }
491
- void view(char const* path) { typed_->view(path); }
492
- void reserve(std::size_t capacity) { typed_->reserve(capacity); }
521
+ serialization_result_t save(char const* path) const { return typed_->save(path); }
522
+ serialization_result_t load(char const* path) { return typed_->load(path); }
523
+ serialization_result_t view(char const* path) { return typed_->view(path); }
524
+ bool reserve(index_limits_t limits) { return typed_->reserve(limits); }
493
525
 
494
- std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const noexcept {
526
+ std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const {
495
527
  return typed_->memory_usage(allocator_entry_bytes);
496
528
  }
497
529
 
@@ -506,40 +538,40 @@ class punned_gt {
506
538
  add_result_t add(label_t label, f32_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f32); }
507
539
  add_result_t add(label_t label, f64_t const* vector, add_config_t config) { return add_(label, vector, config, casts_.from_f64); }
508
540
 
509
- search_results_t search(f8_bits_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f8); }
510
- search_results_t search(f16_bits_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f16); }
511
- search_results_t search(f32_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f32); }
512
- search_results_t search(f64_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f64); }
541
+ search_result_t search(f8_bits_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f8); }
542
+ search_result_t search(f16_bits_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f16); }
543
+ search_result_t search(f32_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f32); }
544
+ search_result_t search(f64_t const* vector, std::size_t wanted) const { return search_(vector, wanted, casts_.from_f64); }
513
545
 
514
- search_results_t search(f8_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f8); }
515
- search_results_t search(f16_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f16); }
516
- search_results_t search(f32_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f32); }
517
- search_results_t search(f64_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f64); }
546
+ 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); }
547
+ search_result_t search(f16_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_(vector, wanted, config, casts_.from_f16); }
548
+ 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); }
549
+ 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); }
518
550
 
519
- search_results_t search_around(label_t hint, f8_bits_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f8); }
520
- search_results_t search_around(label_t hint, f16_bits_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f16); }
521
- search_results_t search_around(label_t hint, f32_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f32); }
522
- search_results_t search_around(label_t hint, f64_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f64); }
551
+ search_result_t search_around(label_t hint, f8_bits_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f8); }
552
+ search_result_t search_around(label_t hint, f16_bits_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f16); }
553
+ search_result_t search_around(label_t hint, f32_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f32); }
554
+ search_result_t search_around(label_t hint, f64_t const* vector, std::size_t wanted) const { return search_around_(hint, vector, wanted, casts_.from_f64); }
523
555
 
524
- search_results_t search_around(label_t hint, f8_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f8); }
525
- search_results_t search_around(label_t hint, f16_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f16); }
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); }
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); }
556
+ search_result_t search_around(label_t hint, f8_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f8); }
557
+ search_result_t search_around(label_t hint, f16_bits_t const* vector, std::size_t wanted, search_config_t config) const { return search_around_(hint, vector, wanted, config, casts_.from_f16); }
558
+ search_result_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); }
559
+ search_result_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); }
528
560
 
529
561
  void reconstruct(label_t label, f8_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f8); }
530
562
  void reconstruct(label_t label, f16_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f16); }
531
563
  void reconstruct(label_t label, f32_t* vector) const { return reconstruct_(label, vector, casts_.to_f32); }
532
564
  void reconstruct(label_t label, f64_t* vector) const { return reconstruct_(label, vector, casts_.to_f64); }
533
565
 
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); }
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); }
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); }
537
- static punned_gt haversine(accuracy_t accuracy = accuracy_t::f32_k, config_t config = {}) { return make_(2, accuracy, haversine_metric_(accuracy), make_casts_(accuracy), config); }
566
+ static punned_gt ip(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, index_config_t config = {}) { return make_(dimensions, accuracy, ip_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
567
+ static punned_gt l2sq(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k, index_config_t config = {}) { return make_(dimensions, accuracy, l2_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
568
+ static punned_gt cos(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f32_k, index_config_t config = {}) { return make_(dimensions, accuracy, cos_metric_(dimensions, accuracy), make_casts_(accuracy), config); }
569
+ static punned_gt haversine(accuracy_t accuracy = accuracy_t::f32_k, index_config_t config = {}) { return make_(2, accuracy, haversine_metric_(accuracy), make_casts_(accuracy), config); }
538
570
  // clang-format on
539
571
 
540
572
  static punned_gt udf( //
541
573
  std::size_t dimensions, punned_stateful_metric_t metric, //
542
- accuracy_t accuracy = accuracy_t::f32_k, config_t config = {}) {
574
+ accuracy_t accuracy = accuracy_t::f32_k, index_config_t config = {}) {
543
575
  return make_(dimensions, accuracy, {metric, isa_t::auto_k}, make_casts_(accuracy), config);
544
576
  }
545
577
 
@@ -618,7 +650,7 @@ class punned_gt {
618
650
  }
619
651
 
620
652
  template <typename scalar_at>
621
- search_results_t search_( //
653
+ search_result_t search_( //
622
654
  scalar_at const* vector, std::size_t wanted, //
623
655
  search_config_t config, cast_t const& cast) const {
624
656
 
@@ -634,7 +666,7 @@ class punned_gt {
634
666
  }
635
667
 
636
668
  template <typename scalar_at>
637
- search_results_t search_around_( //
669
+ search_result_t search_around_( //
638
670
  label_t hint, scalar_at const* vector, std::size_t wanted, //
639
671
  search_config_t config, cast_t const& cast) const {
640
672
 
@@ -672,7 +704,7 @@ class punned_gt {
672
704
  }
673
705
 
674
706
  template <typename scalar_at>
675
- search_results_t search_( //
707
+ search_result_t search_( //
676
708
  scalar_at const* vector, std::size_t wanted, //
677
709
  cast_t const& cast) const {
678
710
  thread_lock_t lock = thread_lock_();
@@ -682,7 +714,7 @@ class punned_gt {
682
714
  }
683
715
 
684
716
  template <typename scalar_at>
685
- search_results_t search_around_( //
717
+ search_result_t search_around_( //
686
718
  label_t hint, scalar_at const* vector, std::size_t wanted, //
687
719
  cast_t const& cast) const {
688
720
  thread_lock_t lock = thread_lock_();
@@ -694,23 +726,20 @@ class punned_gt {
694
726
  static punned_gt make_( //
695
727
  std::size_t dimensions, accuracy_t accuracy, //
696
728
  metric_and_meta_t metric_and_meta, casts_t casts, //
697
- config_t config) {
729
+ index_config_t config) {
698
730
 
699
731
  std::size_t hardware_threads = std::thread::hardware_concurrency();
700
- config.max_threads_add = config.max_threads_add ? config.max_threads_add : hardware_threads;
701
- config.max_threads_search = config.max_threads_search ? config.max_threads_search : hardware_threads;
702
- std::size_t max_threads = (std::max)(config.max_threads_add, config.max_threads_search);
703
732
  punned_gt result;
704
733
  result.dimensions_ = dimensions;
705
734
  result.accuracy_ = accuracy;
706
735
  result.casted_vector_bytes_ = bytes_per_scalar(accuracy) * dimensions;
707
- result.cast_buffer_.resize(max_threads * result.casted_vector_bytes_);
736
+ result.cast_buffer_.resize(hardware_threads * result.casted_vector_bytes_);
708
737
  result.casts_ = casts;
709
738
  result.acceleration_ = metric_and_meta.acceleration;
710
739
  result.root_metric_ = metric_and_meta.metric;
711
740
 
712
741
  // Fill the thread IDs.
713
- result.available_threads_.resize(max_threads);
742
+ result.available_threads_.resize(hardware_threads);
714
743
  std::iota(result.available_threads_.begin(), result.available_threads_.end(), 0ul);
715
744
 
716
745
  // Available since C11, but only C++17, so we use the C version.
package/src/test.cpp CHANGED
@@ -2,7 +2,7 @@
2
2
  * @brief A trivial test.
3
3
  */
4
4
  #include <algorithm>
5
- #include <cassert>
5
+ #include <stdexcept>
6
6
 
7
7
  #include "punned.hpp"
8
8
 
@@ -11,13 +11,20 @@ using namespace unum;
11
11
 
12
12
  using point_id_t = std::int64_t;
13
13
 
14
+ void expect(bool must_be_true) {
15
+ if (!must_be_true)
16
+ throw std::runtime_error("Failed!");
17
+ }
18
+
14
19
  template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
15
- using view_t = span_gt<scalar_at const>;
20
+
21
+ using scalar_t = scalar_at;
22
+ using view_t = span_gt<scalar_t const>;
16
23
  using distance_t = typename index_at::distance_t;
17
24
 
18
- scalar_at vec[3] = {10, 20, 15};
19
- scalar_at vec_a[3] = {15, 16, 17};
20
- scalar_at vec_b[3] = {16, 17, 18};
25
+ scalar_t vec[3] = {10, 20, 15};
26
+ scalar_t vec_a[3] = {15, 16, 17};
27
+ scalar_t vec_b[3] = {16, 17, 18};
21
28
 
22
29
  index.reserve(10);
23
30
  index.add(42, view_t{&vec[0], 3ul});
@@ -27,16 +34,16 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
27
34
  distance_t matched_distances[10] = {0};
28
35
  std::size_t matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
29
36
 
30
- assert(matched_count == 1);
31
- assert(matched_labels[0] == 42);
32
- assert(std::abs(matched_distances[0]) < 0.01);
37
+ expect(matched_count == 1);
38
+ expect(matched_labels[0] == 42);
39
+ expect(std::abs(matched_distances[0]) < 0.01);
33
40
 
34
41
  // Add more entries
35
42
  search_config_t search_config;
36
43
  search_config.exact = true;
37
44
  index.add(43, view_t{&vec_a[0], 3ul});
38
45
  index.add(44, view_t{&vec_b[0], 3ul});
39
- assert(index.size() == 3);
46
+ expect(index.size() == 3);
40
47
 
41
48
  // Perform exact search
42
49
  matched_count = index.search(view_t{&vec[0], 3ul}, 5, search_config).dump_to(matched_labels, matched_distances);
@@ -45,47 +52,84 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
45
52
  std::size_t count = 0;
46
53
  for (auto member : index) {
47
54
  point_id_t id = member.label;
48
- assert(id >= 42 && id <= 44);
55
+ expect(id >= 42 && id <= 44);
49
56
  count++;
50
57
  }
51
- assert((count == 3));
52
- assert((index.stats(0).nodes == 3));
58
+ expect((count == 3));
59
+ expect((index.stats(0).nodes == 3));
53
60
 
54
61
  // Search again over reconstructed index
55
62
  index.save("tmp.usearch");
56
63
  index.load("tmp.usearch");
57
64
  matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
58
- assert(matched_count == 3);
59
- assert(matched_labels[0] == 42);
60
- assert(std::abs(matched_distances[0]) < 0.01);
65
+ expect(matched_count == 3);
66
+ expect(matched_labels[0] == 42);
67
+ expect(std::abs(matched_distances[0]) < 0.01);
61
68
 
62
69
  // Search again over mapped index
63
70
  index.view("tmp.usearch");
64
71
  matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
65
- assert(matched_count == 3);
66
- assert(matched_labels[0] == 42);
67
- assert(std::abs(matched_distances[0]) < 0.01);
72
+ expect(matched_count == 3);
73
+ expect(matched_labels[0] == 42);
74
+ expect(std::abs(matched_distances[0]) < 0.01);
68
75
 
69
- assert(index.memory_usage() > 0);
70
- assert(index.stats().max_edges > 0);
76
+ expect(index.memory_usage() > 0);
77
+ expect(index.stats().max_edges > 0);
71
78
  }
72
79
 
73
80
  template <typename scalar_at, typename index_at> void test3d_punned(index_at&& index) {
74
- using view_t = span_gt<scalar_at const>;
81
+
82
+ using scalar_t = scalar_at;
83
+ using view_t = span_gt<scalar_t const>;
75
84
  using span_t = span_gt<scalar_at>;
76
- using distance_t = typename index_at::distance_t;
77
85
 
78
- scalar_at vec[3] = {10, 20, 15};
86
+ scalar_t vec[3] = {10, 20, 15};
79
87
 
80
88
  index.reserve(10);
81
89
  index.add(42, view_t{&vec[0], 3ul});
82
90
 
83
91
  // Reconstruct
84
- scalar_at vec_reconstructed[3] = {0, 0, 0};
92
+ scalar_t vec_reconstructed[3] = {0, 0, 0};
85
93
  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]);
94
+ expect(vec_reconstructed[0] == vec[0]);
95
+ expect(vec_reconstructed[1] == vec[1]);
96
+ expect(vec_reconstructed[2] == vec[2]);
97
+ }
98
+
99
+ template <typename index_at> void test_sets(index_at&& index) {
100
+
101
+ using index_t = typename std::remove_reference<index_at>::type;
102
+ using scalar_t = typename index_t::scalar_t;
103
+ using view_t = span_gt<scalar_t const>;
104
+
105
+ scalar_t vec0[] = {10, 20};
106
+ scalar_t vec1[] = {10, 15, 20};
107
+ scalar_t vec2[] = {10, 20, 30, 35};
108
+
109
+ index.reserve(10);
110
+ index.add(42, view_t{&vec0[0], 2ul});
111
+ index.add(43, view_t{&vec1[0], 3ul});
112
+ index.add(44, view_t{&vec2[0], 4ul});
113
+
114
+ expect(index.size() == 3);
115
+ }
116
+
117
+ template <typename index_at> void test_sets_moved() {
118
+ {
119
+ index_at index;
120
+ test_sets(index);
121
+ }
122
+ {
123
+ index_at index;
124
+ index.reserve(1);
125
+ test_sets(index_at(std::move(index)));
126
+ }
127
+ {
128
+ index_at index;
129
+ index.reserve(1);
130
+ index_at index_moved = std::move(index);
131
+ test_sets(index_moved);
132
+ }
89
133
  }
90
134
 
91
135
  int main(int, char**) {
@@ -105,5 +149,11 @@ int main(int, char**) {
105
149
  test3d_punned<float>(punned_small_t::cos(3));
106
150
  test3d_punned<float>(punned_small_t::l2sq(3));
107
151
 
152
+ test_sets(index_gt<jaccard_gt<std::int32_t, float>, point_id_t, std::uint32_t, std::int32_t>{});
153
+ test_sets(index_gt<jaccard_gt<std::int64_t, float>, point_id_t, std::uint32_t, std::int64_t>{});
154
+
155
+ test_sets_moved<index_gt<jaccard_gt<std::int32_t, float>, point_id_t, std::uint32_t, std::int32_t>>();
156
+ test_sets_moved<index_gt<jaccard_gt<std::int64_t, float>, point_id_t, std::uint32_t, std::int64_t>>();
157
+
108
158
  return 0;
109
159
  }