usearch 2.15.2 → 2.16.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/README.md +1 -1
- package/binding.gyp +1 -1
- package/c/CMakeLists.txt +10 -3
- package/cpp/README.md +1 -1
- package/cpp/bench.cpp +2 -2
- package/cpp/test.cpp +16 -0
- package/include/usearch/index.hpp +44 -17
- package/include/usearch/index_dense.hpp +51 -89
- package/include/usearch/index_plugins.hpp +468 -43
- package/javascript/lib.cpp +17 -5
- package/javascript/usearch.test.js +205 -34
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/.git-blame-ignore-revs +2 -0
- package/simsimd/CODE_OF_CONDUCT.md +128 -0
- package/simsimd/CONTRIBUTING.md +84 -26
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +4 -4
- package/simsimd/MANIFEST.in +3 -0
- package/simsimd/Package.swift +15 -3
- package/simsimd/README.md +534 -177
- package/simsimd/binding.gyp +6 -2
- package/simsimd/build.rs +13 -1
- package/simsimd/c/lib.c +266 -53
- package/simsimd/include/simsimd/binary.h +321 -112
- package/simsimd/include/simsimd/curved.h +803 -0
- package/simsimd/include/simsimd/dot.h +694 -521
- package/simsimd/include/simsimd/elementwise.h +1437 -0
- package/simsimd/include/simsimd/geospatial.h +9 -3
- package/simsimd/include/simsimd/mesh.h +69 -0
- package/simsimd/include/simsimd/probability.h +234 -206
- package/simsimd/include/simsimd/simsimd.h +1476 -617
- package/simsimd/include/simsimd/sparse.h +1428 -0
- package/simsimd/include/simsimd/spatial.h +1556 -742
- package/simsimd/include/simsimd/types.h +276 -132
- package/simsimd/javascript/lib.c +1 -2
- package/simsimd/javascript/simsimd.ts +4 -4
- package/simsimd/package.json +4 -4
- package/simsimd/rust/lib.rs +179 -1
- package/simsimd/scripts/bench.cxx +1070 -0
- package/simsimd/scripts/bench.ipynb +187 -0
- package/simsimd/{javascript → scripts}/bench.js +1 -1
- package/simsimd/{rust/benches/sqeuclidean.rs → scripts/bench_sqeuclidean.rs} +6 -6
- package/simsimd/scripts/bench_vectors.py +700 -0
- package/simsimd/scripts/bench_vectors_live.py +213 -0
- package/simsimd/scripts/test.c +155 -0
- package/simsimd/{javascript → scripts}/test.mjs +2 -2
- package/simsimd/scripts/test.py +1410 -0
- package/simsimd/swift/{source/SimSIMD.swift → SimSIMD.swift} +6 -2
- package/simsimd/swift/{tests/SimSIMDTests.swift → Test.swift} +1 -1
- /package/simsimd/{rust/benches/cosine.rs → scripts/bench_cosine.rs} +0 -0
- /package/simsimd/{rust/benches/native.rs → scripts/bench_native.rs} +0 -0
package/README.md
CHANGED
package/binding.gyp
CHANGED
package/c/CMakeLists.txt
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
# Define the source files conditionally
|
|
3
|
+
set(USEARCH_LIB_SOURCES lib.cpp)
|
|
4
|
+
if (USEARCH_USE_SIMSIMD)
|
|
5
|
+
list(APPEND USEARCH_LIB_SOURCES ../simsimd/c/lib.c)
|
|
6
|
+
endif ()
|
|
7
|
+
|
|
1
8
|
if (USEARCH_BUILD_TEST_C)
|
|
2
|
-
add_executable(test_c test.c
|
|
9
|
+
add_executable(test_c test.c ${USEARCH_LIB_SOURCES})
|
|
3
10
|
setup_target(test_c)
|
|
4
11
|
include(CTest)
|
|
5
12
|
enable_testing()
|
|
@@ -9,8 +16,8 @@ endif ()
|
|
|
9
16
|
# This article discusses a better way to allow building either static or shared libraries:
|
|
10
17
|
# https://alexreinking.com/blog/building-a-dual-shared-and-static-library-with-cmake.html
|
|
11
18
|
if (USEARCH_BUILD_LIB_C)
|
|
12
|
-
add_library(usearch_c SHARED
|
|
13
|
-
add_library(usearch_static_c STATIC
|
|
19
|
+
add_library(usearch_c SHARED ${USEARCH_LIB_SOURCES})
|
|
20
|
+
add_library(usearch_static_c STATIC ${USEARCH_LIB_SOURCES})
|
|
14
21
|
|
|
15
22
|
# Set the compile options for the static library
|
|
16
23
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
package/cpp/README.md
CHANGED
|
@@ -36,7 +36,7 @@ int main(int argc, char **argv) {
|
|
|
36
36
|
for (std::size_t i = 0; i != results.size(); ++i)
|
|
37
37
|
// You can access the following properties of every match:
|
|
38
38
|
// results[i].element.key, results[i].element.vector, results[i].distance;
|
|
39
|
-
std::printf("Found matching key: %zu", results[i].
|
|
39
|
+
std::printf("Found matching key: %zu", results[i].member.key);
|
|
40
40
|
return 0;
|
|
41
41
|
}
|
|
42
42
|
```
|
package/cpp/bench.cpp
CHANGED
|
@@ -185,7 +185,7 @@ struct persisted_dataset_gt {
|
|
|
185
185
|
std::size_t vectors_count() const noexcept {
|
|
186
186
|
return vectors_to_take_ ? vectors_to_take_ : (vectors_.rows - vectors_to_skip_);
|
|
187
187
|
}
|
|
188
|
-
|
|
188
|
+
matrix_slice_gt<scalar_t const> vectors_view() const noexcept {
|
|
189
189
|
return {vector(vectors_to_skip_), vectors_count(), dimensions()};
|
|
190
190
|
}
|
|
191
191
|
};
|
|
@@ -224,7 +224,7 @@ struct in_memory_dataset_gt {
|
|
|
224
224
|
scalar_t* query(std::size_t i) noexcept { return queries_.data() + i * dimensions_; }
|
|
225
225
|
compressed_slot_t* neighborhood(std::size_t i) noexcept { return neighborhoods_.data() + i * neighborhood_size_; }
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
matrix_slice_gt<scalar_t const> vectors_view() const noexcept { return {vector(0), vectors_count(), dimensions()}; }
|
|
228
228
|
};
|
|
229
229
|
|
|
230
230
|
char const* getenv_or(char const* name, char const* default_) { return getenv(name) ? getenv(name) : default_; }
|
package/cpp/test.cpp
CHANGED
|
@@ -1100,6 +1100,22 @@ int main(int, char**) {
|
|
|
1100
1100
|
test_uint40();
|
|
1101
1101
|
test_cosine<float, std::int64_t, uint40_t>(10, 10);
|
|
1102
1102
|
|
|
1103
|
+
// Test plugins, like K-Means clustering.
|
|
1104
|
+
{
|
|
1105
|
+
std::size_t vectors_count = 1000, centroids_count = 10, dimensions = 256;
|
|
1106
|
+
kmeans_clustering_t clustering;
|
|
1107
|
+
clustering.max_iterations = 2;
|
|
1108
|
+
std::vector<float> vectors(vectors_count * dimensions), centroids(centroids_count * dimensions);
|
|
1109
|
+
matrix_slice_gt<float const> vectors_slice(vectors.data(), dimensions, vectors_count);
|
|
1110
|
+
matrix_slice_gt<float> centroids_slice(centroids.data(), dimensions, centroids_count);
|
|
1111
|
+
std::generate(vectors.begin(), vectors.end(), [] { return float(std::rand()) / float(INT_MAX); });
|
|
1112
|
+
std::vector<std::size_t> assignments(vectors_count);
|
|
1113
|
+
std::vector<distance_punned_t> distances(vectors_count);
|
|
1114
|
+
auto clustering_result = clustering(vectors_slice, centroids_slice, {assignments.data(), assignments.size()},
|
|
1115
|
+
{distances.data(), distances.size()});
|
|
1116
|
+
expect(clustering_result);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1103
1119
|
// Exact search without constructing indexes.
|
|
1104
1120
|
// Great for validating the distance functions.
|
|
1105
1121
|
std::printf("Testing exact search\n");
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
#define UNUM_USEARCH_HPP
|
|
9
9
|
|
|
10
10
|
#define USEARCH_VERSION_MAJOR 2
|
|
11
|
-
#define USEARCH_VERSION_MINOR
|
|
12
|
-
#define USEARCH_VERSION_PATCH
|
|
11
|
+
#define USEARCH_VERSION_MINOR 16
|
|
12
|
+
#define USEARCH_VERSION_PATCH 0
|
|
13
13
|
|
|
14
14
|
// Inferring C++ version
|
|
15
15
|
// https://stackoverflow.com/a/61552074
|
|
@@ -370,7 +370,7 @@ template <typename scalar_at, typename allocator_at = std::allocator<scalar_at>>
|
|
|
370
370
|
void reset() noexcept {
|
|
371
371
|
if (!std::is_trivially_destructible<scalar_at>::value)
|
|
372
372
|
for (std::size_t i = 0; i != size_; ++i)
|
|
373
|
-
destroy_at(data_ + i);
|
|
373
|
+
unum::usearch::destroy_at(data_ + i); //< Facing some symbol visibility/ambiguity issues
|
|
374
374
|
allocator_at{}.deallocate(data_, size_);
|
|
375
375
|
data_ = nullptr;
|
|
376
376
|
size_ = 0;
|
|
@@ -937,18 +937,6 @@ class sorted_buffer_gt {
|
|
|
937
937
|
return true;
|
|
938
938
|
}
|
|
939
939
|
|
|
940
|
-
inline bool insert_sorted(element_t const* elements, std::size_t elements_count, std::size_t limit) noexcept {
|
|
941
|
-
if (!size_) {
|
|
942
|
-
}
|
|
943
|
-
// If we are inserting elements, we only perform full-scale binary search once,
|
|
944
|
-
// and then only compute successive insertion offsets based only on the tail.
|
|
945
|
-
std::size_t slot = std::lower_bound(elements_, elements_ + size_, elements[0], &less) - elements_;
|
|
946
|
-
if (slot == limit)
|
|
947
|
-
return false;
|
|
948
|
-
std::size_t to_move = size_ - slot - (size_ == limit);
|
|
949
|
-
std::size_t next_slot = std::lower_bound(elements_ + slot, elements_ + size_, elements[1], &less) - elements_;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
940
|
inline element_t pop() noexcept {
|
|
953
941
|
size_--;
|
|
954
942
|
element_t result = elements_[size_];
|
|
@@ -972,6 +960,10 @@ class sorted_buffer_gt {
|
|
|
972
960
|
|
|
973
961
|
/**
|
|
974
962
|
* @brief Five-byte integer type to address node clouds with over 4B entries.
|
|
963
|
+
*
|
|
964
|
+
* 40 bits is enough to address a @b Trillion entries potentially colocated on 1 machine.
|
|
965
|
+
* At roughly 5 bytes * 20 neighbors + 100 bytes per entry, this translates to 200 TB of data,
|
|
966
|
+
* which is similar to a single-server capacity of modern NVME arrays.
|
|
975
967
|
*/
|
|
976
968
|
class usearch_pack_m uint40_t {
|
|
977
969
|
unsigned char octets[5];
|
|
@@ -1226,8 +1218,8 @@ class ring_gt {
|
|
|
1226
1218
|
using element_t = element_at;
|
|
1227
1219
|
using allocator_t = allocator_at;
|
|
1228
1220
|
|
|
1229
|
-
static_assert(std::is_trivially_destructible<element_t>(), "This
|
|
1230
|
-
static_assert(std::is_trivially_copy_constructible<element_t>(), "This
|
|
1221
|
+
static_assert(std::is_trivially_destructible<element_t>(), "This ring is designed for trivial structs");
|
|
1222
|
+
static_assert(std::is_trivially_copy_constructible<element_t>(), "This ring is designed for trivial structs");
|
|
1231
1223
|
|
|
1232
1224
|
using value_type = element_t;
|
|
1233
1225
|
|
|
@@ -2619,6 +2611,16 @@ class index_gt {
|
|
|
2619
2611
|
node_t node = nodes_[candidate.slot];
|
|
2620
2612
|
return {member_cref_t{node.ckey(), candidate.slot}, candidate.distance};
|
|
2621
2613
|
}
|
|
2614
|
+
|
|
2615
|
+
/**
|
|
2616
|
+
* @brief Extracts the search results into a user-provided buffer, that unlike `dump_to`,
|
|
2617
|
+
* may already contain some data, so the new and old results are merged together.
|
|
2618
|
+
* @return The number of results stored in the buffer.
|
|
2619
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2620
|
+
* @param[in] distances The buffer to store the distances to the search results.
|
|
2621
|
+
* @param[in] old_count The number of results already stored in the buffers.
|
|
2622
|
+
* @param[in] max_count The maximum number of results that can be stored in the buffers.
|
|
2623
|
+
*/
|
|
2622
2624
|
inline std::size_t merge_into( //
|
|
2623
2625
|
vector_key_t* keys, distance_t* distances, //
|
|
2624
2626
|
std::size_t old_count, std::size_t max_count) const noexcept {
|
|
@@ -2640,6 +2642,13 @@ class index_gt {
|
|
|
2640
2642
|
}
|
|
2641
2643
|
return merged_count;
|
|
2642
2644
|
}
|
|
2645
|
+
|
|
2646
|
+
/**
|
|
2647
|
+
* @brief Extracts the search results into a user-provided buffer.
|
|
2648
|
+
* @return The number of results stored in the buffer.
|
|
2649
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2650
|
+
* @param[in] distances The buffer to store the distances to the search results.
|
|
2651
|
+
*/
|
|
2643
2652
|
inline std::size_t dump_to(vector_key_t* keys, distance_t* distances) const noexcept {
|
|
2644
2653
|
for (std::size_t i = 0; i != count; ++i) {
|
|
2645
2654
|
match_t result = operator[](i);
|
|
@@ -2648,6 +2657,12 @@ class index_gt {
|
|
|
2648
2657
|
}
|
|
2649
2658
|
return count;
|
|
2650
2659
|
}
|
|
2660
|
+
|
|
2661
|
+
/**
|
|
2662
|
+
* @brief Extracts the search results into a user-provided buffer.
|
|
2663
|
+
* @return The number of results stored in the buffer.
|
|
2664
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2665
|
+
*/
|
|
2651
2666
|
inline std::size_t dump_to(vector_key_t* keys) const noexcept {
|
|
2652
2667
|
for (std::size_t i = 0; i != count; ++i) {
|
|
2653
2668
|
match_t result = operator[](i);
|
|
@@ -3164,6 +3179,18 @@ class index_gt {
|
|
|
3164
3179
|
|
|
3165
3180
|
std::size_t memory_usage_per_node(level_t level) const noexcept { return node_bytes_(level); }
|
|
3166
3181
|
|
|
3182
|
+
double inverse_log_connectivity() const {
|
|
3183
|
+
return pre_.inverse_log_connectivity;
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
std::size_t neighbors_base_bytes() const {
|
|
3187
|
+
return pre_.neighbors_base_bytes;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
std::size_t neighbors_bytes() const {
|
|
3191
|
+
return pre_.neighbors_bytes;
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3167
3194
|
#if defined(USEARCH_USE_PRAGMA_REGION)
|
|
3168
3195
|
#pragma endregion
|
|
3169
3196
|
|
|
@@ -406,8 +406,6 @@ class index_dense_gt {
|
|
|
406
406
|
using tape_allocator_t = memory_mapping_allocator_gt<64>;
|
|
407
407
|
|
|
408
408
|
private:
|
|
409
|
-
/// @brief Schema: input buffer, bytes in input buffer, output buffer.
|
|
410
|
-
using cast_t = bool (*)(byte_t const*, std::size_t, byte_t*);
|
|
411
409
|
/// @brief Punned index.
|
|
412
410
|
using index_t = index_gt< //
|
|
413
411
|
distance_t, vector_key_t, compressed_slot_t, //
|
|
@@ -446,19 +444,7 @@ class index_dense_gt {
|
|
|
446
444
|
|
|
447
445
|
/// @brief Temporary memory for every thread to store a casted vector.
|
|
448
446
|
mutable cast_buffer_t cast_buffer_;
|
|
449
|
-
|
|
450
|
-
cast_t from_b1x8;
|
|
451
|
-
cast_t from_i8;
|
|
452
|
-
cast_t from_f16;
|
|
453
|
-
cast_t from_f32;
|
|
454
|
-
cast_t from_f64;
|
|
455
|
-
|
|
456
|
-
cast_t to_b1x8;
|
|
457
|
-
cast_t to_i8;
|
|
458
|
-
cast_t to_f16;
|
|
459
|
-
cast_t to_f32;
|
|
460
|
-
cast_t to_f64;
|
|
461
|
-
} casts_;
|
|
447
|
+
casts_punned_t casts_;
|
|
462
448
|
|
|
463
449
|
/// @brief An instance of a potentially stateful `metric_t` used to initialize copies and forks.
|
|
464
450
|
metric_t metric_;
|
|
@@ -677,7 +663,7 @@ class index_dense_gt {
|
|
|
677
663
|
// In some cases the metric is not provided, and will be set later.
|
|
678
664
|
if (metric) {
|
|
679
665
|
scalar_kind_t scalar_kind = metric.scalar_kind();
|
|
680
|
-
index.casts_ =
|
|
666
|
+
index.casts_ = casts_punned_t::make(scalar_kind);
|
|
681
667
|
index.metric_ = metric;
|
|
682
668
|
}
|
|
683
669
|
|
|
@@ -707,6 +693,9 @@ class index_dense_gt {
|
|
|
707
693
|
std::size_t max_level() const { return typed_->max_level(); }
|
|
708
694
|
index_dense_config_t const& config() const { return config_; }
|
|
709
695
|
index_limits_t const& limits() const { return typed_->limits(); }
|
|
696
|
+
double inverse_log_connectivity() const { return typed_->inverse_log_connectivity(); }
|
|
697
|
+
std::size_t neighbors_base_bytes() const { return typed_->neighbors_base_bytes(); }
|
|
698
|
+
std::size_t neighbors_bytes() const { return typed_->neighbors_bytes(); }
|
|
710
699
|
bool multi() const { return config_.multi; }
|
|
711
700
|
std::size_t currently_available_threads() const {
|
|
712
701
|
std::unique_lock<std::mutex> available_threads_lock(available_threads_mutex_);
|
|
@@ -767,41 +756,41 @@ class index_dense_gt {
|
|
|
767
756
|
};
|
|
768
757
|
|
|
769
758
|
// clang-format off
|
|
770
|
-
add_result_t add(vector_key_t key, b1x8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.
|
|
771
|
-
add_result_t add(vector_key_t key, i8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.
|
|
772
|
-
add_result_t add(vector_key_t key, f16_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.
|
|
773
|
-
add_result_t add(vector_key_t key, f32_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.
|
|
774
|
-
add_result_t add(vector_key_t key, f64_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.
|
|
775
|
-
|
|
776
|
-
search_result_t search(b1x8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.
|
|
777
|
-
search_result_t search(i8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.
|
|
778
|
-
search_result_t search(f16_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.
|
|
779
|
-
search_result_t search(f32_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.
|
|
780
|
-
search_result_t search(f64_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.
|
|
781
|
-
|
|
782
|
-
template <typename predicate_at> search_result_t filtered_search(b1x8_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.
|
|
783
|
-
template <typename predicate_at> search_result_t filtered_search(i8_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.
|
|
784
|
-
template <typename predicate_at> search_result_t filtered_search(f16_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.
|
|
785
|
-
template <typename predicate_at> search_result_t filtered_search(f32_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.
|
|
786
|
-
template <typename predicate_at> search_result_t filtered_search(f64_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.
|
|
787
|
-
|
|
788
|
-
std::size_t get(vector_key_t key, b1x8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.
|
|
789
|
-
std::size_t get(vector_key_t key, i8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.
|
|
790
|
-
std::size_t get(vector_key_t key, f16_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.
|
|
791
|
-
std::size_t get(vector_key_t key, f32_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.
|
|
792
|
-
std::size_t get(vector_key_t key, f64_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.
|
|
793
|
-
|
|
794
|
-
cluster_result_t cluster(b1x8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.
|
|
795
|
-
cluster_result_t cluster(i8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.
|
|
796
|
-
cluster_result_t cluster(f16_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.
|
|
797
|
-
cluster_result_t cluster(f32_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.
|
|
798
|
-
cluster_result_t cluster(f64_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.
|
|
799
|
-
|
|
800
|
-
aggregated_distances_t distance_between(vector_key_t key, b1x8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.
|
|
801
|
-
aggregated_distances_t distance_between(vector_key_t key, i8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.
|
|
802
|
-
aggregated_distances_t distance_between(vector_key_t key, f16_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.
|
|
803
|
-
aggregated_distances_t distance_between(vector_key_t key, f32_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.
|
|
804
|
-
aggregated_distances_t distance_between(vector_key_t key, f64_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.
|
|
759
|
+
add_result_t add(vector_key_t key, b1x8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from.b1x8); }
|
|
760
|
+
add_result_t add(vector_key_t key, i8_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from.i8); }
|
|
761
|
+
add_result_t add(vector_key_t key, f16_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from.f16); }
|
|
762
|
+
add_result_t add(vector_key_t key, f32_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from.f32); }
|
|
763
|
+
add_result_t add(vector_key_t key, f64_t const* vector, std::size_t thread = any_thread(), bool force_vector_copy = true) { return add_(key, vector, thread, force_vector_copy, casts_.from.f64); }
|
|
764
|
+
|
|
765
|
+
search_result_t search(b1x8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.from.b1x8); }
|
|
766
|
+
search_result_t search(i8_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.from.i8); }
|
|
767
|
+
search_result_t search(f16_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.from.f16); }
|
|
768
|
+
search_result_t search(f32_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.from.f32); }
|
|
769
|
+
search_result_t search(f64_t const* vector, std::size_t wanted, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, dummy_predicate_t {}, thread, exact, casts_.from.f64); }
|
|
770
|
+
|
|
771
|
+
template <typename predicate_at> search_result_t filtered_search(b1x8_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.from.b1x8); }
|
|
772
|
+
template <typename predicate_at> search_result_t filtered_search(i8_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.from.i8); }
|
|
773
|
+
template <typename predicate_at> search_result_t filtered_search(f16_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.from.f16); }
|
|
774
|
+
template <typename predicate_at> search_result_t filtered_search(f32_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.from.f32); }
|
|
775
|
+
template <typename predicate_at> search_result_t filtered_search(f64_t const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread = any_thread(), bool exact = false) const { return search_(vector, wanted, std::forward<predicate_at>(predicate), thread, exact, casts_.from.f64); }
|
|
776
|
+
|
|
777
|
+
std::size_t get(vector_key_t key, b1x8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to.b1x8); }
|
|
778
|
+
std::size_t get(vector_key_t key, i8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to.i8); }
|
|
779
|
+
std::size_t get(vector_key_t key, f16_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to.f16); }
|
|
780
|
+
std::size_t get(vector_key_t key, f32_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to.f32); }
|
|
781
|
+
std::size_t get(vector_key_t key, f64_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to.f64); }
|
|
782
|
+
|
|
783
|
+
cluster_result_t cluster(b1x8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from.b1x8); }
|
|
784
|
+
cluster_result_t cluster(i8_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from.i8); }
|
|
785
|
+
cluster_result_t cluster(f16_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from.f16); }
|
|
786
|
+
cluster_result_t cluster(f32_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from.f32); }
|
|
787
|
+
cluster_result_t cluster(f64_t const* vector, std::size_t level, std::size_t thread = any_thread()) const { return cluster_(vector, level, thread, casts_.from.f64); }
|
|
788
|
+
|
|
789
|
+
aggregated_distances_t distance_between(vector_key_t key, b1x8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to.b1x8); }
|
|
790
|
+
aggregated_distances_t distance_between(vector_key_t key, i8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to.i8); }
|
|
791
|
+
aggregated_distances_t distance_between(vector_key_t key, f16_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to.f16); }
|
|
792
|
+
aggregated_distances_t distance_between(vector_key_t key, f32_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to.f32); }
|
|
793
|
+
aggregated_distances_t distance_between(vector_key_t key, f64_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to.f64); }
|
|
805
794
|
// clang-format on
|
|
806
795
|
|
|
807
796
|
/**
|
|
@@ -1154,7 +1143,7 @@ class index_dense_gt {
|
|
|
1154
1143
|
cast_buffer_ = cast_buffer_t(available_threads_.size() * metric_.bytes_per_vector());
|
|
1155
1144
|
if (!cast_buffer_)
|
|
1156
1145
|
return result.failed("Failed to allocate memory for the casts");
|
|
1157
|
-
casts_ =
|
|
1146
|
+
casts_ = casts_punned_t::make(head.kind_scalar);
|
|
1158
1147
|
}
|
|
1159
1148
|
|
|
1160
1149
|
// Pull the actual proximity graph
|
|
@@ -1266,7 +1255,7 @@ class index_dense_gt {
|
|
|
1266
1255
|
cast_buffer_ = cast_buffer_t(available_threads_.size() * metric_.bytes_per_vector());
|
|
1267
1256
|
if (!cast_buffer_)
|
|
1268
1257
|
return result.failed("Failed to allocate memory for the casts");
|
|
1269
|
-
casts_ =
|
|
1258
|
+
casts_ = casts_punned_t::make(head.kind_scalar);
|
|
1270
1259
|
offset += sizeof(buffer);
|
|
1271
1260
|
}
|
|
1272
1261
|
|
|
@@ -1474,6 +1463,8 @@ class index_dense_gt {
|
|
|
1474
1463
|
labeling_result_t remove(vector_key_t key) {
|
|
1475
1464
|
usearch_assert_m(config().enable_key_lookups, "Key lookups are disabled");
|
|
1476
1465
|
labeling_result_t result;
|
|
1466
|
+
if (typed_->is_immutable())
|
|
1467
|
+
return result.failed("Can't remove from an immutable index");
|
|
1477
1468
|
|
|
1478
1469
|
unique_lock_t lookup_lock(slot_lookup_mutex_);
|
|
1479
1470
|
auto matching_slots = slot_lookup_.equal_range(key_and_slot_t::any_slot(key));
|
|
@@ -1994,7 +1985,7 @@ class index_dense_gt {
|
|
|
1994
1985
|
template <typename scalar_at>
|
|
1995
1986
|
add_result_t add_( //
|
|
1996
1987
|
vector_key_t key, scalar_at const* vector, //
|
|
1997
|
-
std::size_t thread, bool force_vector_copy,
|
|
1988
|
+
std::size_t thread, bool force_vector_copy, cast_punned_t const& cast) {
|
|
1998
1989
|
|
|
1999
1990
|
if (!multi() && config().enable_key_lookups && contains(key))
|
|
2000
1991
|
return add_result_t{}.failed("Duplicate keys not allowed in high-level wrappers");
|
|
@@ -2044,7 +2035,7 @@ class index_dense_gt {
|
|
|
2044
2035
|
|
|
2045
2036
|
template <typename scalar_at, typename predicate_at>
|
|
2046
2037
|
search_result_t search_(scalar_at const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread,
|
|
2047
|
-
bool exact,
|
|
2038
|
+
bool exact, cast_punned_t const& cast) const {
|
|
2048
2039
|
|
|
2049
2040
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2050
2041
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2080,7 +2071,7 @@ class index_dense_gt {
|
|
|
2080
2071
|
template <typename scalar_at>
|
|
2081
2072
|
cluster_result_t cluster_( //
|
|
2082
2073
|
scalar_at const* vector, std::size_t level, //
|
|
2083
|
-
std::size_t thread,
|
|
2074
|
+
std::size_t thread, cast_punned_t const& cast) const {
|
|
2084
2075
|
|
|
2085
2076
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2086
2077
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2104,7 +2095,7 @@ class index_dense_gt {
|
|
|
2104
2095
|
template <typename scalar_at>
|
|
2105
2096
|
aggregated_distances_t distance_between_( //
|
|
2106
2097
|
vector_key_t key, scalar_at const* vector, //
|
|
2107
|
-
std::size_t thread,
|
|
2098
|
+
std::size_t thread, cast_punned_t const& cast) const {
|
|
2108
2099
|
|
|
2109
2100
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2110
2101
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2181,7 +2172,8 @@ class index_dense_gt {
|
|
|
2181
2172
|
}
|
|
2182
2173
|
|
|
2183
2174
|
template <typename scalar_at>
|
|
2184
|
-
std::size_t get_(vector_key_t key, scalar_at* reconstructed, std::size_t vectors_limit,
|
|
2175
|
+
std::size_t get_(vector_key_t key, scalar_at* reconstructed, std::size_t vectors_limit,
|
|
2176
|
+
cast_punned_t const& cast) const {
|
|
2185
2177
|
|
|
2186
2178
|
if (!multi()) {
|
|
2187
2179
|
compressed_slot_t slot;
|
|
@@ -2216,36 +2208,6 @@ class index_dense_gt {
|
|
|
2216
2208
|
return count_exported;
|
|
2217
2209
|
}
|
|
2218
2210
|
}
|
|
2219
|
-
|
|
2220
|
-
template <typename to_scalar_at> static casts_t make_casts_() {
|
|
2221
|
-
casts_t result;
|
|
2222
|
-
|
|
2223
|
-
result.from_b1x8 = &cast_gt<b1x8_t, to_scalar_at>::try_;
|
|
2224
|
-
result.from_i8 = &cast_gt<i8_t, to_scalar_at>::try_;
|
|
2225
|
-
result.from_f16 = &cast_gt<f16_t, to_scalar_at>::try_;
|
|
2226
|
-
result.from_f32 = &cast_gt<f32_t, to_scalar_at>::try_;
|
|
2227
|
-
result.from_f64 = &cast_gt<f64_t, to_scalar_at>::try_;
|
|
2228
|
-
|
|
2229
|
-
result.to_b1x8 = &cast_gt<to_scalar_at, b1x8_t>::try_;
|
|
2230
|
-
result.to_i8 = &cast_gt<to_scalar_at, i8_t>::try_;
|
|
2231
|
-
result.to_f16 = &cast_gt<to_scalar_at, f16_t>::try_;
|
|
2232
|
-
result.to_f32 = &cast_gt<to_scalar_at, f32_t>::try_;
|
|
2233
|
-
result.to_f64 = &cast_gt<to_scalar_at, f64_t>::try_;
|
|
2234
|
-
|
|
2235
|
-
return result;
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
static casts_t make_casts_(scalar_kind_t scalar_kind) {
|
|
2239
|
-
switch (scalar_kind) {
|
|
2240
|
-
case scalar_kind_t::f64_k: return make_casts_<f64_t>();
|
|
2241
|
-
case scalar_kind_t::f32_k: return make_casts_<f32_t>();
|
|
2242
|
-
case scalar_kind_t::f16_k: return make_casts_<f16_t>();
|
|
2243
|
-
case scalar_kind_t::bf16_k: return make_casts_<bf16_t>();
|
|
2244
|
-
case scalar_kind_t::i8_k: return make_casts_<i8_t>();
|
|
2245
|
-
case scalar_kind_t::b1x8_k: return make_casts_<b1x8_t>();
|
|
2246
|
-
default: return {};
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
2211
|
};
|
|
2250
2212
|
|
|
2251
2213
|
using index_dense_t = index_dense_gt<>;
|
|
@@ -2292,4 +2254,4 @@ static join_result_t join( //
|
|
|
2292
2254
|
}
|
|
2293
2255
|
|
|
2294
2256
|
} // namespace usearch
|
|
2295
|
-
} // namespace unum
|
|
2257
|
+
} // namespace unum
|