usearch 0.11.1 → 0.12.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.
- package/.github/workflows/prerelease.yml +1 -5
- package/.github/workflows/release.yml +22 -2
- package/.gitignore +84 -0
- package/.vscode/launch.json +7 -1
- package/.vscode/settings.json +15 -1
- package/Cargo.toml +1 -1
- package/README.md +42 -7
- package/binding.gyp +2 -2
- package/c/lib.cpp +141 -0
- package/c/usearch.h +43 -0
- package/conanfile.py +1 -1
- package/docs/compilation.md +14 -3
- package/golang/go.mod +3 -0
- package/golang/lib.go +238 -0
- package/golang/usearch.h +9 -0
- package/golang/usearch_par_test.go +40 -0
- package/golang/usearch_test.go +82 -0
- package/include/usearch/usearch.hpp +595 -248
- package/java/cloud/unum/usearch/Index.java +1 -1
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +36 -44
- package/javascript/lib.cpp +10 -6
- package/javascript/usearch.js +3 -0
- package/objc/USearchObjective.mm +3 -3
- package/package.json +4 -3
- package/pyproject.toml +1 -1
- package/python/lib.cpp +354 -181
- package/python/scripts/test.py +87 -28
- package/python/usearch/index.py +131 -56
- package/rust/lib.cpp +33 -24
- package/rust/lib.hpp +2 -2
- package/setup.py +4 -1
- package/src/CMakeLists.txt +4 -4
- package/src/bench.cpp +22 -19
- package/src/punned.hpp +108 -79
- package/src/test.cpp +77 -27
- package/wolfram/lib.cpp +7 -8
- package/Dockerfile +0 -16
- package/VERSION +0 -1
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
|
|
256
|
-
int
|
|
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,
|
|
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
|
|
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
|
|
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{
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
642
|
-
|
|
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
|
|
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
|
-
|
|
279
|
+
parsed.result = accuracy_t::f32_k;
|
|
279
280
|
else if (str_equals(name, len, "f64"))
|
|
280
|
-
|
|
281
|
+
parsed.result = accuracy_t::f64_k;
|
|
281
282
|
else if (str_equals(name, len, "f16"))
|
|
282
|
-
|
|
283
|
+
parsed.result = accuracy_t::f16_k;
|
|
283
284
|
else if (str_equals(name, len, "f8"))
|
|
284
|
-
|
|
285
|
+
parsed.result = accuracy_t::f8_k;
|
|
285
286
|
else
|
|
286
|
-
|
|
287
|
-
return
|
|
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
|
-
|
|
292
|
-
|
|
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
|
-
|
|
319
|
+
expected_gt<index_at> parsed;
|
|
320
|
+
if (metric == common_metric_kind_t::l2sq_k) {
|
|
295
321
|
if (dimensions == 0)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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)
|
|
474
|
-
void change_expansion_search(std::size_t 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
|
|
477
|
-
member_citerator_t cend() const
|
|
478
|
-
member_citerator_t begin() const
|
|
479
|
-
member_citerator_t end() const
|
|
480
|
-
member_iterator_t begin()
|
|
481
|
-
member_iterator_t 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
|
|
484
|
-
stats_t stats(std::size_t level) const
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
|
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
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
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
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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,
|
|
535
|
-
static punned_gt l2sq(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f16_k,
|
|
536
|
-
static punned_gt cos(std::size_t dimensions, accuracy_t accuracy = accuracy_t::f32_k,
|
|
537
|
-
static punned_gt haversine(accuracy_t accuracy = accuracy_t::f32_k,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 <
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
+
expect(id >= 42 && id <= 44);
|
|
49
56
|
count++;
|
|
50
57
|
}
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
expect(matched_count == 3);
|
|
73
|
+
expect(matched_labels[0] == 42);
|
|
74
|
+
expect(std::abs(matched_distances[0]) < 0.01);
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
+
scalar_t vec_reconstructed[3] = {0, 0, 0};
|
|
85
93
|
index.reconstruct(42, span_t{&vec_reconstructed[0], 3ul});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
}
|