usearch 0.18.8 → 0.19.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.
@@ -20,6 +20,7 @@ struct cos_f8_t {
20
20
  std::size_t dimensions;
21
21
 
22
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; }
23
24
  inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
24
25
  std::int32_t ab{}, a2{}, b2{};
25
26
  #if USEARCH_USE_OPENMP
@@ -45,6 +46,8 @@ struct l2sq_f8_t {
45
46
  std::size_t dimensions;
46
47
 
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; }
48
51
  inline punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b) const noexcept {
49
52
  std::int32_t ab_deltas_sq{};
50
53
  #if USEARCH_USE_OPENMP
@@ -60,6 +63,48 @@ struct l2sq_f8_t {
60
63
  }
61
64
  };
62
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
+
63
108
  /**
64
109
  * @brief Oversimplified type-punned index for equidimensional vectors
65
110
  * with automatic down-casting and hardware isa.
@@ -71,25 +116,15 @@ class index_punned_dense_gt {
71
116
  using id_t = id_at;
72
117
  using distance_t = punned_distance_t;
73
118
  /// @brief Punned metric object.
74
- using metric_t = punned_metric_t;
119
+ using metric_t = index_punned_dense_metric_t;
75
120
 
76
121
  private:
77
122
  /// @brief Schema: input buffer, bytes in input buffer, output buffer.
78
123
  using cast_t = std::function<bool(byte_t const*, std::size_t, byte_t*)>;
79
124
  /// @brief Punned index.
80
- using index_t = index_gt<metric_t, label_t, id_t, byte_t, aligned_allocator_t, memory_mapping_allocator_t>;
125
+ using index_t = index_gt<metric_t, label_t, id_t, aligned_allocator_t, memory_mapping_allocator_t>;
81
126
  using index_allocator_t = aligned_allocator_gt<index_t, 64>;
82
127
 
83
- /// @brief A type-punned metric and metadata about present isa support.
84
- struct metric_and_meta_t {
85
- metric_t metric;
86
- isa_t isa;
87
-
88
- metric_and_meta_t(metric_t m = {}, isa_t i = isa_t::auto_k) : metric(m), isa(i) {}
89
- metric_and_meta_t(metric_and_meta_t const&) = default;
90
- metric_and_meta_t& operator=(metric_and_meta_t const&) = default;
91
- };
92
-
93
128
  using member_iterator_t = typename index_t::member_iterator_t;
94
129
  using member_citerator_t = typename index_t::member_citerator_t;
95
130
  using member_ref_t = typename index_t::member_ref_t;
@@ -104,9 +139,6 @@ class index_punned_dense_gt {
104
139
  index_t* typed_ = nullptr;
105
140
 
106
141
  std::size_t casted_vector_bytes_ = 0;
107
- scalar_kind_t scalar_kind_ = scalar_kind_t::f32_k;
108
- isa_t isa_ = isa_t::auto_k;
109
-
110
142
  mutable std::vector<byte_t> cast_buffer_;
111
143
  struct casts_t {
112
144
  cast_t from_b1x8;
@@ -153,8 +185,6 @@ class index_punned_dense_gt {
153
185
  std::swap(dimensions_, other.dimensions_);
154
186
  std::swap(scalar_words_, other.scalar_words_);
155
187
  std::swap(casted_vector_bytes_, other.casted_vector_bytes_);
156
- std::swap(scalar_kind_, other.scalar_kind_);
157
- std::swap(isa_, other.isa_);
158
188
  std::swap(typed_, other.typed_);
159
189
  std::swap(cast_buffer_, other.cast_buffer_);
160
190
  std::swap(casts_, other.casts_);
@@ -173,6 +203,7 @@ class index_punned_dense_gt {
173
203
  index_limits_t const& limits() const { return typed_->limits(); }
174
204
  void clear() { return typed_->clear(); }
175
205
 
206
+ metric_t const& metric() const { return root_metric_; }
176
207
  std::size_t expansion_add() const { return expansion_add_; }
177
208
  std::size_t expansion_search() const { return expansion_search_; }
178
209
  void change_expansion_add(std::size_t n) { expansion_add_ = n; }
@@ -188,13 +219,22 @@ class index_punned_dense_gt {
188
219
  stats_t stats() const { return typed_->stats(); }
189
220
  stats_t stats(std::size_t level) const { return typed_->stats(level); }
190
221
 
191
- scalar_kind_t scalar_kind() const { return scalar_kind_; }
192
- isa_t isa() const { return isa_; }
193
-
194
- serialization_result_t save(char const* path) const { return typed_->save(path); }
195
- serialization_result_t load(char const* path) { return typed_->load(path); }
196
- serialization_result_t view(char const* path) { return typed_->view(path); }
197
222
  bool reserve(index_limits_t limits) { return typed_->reserve(limits); }
223
+ serialization_result_t save(char const* path) const { return typed_->save(path); }
224
+
225
+ serialization_result_t load(char const* path) {
226
+ serialization_result_t result = typed_->load(path);
227
+ if (result)
228
+ reindex_labels();
229
+ return result;
230
+ }
231
+
232
+ serialization_result_t view(char const* path) {
233
+ serialization_result_t result = typed_->view(path);
234
+ if (result)
235
+ reindex_labels();
236
+ return result;
237
+ }
198
238
 
199
239
  std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const {
200
240
  return typed_->memory_usage(allocator_entry_bytes);
@@ -248,6 +288,8 @@ class index_punned_dense_gt {
248
288
  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); }
249
289
  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); }
250
290
 
291
+ search_result_t empty_search_result() const { return search_result_t{*typed_}; }
292
+
251
293
  bool get(label_t label, b1x8_t* vector) const { return get_(label, vector, casts_.to_b1x8); }
252
294
  bool get(label_t label, f8_bits_t* vector) const { return get_(label, vector, casts_.to_f8); }
253
295
  bool get(label_t label, f16_t* vector) const { return get_(label, vector, casts_.to_f16); }
@@ -278,7 +320,7 @@ class index_punned_dense_gt {
278
320
  return make_( //
279
321
  dimensions, accuracy, //
280
322
  config, expansion_add, expansion_search, //
281
- metric_and_meta_t(metric), make_casts_(accuracy));
323
+ metric_t(metric), make_casts_(accuracy));
282
324
  }
283
325
 
284
326
  index_punned_dense_gt fork() const {
@@ -286,8 +328,6 @@ class index_punned_dense_gt {
286
328
 
287
329
  result.dimensions_ = dimensions_;
288
330
  result.scalar_words_ = scalar_words_;
289
- result.scalar_kind_ = scalar_kind_;
290
- result.isa_ = isa_;
291
331
  result.casted_vector_bytes_ = casted_vector_bytes_;
292
332
  result.cast_buffer_ = cast_buffer_;
293
333
  result.casts_ = casts_;
@@ -372,6 +412,16 @@ class index_punned_dense_gt {
372
412
  return typed_->search_around(static_cast<id_t>(hint), {vector_data, vector_bytes}, wanted, config);
373
413
  }
374
414
 
415
+ void reindex_labels() {
416
+ shared_lock_t lock(lookup_table_mutex_);
417
+ lookup_table_.clear();
418
+ for (std::size_t i = 0; i != typed_->size(); ++i) {
419
+ member_citerator_t iterator = typed_->cbegin() + i;
420
+ member_cref_t member = *iterator;
421
+ lookup_table_[member.label] = static_cast<id_t>(i);
422
+ }
423
+ }
424
+
375
425
  id_t lookup_id_(label_t label) const {
376
426
  shared_lock_t lock(lookup_table_mutex_);
377
427
  return lookup_table_.at(label);
@@ -425,20 +475,18 @@ class index_punned_dense_gt {
425
475
  static index_punned_dense_gt make_( //
426
476
  std::size_t dimensions, scalar_kind_t scalar_kind, //
427
477
  index_config_t config, std::size_t expansion_add, std::size_t expansion_search, //
428
- metric_and_meta_t metric_and_meta, casts_t casts) {
478
+ metric_t metric, casts_t casts) {
429
479
 
430
480
  std::size_t hardware_threads = std::thread::hardware_concurrency();
431
481
  index_punned_dense_gt result;
432
482
  result.dimensions_ = dimensions;
433
- result.scalar_kind_ = scalar_kind;
434
483
  result.scalar_words_ = count_scalar_words_(dimensions, scalar_kind);
435
484
  result.expansion_add_ = expansion_add;
436
485
  result.expansion_search_ = expansion_search;
437
486
  result.casted_vector_bytes_ = bytes_per_scalar(scalar_kind) * result.scalar_words_;
438
487
  result.cast_buffer_.resize(hardware_threads * result.casted_vector_bytes_);
439
488
  result.casts_ = casts;
440
- result.isa_ = metric_and_meta.isa;
441
- result.root_metric_ = metric_and_meta.metric;
489
+ result.root_metric_ = metric;
442
490
 
443
491
  // Fill the thread IDs.
444
492
  result.available_threads_.resize(hardware_threads);
@@ -446,7 +494,7 @@ class index_punned_dense_gt {
446
494
 
447
495
  // Available since C11, but only C++17, so we use the C version.
448
496
  index_t* raw = index_allocator_t{}.allocate(1);
449
- new (raw) index_t(config, metric_and_meta.metric);
497
+ new (raw) index_t(config, metric);
450
498
  result.typed_ = raw;
451
499
  return result;
452
500
  }
@@ -491,80 +539,49 @@ class index_punned_dense_gt {
491
539
  }
492
540
  }
493
541
 
494
- template <typename scalar_at, typename typed_at> static metric_t pun_metric_(typed_at metric) {
495
- using scalar_t = scalar_at;
496
- using vector_view_t = span_gt<scalar_t const>;
497
- return [=](punned_vector_view_t a, punned_vector_view_t b) -> punned_distance_t {
498
- std::size_t dims = a.size() / sizeof(scalar_t);
499
- vector_view_t a_typed{(scalar_t const*)a.data(), dims};
500
- vector_view_t b_typed{(scalar_t const*)b.data(), dims};
501
- return metric(a_typed, b_typed);
502
- };
503
- }
504
-
505
- static metric_and_meta_t ip_metric_f32_(std::size_t dimensions) {
542
+ static metric_t ip_metric_f32_(std::size_t dimensions) {
506
543
  (void)dimensions;
507
544
  #if USEARCH_USE_SIMSIMD
508
545
  if (hardware_supports(isa_t::avx2_k) && dimensions % 4 == 0)
509
- return {
510
- pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
511
- return 1.f - simsimd_dot_f32x4avx2(a, b, dimensions);
512
- }),
513
- isa_t::avx2_k,
514
- };
546
+ return {metric_kind_t::ip_k, isa_t::avx2_k, 0.f,
547
+ [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32x4avx2(a, b, dimensions); }};
515
548
  if (hardware_supports(isa_t::sve_k))
516
- return {
517
- pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
518
- return 1.f - simsimd_dot_f32sve(a, b, dimensions);
519
- }),
520
- isa_t::sve_k,
521
- };
549
+ return {metric_kind_t::ip_k, isa_t::sve_k, 0.f,
550
+ [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32sve(a, b, dimensions); }};
522
551
  if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
523
- return {
524
- pun_metric_<simsimd_f32_t>([=](simsimd_f32_t const* a, simsimd_f32_t const* b) {
525
- return 1.f - simsimd_dot_f32x4neon(a, b, dimensions);
526
- }),
527
- isa_t::neon_k,
528
- };
552
+ return {metric_kind_t::ip_k, isa_t::neon_k, 0.f,
553
+ [=](f32_t const* a, f32_t const* b) { return 1.f - simsimd_dot_f32x4neon(a, b, dimensions); }};
529
554
  #endif
530
- return pun_metric_<f32_t>(ip_gt<f32_t>{});
555
+ return ip_gt<f32_t>{};
531
556
  }
532
557
 
533
- static metric_and_meta_t cos_metric_f16_(std::size_t dimensions) {
558
+ static metric_t cos_metric_f16_(std::size_t dimensions) {
534
559
  (void)dimensions;
535
560
  #if USEARCH_USE_SIMSIMD
536
561
  if (hardware_supports(isa_t::avx512_k) && dimensions % 16 == 0)
537
- return {
538
- pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
539
- return 1.f - simsimd_cos_f16x16avx512(a, b, dimensions);
540
- }),
541
- isa_t::avx512_k,
542
- };
562
+ return {metric_kind_t::cos_k, isa_t::avx512_k, simsimd_f16_t(0),
563
+ [=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
564
+ return 1.f - simsimd_cos_f16x16avx512(a, b, dimensions);
565
+ }};
543
566
  if (hardware_supports(isa_t::neon_k) && dimensions % 4 == 0)
544
- return {
545
- pun_metric_<simsimd_f16_t>([=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
546
- return 1.f - simsimd_cos_f16x4neon(a, b, dimensions);
547
- }),
548
- isa_t::neon_k,
549
- };
567
+ return {metric_kind_t::cos_k, isa_t::neon_k, simsimd_f16_t(0),
568
+ [=](simsimd_f16_t const* a, simsimd_f16_t const* b) {
569
+ return 1.f - simsimd_cos_f16x4neon(a, b, dimensions);
570
+ }};
550
571
  #endif
551
- return pun_metric_<f16_t>(cos_gt<f16_t, f32_t>{});
572
+ return cos_gt<f16_t, f32_t>{};
552
573
  }
553
574
 
554
- static metric_and_meta_t cos_metric_f8_(std::size_t dimensions) {
555
- (void)dimensions;
575
+ static metric_t cos_metric_f8_(std::size_t dimensions) {
556
576
  #if USEARCH_USE_SIMSIMD
557
577
  if (hardware_supports(isa_t::neon_k) && dimensions % 16 == 0)
558
- return {
559
- pun_metric_<int8_t>(
560
- [=](int8_t const* a, int8_t const* b) { return 1.f - simsimd_cos_i8x16neon(a, b, dimensions); }),
561
- isa_t::neon_k,
562
- };
578
+ return {metric_kind_t::cos_k, isa_t::neon_k, int8_t(0),
579
+ [=](int8_t const* a, int8_t const* b) { return 1.f - simsimd_cos_i8x16neon(a, b, dimensions); }};
563
580
  #endif
564
- return pun_metric_<f8_bits_t>(cos_f8_t{dimensions});
581
+ return cos_f8_t{dimensions};
565
582
  }
566
583
 
567
- static metric_and_meta_t ip_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
584
+ static metric_t ip_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
568
585
  switch (accuracy) {
569
586
  case scalar_kind_t::f32_k:
570
587
  // The two most common numeric types for the most common metric have optimized versions
@@ -574,62 +591,62 @@ class index_punned_dense_gt {
574
591
  return cos_metric_f16_(dimensions);
575
592
 
576
593
  case scalar_kind_t::f8_k: return cos_metric_f8_(dimensions);
577
- case scalar_kind_t::f64_k: return pun_metric_<f64_t>(ip_gt<f64_t>{});
594
+ case scalar_kind_t::f64_k: return ip_gt<f64_t>{};
578
595
  default: return {};
579
596
  }
580
597
  }
581
598
 
582
- static metric_and_meta_t l2sq_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
599
+ static metric_t l2sq_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
583
600
  switch (accuracy) {
584
- case scalar_kind_t::f8_k: return pun_metric_<f8_bits_t>(l2sq_f8_t{dimensions});
585
- case scalar_kind_t::f16_k: return pun_metric_<f16_t>(l2sq_gt<f16_t, f32_t>{});
586
- case scalar_kind_t::f32_k: return pun_metric_<f32_t>(l2sq_gt<f32_t>{});
587
- case scalar_kind_t::f64_k: return pun_metric_<f64_t>(l2sq_gt<f64_t>{});
601
+ case scalar_kind_t::f8_k: return l2sq_f8_t{dimensions};
602
+ case scalar_kind_t::f16_k: return l2sq_gt<f16_t, f32_t>{};
603
+ case scalar_kind_t::f32_k: return l2sq_gt<f32_t>{};
604
+ case scalar_kind_t::f64_k: return l2sq_gt<f64_t>{};
588
605
  default: return {};
589
606
  }
590
607
  }
591
608
 
592
- static metric_and_meta_t cos_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
609
+ static metric_t cos_metric_(std::size_t dimensions, scalar_kind_t accuracy) {
593
610
  switch (accuracy) {
594
611
  case scalar_kind_t::f8_k: return cos_metric_f8_(dimensions);
595
612
  case scalar_kind_t::f16_k: return cos_metric_f16_(dimensions);
596
- case scalar_kind_t::f32_k: return pun_metric_<f32_t>(cos_gt<f32_t>{});
597
- case scalar_kind_t::f64_k: return pun_metric_<f64_t>(cos_gt<f64_t>{});
613
+ case scalar_kind_t::f32_k: return cos_gt<f32_t>{};
614
+ case scalar_kind_t::f64_k: return cos_gt<f64_t>{};
598
615
  default: return {};
599
616
  }
600
617
  }
601
618
 
602
- static metric_and_meta_t haversine_metric_(scalar_kind_t accuracy) {
619
+ static metric_t haversine_metric_(scalar_kind_t accuracy) {
603
620
  switch (accuracy) {
604
- case scalar_kind_t::f8_k: return pun_metric_<f8_bits_t>(haversine_gt<f8_bits_t, f32_t>{});
605
- case scalar_kind_t::f16_k: return pun_metric_<f16_t>(haversine_gt<f16_t, f32_t>{});
606
- case scalar_kind_t::f32_k: return pun_metric_<f32_t>(haversine_gt<f32_t>{});
607
- case scalar_kind_t::f64_k: return pun_metric_<f64_t>(haversine_gt<f64_t>{});
621
+ case scalar_kind_t::f8_k: return haversine_gt<f8_bits_t, f32_t>{};
622
+ case scalar_kind_t::f16_k: return haversine_gt<f16_t, f32_t>{};
623
+ case scalar_kind_t::f32_k: return haversine_gt<f32_t>{};
624
+ case scalar_kind_t::f64_k: return haversine_gt<f64_t>{};
608
625
  default: return {};
609
626
  }
610
627
  }
611
628
 
612
- static metric_and_meta_t pearson_metric_(scalar_kind_t accuracy) {
629
+ static metric_t pearson_metric_(scalar_kind_t accuracy) {
613
630
  switch (accuracy) {
614
- case scalar_kind_t::f8_k: return pun_metric_<f8_bits_t>(pearson_correlation_gt<f8_bits_t, f32_t>{});
615
- case scalar_kind_t::f16_k: return pun_metric_<f16_t>(pearson_correlation_gt<f16_t, f32_t>{});
616
- case scalar_kind_t::f32_k: return pun_metric_<f32_t>(pearson_correlation_gt<f32_t>{});
617
- case scalar_kind_t::f64_k: return pun_metric_<f64_t>(pearson_correlation_gt<f64_t>{});
631
+ case scalar_kind_t::f8_k: return pearson_correlation_gt<f8_bits_t, f32_t>{};
632
+ case scalar_kind_t::f16_k: return pearson_correlation_gt<f16_t, f32_t>{};
633
+ case scalar_kind_t::f32_k: return pearson_correlation_gt<f32_t>{};
634
+ case scalar_kind_t::f64_k: return pearson_correlation_gt<f64_t>{};
618
635
  default: return {};
619
636
  }
620
637
  }
621
638
 
622
- static metric_and_meta_t make_metric_(metric_kind_t kind, std::size_t dimensions, scalar_kind_t accuracy) {
639
+ static metric_t make_metric_(metric_kind_t kind, std::size_t dimensions, scalar_kind_t accuracy) {
623
640
  switch (kind) {
624
641
  case metric_kind_t::ip_k: return ip_metric_(dimensions, accuracy);
625
642
  case metric_kind_t::cos_k: return cos_metric_(dimensions, accuracy);
626
643
  case metric_kind_t::l2sq_k: return l2sq_metric_(dimensions, accuracy);
627
644
  case metric_kind_t::pearson_k: return pearson_metric_(accuracy);
628
645
  case metric_kind_t::haversine_k: return haversine_metric_(accuracy);
629
- case metric_kind_t::hamming_k: return pun_metric_<unsigned char>(hamming_gt<unsigned char>{});
646
+ case metric_kind_t::hamming_k: return hamming_gt<b1x8_t>{};
630
647
  case metric_kind_t::jaccard_k: // Equivalent to Tanimoto
631
- case metric_kind_t::tanimoto_k: return pun_metric_<unsigned char>(tanimoto_gt<unsigned char>{});
632
- case metric_kind_t::sorensen_k: return pun_metric_<unsigned char>(sorensen_gt<unsigned char>{});
648
+ case metric_kind_t::tanimoto_k: return tanimoto_gt<b1x8_t>{};
649
+ case metric_kind_t::sorensen_k: return sorensen_gt<b1x8_t>{};
633
650
  default: return {};
634
651
  }
635
652
  }
@@ -94,20 +94,11 @@ inline bool hardware_supports(isa_t isa) noexcept {
94
94
  default: return false;
95
95
  }
96
96
  #endif
97
+
98
+ (void)isa;
97
99
  return false;
98
100
  }
99
101
 
100
- enum class b1x8_t : unsigned char {};
101
-
102
- enum class scalar_kind_t {
103
- unknown_k = 0,
104
- f64_k,
105
- f32_k,
106
- f16_k,
107
- f8_k,
108
- b1x8_k,
109
- };
110
-
111
102
  inline std::size_t bytes_per_scalar(scalar_kind_t accuracy) noexcept {
112
103
  switch (accuracy) {
113
104
  case scalar_kind_t::f32_k: return 4;
@@ -186,7 +177,6 @@ inline expected_gt<metric_kind_t> metric_from_name(char const* name, std::size_t
186
177
 
187
178
  using punned_distance_t = float;
188
179
  using punned_vector_view_t = span_gt<byte_t const>;
189
- using punned_metric_t = std::function<punned_distance_t(punned_vector_view_t, punned_vector_view_t)>;
190
180
 
191
181
  class f8_bits_t;
192
182
  class f16_bits_t;
@@ -206,6 +206,10 @@ scalar_kind_t to_native_scalar(USearchScalar m) {
206
206
  _native->clear();
207
207
  }
208
208
 
209
+ - (void)reserve:(UInt32)count {
210
+ _native->reserve(static_cast<std::size_t>(count));
211
+ }
212
+
209
213
  - (void)save:(NSString *)path {
210
214
  char const *path_c = [path UTF8String];
211
215
 
@@ -51,6 +51,11 @@ API_AVAILABLE(ios(13.0), macos(10.15), tvos(13.0), watchos(6.0))
51
51
  */
52
52
  + (instancetype)make:(USearchMetric)metric dimensions:(UInt32)dimensions connectivity:(UInt32)connectivity quantization:(USearchScalar)quantization NS_SWIFT_NAME(make(metric:dimensions:connectivity:quantization:));
53
53
 
54
+ /**
55
+ * @brief Pre-allocates space in the index for the given number of vectors.
56
+ */
57
+ - (void)reserve:(UInt32)count NS_SWIFT_NAME(reserve(_:));
58
+
54
59
  /**
55
60
  * @brief Adds a labeled vector to the index.
56
61
  * @param vector Single-precision vector.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "0.18.8",
3
+ "version": "0.19.1",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ashot Vardanian",
6
6
  "license": "Apache 2.0",
@@ -24,6 +24,6 @@
24
24
  "@semantic-release/exec": "^6.0.3",
25
25
  "@semantic-release/git": "^10.0.1",
26
26
  "conventional-changelog-eslint": "^3.0.9",
27
- "semantic-release": "^21.0.5"
27
+ "semantic-release": "^21.0.6"
28
28
  }
29
29
  }