usearch 0.9.7 → 0.11.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/release.yml +1 -1
- package/.gitmodules +3 -0
- package/.vscode/settings.json +2 -0
- package/Cargo.toml +1 -1
- package/Package.swift +1 -0
- package/README.md +1 -1
- package/VERSION +1 -1
- package/binding.gyp +4 -3
- package/build.gradle +1 -1
- package/build.rs +1 -0
- package/conanfile.py +1 -1
- package/docs/compilation.md +6 -3
- package/include/usearch/usearch.hpp +168 -45
- package/package.json +1 -1
- package/python/lib.cpp +42 -23
- package/python/scripts/bench.py +2 -2
- package/python/scripts/test.py +1 -1
- package/python/usearch/index.py +298 -0
- package/robin-map/.codecov.yml +5 -0
- package/robin-map/.github/workflows/ci.yml +136 -0
- package/robin-map/CMakeLists.txt +86 -0
- package/robin-map/LICENSE +21 -0
- package/robin-map/README.md +483 -0
- package/robin-map/cmake/tsl-robin-mapConfig.cmake.in +9 -0
- package/robin-map/doxygen.conf +2483 -0
- package/robin-map/include/tsl/robin_growth_policy.h +406 -0
- package/robin-map/include/tsl/robin_hash.h +1639 -0
- package/robin-map/include/tsl/robin_map.h +807 -0
- package/robin-map/include/tsl/robin_set.h +660 -0
- package/robin-map/tests/CMakeLists.txt +26 -0
- package/robin-map/tests/custom_allocator_tests.cpp +138 -0
- package/robin-map/tests/main.cpp +26 -0
- package/robin-map/tests/policy_tests.cpp +97 -0
- package/robin-map/tests/robin_map_tests.cpp +1451 -0
- package/robin-map/tests/robin_set_tests.cpp +164 -0
- package/robin-map/tests/utils.h +443 -0
- package/robin-map/tsl-robin-map.natvis +78 -0
- package/setup.py +4 -2
- package/src/CMakeLists.txt +1 -0
- package/src/punned.hpp +56 -6
- package/src/test.cpp +32 -8
package/src/CMakeLists.txt
CHANGED
|
@@ -69,6 +69,7 @@ include(clipp)
|
|
|
69
69
|
set(USEARCH_PUNNED_INCLUDE_DIRS
|
|
70
70
|
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
|
71
71
|
"${CMAKE_CURRENT_SOURCE_DIR}/../fp16/include"
|
|
72
|
+
"${CMAKE_CURRENT_SOURCE_DIR}/../robin-map/include"
|
|
72
73
|
"${CMAKE_CURRENT_SOURCE_DIR}/../simsimd/include"
|
|
73
74
|
"${CMAKE_CURRENT_SOURCE_DIR}/"
|
|
74
75
|
)
|
package/src/punned.hpp
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include <stdlib.h> // `aligned_alloc`
|
|
3
3
|
|
|
4
|
-
#include <functional>
|
|
5
|
-
#include <numeric>
|
|
6
|
-
#include <
|
|
7
|
-
#include <
|
|
8
|
-
#include <
|
|
4
|
+
#include <functional> // `std::function`
|
|
5
|
+
#include <numeric> // `std::iota`
|
|
6
|
+
#include <shared_mutex> // `std::shared_mutex`
|
|
7
|
+
#include <stdexcept> // `std::invalid_argument`
|
|
8
|
+
#include <thread> // `std::thread`
|
|
9
|
+
#include <vector> // `std::vector`
|
|
9
10
|
|
|
10
11
|
#include <fp16/fp16.h>
|
|
12
|
+
#include <tsl/robin_map.h>
|
|
11
13
|
|
|
12
14
|
#include <usearch/usearch.hpp>
|
|
13
15
|
|
|
@@ -401,6 +403,8 @@ class punned_gt {
|
|
|
401
403
|
|
|
402
404
|
using member_iterator_t = typename index_t::member_iterator_t;
|
|
403
405
|
using member_citerator_t = typename index_t::member_citerator_t;
|
|
406
|
+
using member_ref_t = typename index_t::member_ref_t;
|
|
407
|
+
using member_cref_t = typename index_t::member_cref_t;
|
|
404
408
|
|
|
405
409
|
std::size_t dimensions_ = 0;
|
|
406
410
|
std::size_t casted_vector_bytes_ = 0;
|
|
@@ -414,6 +418,10 @@ class punned_gt {
|
|
|
414
418
|
cast_t from_f16{};
|
|
415
419
|
cast_t from_f32{};
|
|
416
420
|
cast_t from_f64{};
|
|
421
|
+
cast_t to_f8{};
|
|
422
|
+
cast_t to_f16{};
|
|
423
|
+
cast_t to_f32{};
|
|
424
|
+
cast_t to_f64{};
|
|
417
425
|
} casts_;
|
|
418
426
|
|
|
419
427
|
punned_stateful_metric_t root_metric_;
|
|
@@ -421,6 +429,13 @@ class punned_gt {
|
|
|
421
429
|
mutable std::vector<std::size_t> available_threads_;
|
|
422
430
|
mutable std::mutex available_threads_mutex_;
|
|
423
431
|
|
|
432
|
+
using shared_mutex_t = std::mutex; // TODO: Find an OS-compatible solution
|
|
433
|
+
using shared_lock_t = std::unique_lock<shared_mutex_t>;
|
|
434
|
+
using unique_lock_t = std::unique_lock<shared_mutex_t>;
|
|
435
|
+
|
|
436
|
+
mutable shared_mutex_t lookup_table_mutex_;
|
|
437
|
+
tsl::robin_map<label_t, id_t> lookup_table_;
|
|
438
|
+
|
|
424
439
|
public:
|
|
425
440
|
using search_results_t = typename index_t::search_results_t;
|
|
426
441
|
using add_result_t = typename index_t::add_result_t;
|
|
@@ -455,6 +470,8 @@ class punned_gt {
|
|
|
455
470
|
std::size_t capacity() const { return typed_->capacity(); }
|
|
456
471
|
config_t const& config() const { return typed_->config(); }
|
|
457
472
|
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); }
|
|
458
475
|
|
|
459
476
|
member_citerator_t cbegin() const noexcept { return typed_->cbegin(); }
|
|
460
477
|
member_citerator_t cend() const noexcept { return typed_->cend(); }
|
|
@@ -474,6 +491,10 @@ class punned_gt {
|
|
|
474
491
|
void view(char const* path) { typed_->view(path); }
|
|
475
492
|
void reserve(std::size_t capacity) { typed_->reserve(capacity); }
|
|
476
493
|
|
|
494
|
+
std::size_t memory_usage(std::size_t allocator_entry_bytes = default_allocator_entry_bytes()) const noexcept {
|
|
495
|
+
return typed_->memory_usage(allocator_entry_bytes);
|
|
496
|
+
}
|
|
497
|
+
|
|
477
498
|
// clang-format off
|
|
478
499
|
add_result_t add(label_t label, f8_bits_t const* vector) { return add_(label, vector, casts_.from_f8); }
|
|
479
500
|
add_result_t add(label_t label, f16_bits_t const* vector) { return add_(label, vector, casts_.from_f16); }
|
|
@@ -505,6 +526,11 @@ class punned_gt {
|
|
|
505
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); }
|
|
506
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); }
|
|
507
528
|
|
|
529
|
+
void reconstruct(label_t label, f8_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f8); }
|
|
530
|
+
void reconstruct(label_t label, f16_bits_t* vector) const { return reconstruct_(label, vector, casts_.to_f16); }
|
|
531
|
+
void reconstruct(label_t label, f32_t* vector) const { return reconstruct_(label, vector, casts_.to_f32); }
|
|
532
|
+
void reconstruct(label_t label, f64_t* vector) const { return reconstruct_(label, vector, casts_.to_f64); }
|
|
533
|
+
|
|
508
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); }
|
|
509
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); }
|
|
510
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); }
|
|
@@ -583,7 +609,12 @@ class punned_gt {
|
|
|
583
609
|
if (casted)
|
|
584
610
|
vector_data = casted_data, vector_bytes = casted_vector_bytes_, config.store_vector = true;
|
|
585
611
|
|
|
586
|
-
|
|
612
|
+
add_result_t result = typed_->add(label, {vector_data, vector_bytes}, config);
|
|
613
|
+
{
|
|
614
|
+
unique_lock_t lock(lookup_table_mutex_);
|
|
615
|
+
lookup_table_.emplace(label, result.id);
|
|
616
|
+
}
|
|
617
|
+
return result;
|
|
587
618
|
}
|
|
588
619
|
|
|
589
620
|
template <typename scalar_at>
|
|
@@ -618,6 +649,21 @@ class punned_gt {
|
|
|
618
649
|
return typed_->search_around(static_cast<id_t>(hint), {vector_data, vector_bytes}, wanted, config);
|
|
619
650
|
}
|
|
620
651
|
|
|
652
|
+
id_t lookup_id_(label_t label) const {
|
|
653
|
+
shared_lock_t lock(lookup_table_mutex_);
|
|
654
|
+
return lookup_table_.at(label);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
template <typename scalar_at> void reconstruct_(label_t label, scalar_at* reconstructed, cast_t const& cast) const {
|
|
658
|
+
id_t id = lookup_id_(label);
|
|
659
|
+
member_citerator_t iterator = typed_->cbegin() + id;
|
|
660
|
+
member_cref_t member = *iterator;
|
|
661
|
+
byte_t const* casted_vector = reinterpret_cast<byte_t const*>(member.vector.data());
|
|
662
|
+
bool casted = cast(casted_vector, casted_vector_bytes_, (byte_t*)reconstructed);
|
|
663
|
+
if (!casted)
|
|
664
|
+
std::memcpy(reconstructed, casted_vector, casted_vector_bytes_);
|
|
665
|
+
}
|
|
666
|
+
|
|
621
667
|
template <typename scalar_at> add_result_t add_(label_t label, scalar_at const* vector, cast_t const& cast) {
|
|
622
668
|
thread_lock_t lock = thread_lock_();
|
|
623
669
|
add_config_t add_config;
|
|
@@ -680,6 +726,10 @@ class punned_gt {
|
|
|
680
726
|
result.from_f16 = cast_gt<f16_bits_t, to_scalar_at>{};
|
|
681
727
|
result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
|
|
682
728
|
result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
|
|
729
|
+
result.to_f8 = cast_gt<to_scalar_at, f8_bits_t>{};
|
|
730
|
+
result.to_f16 = cast_gt<to_scalar_at, f16_bits_t>{};
|
|
731
|
+
result.to_f32 = cast_gt<to_scalar_at, f32_t>{};
|
|
732
|
+
result.to_f64 = cast_gt<to_scalar_at, f64_t>{};
|
|
683
733
|
return result;
|
|
684
734
|
}
|
|
685
735
|
|
package/src/test.cpp
CHANGED
|
@@ -12,7 +12,7 @@ using namespace unum;
|
|
|
12
12
|
using point_id_t = std::int64_t;
|
|
13
13
|
|
|
14
14
|
template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
|
|
15
|
-
using
|
|
15
|
+
using view_t = span_gt<scalar_at const>;
|
|
16
16
|
using distance_t = typename index_at::distance_t;
|
|
17
17
|
|
|
18
18
|
scalar_at vec[3] = {10, 20, 15};
|
|
@@ -20,12 +20,12 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
|
|
|
20
20
|
scalar_at vec_b[3] = {16, 17, 18};
|
|
21
21
|
|
|
22
22
|
index.reserve(10);
|
|
23
|
-
index.add(42,
|
|
23
|
+
index.add(42, view_t{&vec[0], 3ul});
|
|
24
24
|
|
|
25
25
|
// Default approximate search
|
|
26
26
|
point_id_t matched_labels[10] = {0};
|
|
27
27
|
distance_t matched_distances[10] = {0};
|
|
28
|
-
std::size_t matched_count = index.search(
|
|
28
|
+
std::size_t matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
|
|
29
29
|
|
|
30
30
|
assert(matched_count == 1);
|
|
31
31
|
assert(matched_labels[0] == 42);
|
|
@@ -34,12 +34,12 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
|
|
|
34
34
|
// Add more entries
|
|
35
35
|
search_config_t search_config;
|
|
36
36
|
search_config.exact = true;
|
|
37
|
-
index.add(43,
|
|
38
|
-
index.add(44,
|
|
37
|
+
index.add(43, view_t{&vec_a[0], 3ul});
|
|
38
|
+
index.add(44, view_t{&vec_b[0], 3ul});
|
|
39
39
|
assert(index.size() == 3);
|
|
40
40
|
|
|
41
41
|
// Perform exact search
|
|
42
|
-
matched_count = index.search(
|
|
42
|
+
matched_count = index.search(view_t{&vec[0], 3ul}, 5, search_config).dump_to(matched_labels, matched_distances);
|
|
43
43
|
|
|
44
44
|
// Validate scans
|
|
45
45
|
std::size_t count = 0;
|
|
@@ -54,17 +54,38 @@ template <typename scalar_at, typename index_at> void test3d(index_at&& index) {
|
|
|
54
54
|
// Search again over reconstructed index
|
|
55
55
|
index.save("tmp.usearch");
|
|
56
56
|
index.load("tmp.usearch");
|
|
57
|
-
matched_count = index.search(
|
|
57
|
+
matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
|
|
58
58
|
assert(matched_count == 3);
|
|
59
59
|
assert(matched_labels[0] == 42);
|
|
60
60
|
assert(std::abs(matched_distances[0]) < 0.01);
|
|
61
61
|
|
|
62
62
|
// Search again over mapped index
|
|
63
63
|
index.view("tmp.usearch");
|
|
64
|
-
matched_count = index.search(
|
|
64
|
+
matched_count = index.search(view_t{&vec[0], 3ul}, 5).dump_to(matched_labels, matched_distances);
|
|
65
65
|
assert(matched_count == 3);
|
|
66
66
|
assert(matched_labels[0] == 42);
|
|
67
67
|
assert(std::abs(matched_distances[0]) < 0.01);
|
|
68
|
+
|
|
69
|
+
assert(index.memory_usage() > 0);
|
|
70
|
+
assert(index.stats().max_edges > 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
template <typename scalar_at, typename index_at> void test3d_punned(index_at&& index) {
|
|
74
|
+
using view_t = span_gt<scalar_at const>;
|
|
75
|
+
using span_t = span_gt<scalar_at>;
|
|
76
|
+
using distance_t = typename index_at::distance_t;
|
|
77
|
+
|
|
78
|
+
scalar_at vec[3] = {10, 20, 15};
|
|
79
|
+
|
|
80
|
+
index.reserve(10);
|
|
81
|
+
index.add(42, view_t{&vec[0], 3ul});
|
|
82
|
+
|
|
83
|
+
// Reconstruct
|
|
84
|
+
scalar_at vec_reconstructed[3] = {0, 0, 0};
|
|
85
|
+
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]);
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
int main(int, char**) {
|
|
@@ -81,5 +102,8 @@ int main(int, char**) {
|
|
|
81
102
|
test3d<double>(punned_small_t::cos(3));
|
|
82
103
|
test3d<double>(punned_small_t::l2sq(3));
|
|
83
104
|
|
|
105
|
+
test3d_punned<float>(punned_small_t::cos(3));
|
|
106
|
+
test3d_punned<float>(punned_small_t::l2sq(3));
|
|
107
|
+
|
|
84
108
|
return 0;
|
|
85
109
|
}
|