usearch 2.15.1 → 2.15.3
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 +2 -2
- package/binding.gyp +1 -1
- package/c/CMakeLists.txt +10 -3
- package/cpp/README.md +1 -1
- package/cpp/bench.cpp +2 -2
- package/include/usearch/index.hpp +27 -13
- package/include/usearch/index_dense.hpp +45 -88
- package/include/usearch/index_plugins.hpp +128 -40
- package/javascript/usearch.test.js +15 -13
- 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/CODE_OF_CONDUCT.md +128 -0
- package/simsimd/CONTRIBUTING.md +18 -8
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/MANIFEST.in +2 -0
- package/simsimd/README.md +446 -158
- package/simsimd/build.rs +11 -1
- package/simsimd/c/lib.c +172 -40
- package/simsimd/include/simsimd/binary.h +11 -11
- package/simsimd/include/simsimd/curved.h +804 -0
- package/simsimd/include/simsimd/dot.h +333 -334
- package/simsimd/include/simsimd/geospatial.h +9 -3
- package/simsimd/include/simsimd/mesh.h +69 -0
- package/simsimd/include/simsimd/probability.h +178 -150
- package/simsimd/include/simsimd/simsimd.h +581 -107
- package/simsimd/include/simsimd/sparse.h +807 -0
- package/simsimd/include/simsimd/spatial.h +737 -502
- package/simsimd/include/simsimd/types.h +145 -75
- package/simsimd/javascript/simsimd.ts +4 -4
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +165 -0
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ Spatial • Binary • Probabilistic • User-Defined Metrics
|
|
|
32
32
|
<a href="https://unum-cloud.github.io/usearch/golang">GoLang</a> •
|
|
33
33
|
<a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
|
|
34
34
|
<br/>
|
|
35
|
-
Linux • MacOS • Windows • iOS • WebAssembly •
|
|
35
|
+
Linux • MacOS • Windows • iOS • Android • WebAssembly •
|
|
36
36
|
<a href="https://unum-cloud.github.io/usearch/sqlite">SQLite3</a>
|
|
37
37
|
</p>
|
|
38
38
|
|
|
@@ -544,7 +544,7 @@ doi = {10.5281/zenodo.7949416},
|
|
|
544
544
|
author = {Vardanian, Ash},
|
|
545
545
|
title = {{USearch by Unum Cloud}},
|
|
546
546
|
url = {https://github.com/unum-cloud/usearch},
|
|
547
|
-
version = {2.15.
|
|
547
|
+
version = {2.15.3},
|
|
548
548
|
year = {2023},
|
|
549
549
|
month = oct,
|
|
550
550
|
}
|
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_; }
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#define USEARCH_VERSION_MAJOR 2
|
|
11
11
|
#define USEARCH_VERSION_MINOR 15
|
|
12
|
-
#define USEARCH_VERSION_PATCH
|
|
12
|
+
#define USEARCH_VERSION_PATCH 3
|
|
13
13
|
|
|
14
14
|
// Inferring C++ version
|
|
15
15
|
// https://stackoverflow.com/a/61552074
|
|
@@ -27,6 +27,9 @@
|
|
|
27
27
|
#define USEARCH_DEFINED_APPLE
|
|
28
28
|
#elif defined(__linux__)
|
|
29
29
|
#define USEARCH_DEFINED_LINUX
|
|
30
|
+
#if defined(__ANDROID_API__)
|
|
31
|
+
#define USEARCH_DEFINED_ANDROID
|
|
32
|
+
#endif
|
|
30
33
|
#endif
|
|
31
34
|
|
|
32
35
|
// Inferring the compiler: Clang vs GCC
|
|
@@ -934,18 +937,6 @@ class sorted_buffer_gt {
|
|
|
934
937
|
return true;
|
|
935
938
|
}
|
|
936
939
|
|
|
937
|
-
inline bool insert_sorted(element_t const* elements, std::size_t elements_count, std::size_t limit) noexcept {
|
|
938
|
-
if (!size_) {
|
|
939
|
-
}
|
|
940
|
-
// If we are inserting elements, we only perform full-scale binary search once,
|
|
941
|
-
// and then only compute successive insertion offsets based only on the tail.
|
|
942
|
-
std::size_t slot = std::lower_bound(elements_, elements_ + size_, elements[0], &less) - elements_;
|
|
943
|
-
if (slot == limit)
|
|
944
|
-
return false;
|
|
945
|
-
std::size_t to_move = size_ - slot - (size_ == limit);
|
|
946
|
-
std::size_t next_slot = std::lower_bound(elements_ + slot, elements_ + size_, elements[1], &less) - elements_;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
940
|
inline element_t pop() noexcept {
|
|
950
941
|
size_--;
|
|
951
942
|
element_t result = elements_[size_];
|
|
@@ -2616,6 +2607,16 @@ class index_gt {
|
|
|
2616
2607
|
node_t node = nodes_[candidate.slot];
|
|
2617
2608
|
return {member_cref_t{node.ckey(), candidate.slot}, candidate.distance};
|
|
2618
2609
|
}
|
|
2610
|
+
|
|
2611
|
+
/**
|
|
2612
|
+
* @brief Extracts the search results into a user-provided buffer, that unlike `dump_to`,
|
|
2613
|
+
* may already contain some data, so the new and old results are merged together.
|
|
2614
|
+
* @return The number of results stored in the buffer.
|
|
2615
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2616
|
+
* @param[in] distances The buffer to store the distances to the search results.
|
|
2617
|
+
* @param[in] old_count The number of results already stored in the buffers.
|
|
2618
|
+
* @param[in] max_count The maximum number of results that can be stored in the buffers.
|
|
2619
|
+
*/
|
|
2619
2620
|
inline std::size_t merge_into( //
|
|
2620
2621
|
vector_key_t* keys, distance_t* distances, //
|
|
2621
2622
|
std::size_t old_count, std::size_t max_count) const noexcept {
|
|
@@ -2637,6 +2638,13 @@ class index_gt {
|
|
|
2637
2638
|
}
|
|
2638
2639
|
return merged_count;
|
|
2639
2640
|
}
|
|
2641
|
+
|
|
2642
|
+
/**
|
|
2643
|
+
* @brief Extracts the search results into a user-provided buffer.
|
|
2644
|
+
* @return The number of results stored in the buffer.
|
|
2645
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2646
|
+
* @param[in] distances The buffer to store the distances to the search results.
|
|
2647
|
+
*/
|
|
2640
2648
|
inline std::size_t dump_to(vector_key_t* keys, distance_t* distances) const noexcept {
|
|
2641
2649
|
for (std::size_t i = 0; i != count; ++i) {
|
|
2642
2650
|
match_t result = operator[](i);
|
|
@@ -2645,6 +2653,12 @@ class index_gt {
|
|
|
2645
2653
|
}
|
|
2646
2654
|
return count;
|
|
2647
2655
|
}
|
|
2656
|
+
|
|
2657
|
+
/**
|
|
2658
|
+
* @brief Extracts the search results into a user-provided buffer.
|
|
2659
|
+
* @return The number of results stored in the buffer.
|
|
2660
|
+
* @param[in] keys The buffer to store the keys of the search results.
|
|
2661
|
+
*/
|
|
2648
2662
|
inline std::size_t dump_to(vector_key_t* keys) const noexcept {
|
|
2649
2663
|
for (std::size_t i = 0; i != count; ++i) {
|
|
2650
2664
|
match_t result = operator[](i);
|
|
@@ -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
|
|
|
@@ -767,41 +753,41 @@ class index_dense_gt {
|
|
|
767
753
|
};
|
|
768
754
|
|
|
769
755
|
// 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_.
|
|
756
|
+
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); }
|
|
757
|
+
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); }
|
|
758
|
+
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); }
|
|
759
|
+
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); }
|
|
760
|
+
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); }
|
|
761
|
+
|
|
762
|
+
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); }
|
|
763
|
+
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); }
|
|
764
|
+
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); }
|
|
765
|
+
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); }
|
|
766
|
+
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); }
|
|
767
|
+
|
|
768
|
+
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); }
|
|
769
|
+
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); }
|
|
770
|
+
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); }
|
|
771
|
+
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); }
|
|
772
|
+
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); }
|
|
773
|
+
|
|
774
|
+
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); }
|
|
775
|
+
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); }
|
|
776
|
+
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); }
|
|
777
|
+
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); }
|
|
778
|
+
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); }
|
|
779
|
+
|
|
780
|
+
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); }
|
|
781
|
+
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); }
|
|
782
|
+
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); }
|
|
783
|
+
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); }
|
|
784
|
+
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); }
|
|
785
|
+
|
|
786
|
+
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); }
|
|
787
|
+
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); }
|
|
788
|
+
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); }
|
|
789
|
+
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); }
|
|
790
|
+
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
791
|
// clang-format on
|
|
806
792
|
|
|
807
793
|
/**
|
|
@@ -1154,7 +1140,7 @@ class index_dense_gt {
|
|
|
1154
1140
|
cast_buffer_ = cast_buffer_t(available_threads_.size() * metric_.bytes_per_vector());
|
|
1155
1141
|
if (!cast_buffer_)
|
|
1156
1142
|
return result.failed("Failed to allocate memory for the casts");
|
|
1157
|
-
casts_ =
|
|
1143
|
+
casts_ = casts_punned_t::make(head.kind_scalar);
|
|
1158
1144
|
}
|
|
1159
1145
|
|
|
1160
1146
|
// Pull the actual proximity graph
|
|
@@ -1266,7 +1252,7 @@ class index_dense_gt {
|
|
|
1266
1252
|
cast_buffer_ = cast_buffer_t(available_threads_.size() * metric_.bytes_per_vector());
|
|
1267
1253
|
if (!cast_buffer_)
|
|
1268
1254
|
return result.failed("Failed to allocate memory for the casts");
|
|
1269
|
-
casts_ =
|
|
1255
|
+
casts_ = casts_punned_t::make(head.kind_scalar);
|
|
1270
1256
|
offset += sizeof(buffer);
|
|
1271
1257
|
}
|
|
1272
1258
|
|
|
@@ -1994,7 +1980,7 @@ class index_dense_gt {
|
|
|
1994
1980
|
template <typename scalar_at>
|
|
1995
1981
|
add_result_t add_( //
|
|
1996
1982
|
vector_key_t key, scalar_at const* vector, //
|
|
1997
|
-
std::size_t thread, bool force_vector_copy,
|
|
1983
|
+
std::size_t thread, bool force_vector_copy, cast_punned_t const& cast) {
|
|
1998
1984
|
|
|
1999
1985
|
if (!multi() && config().enable_key_lookups && contains(key))
|
|
2000
1986
|
return add_result_t{}.failed("Duplicate keys not allowed in high-level wrappers");
|
|
@@ -2044,7 +2030,7 @@ class index_dense_gt {
|
|
|
2044
2030
|
|
|
2045
2031
|
template <typename scalar_at, typename predicate_at>
|
|
2046
2032
|
search_result_t search_(scalar_at const* vector, std::size_t wanted, predicate_at&& predicate, std::size_t thread,
|
|
2047
|
-
bool exact,
|
|
2033
|
+
bool exact, cast_punned_t const& cast) const {
|
|
2048
2034
|
|
|
2049
2035
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2050
2036
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2080,7 +2066,7 @@ class index_dense_gt {
|
|
|
2080
2066
|
template <typename scalar_at>
|
|
2081
2067
|
cluster_result_t cluster_( //
|
|
2082
2068
|
scalar_at const* vector, std::size_t level, //
|
|
2083
|
-
std::size_t thread,
|
|
2069
|
+
std::size_t thread, cast_punned_t const& cast) const {
|
|
2084
2070
|
|
|
2085
2071
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2086
2072
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2104,7 +2090,7 @@ class index_dense_gt {
|
|
|
2104
2090
|
template <typename scalar_at>
|
|
2105
2091
|
aggregated_distances_t distance_between_( //
|
|
2106
2092
|
vector_key_t key, scalar_at const* vector, //
|
|
2107
|
-
std::size_t thread,
|
|
2093
|
+
std::size_t thread, cast_punned_t const& cast) const {
|
|
2108
2094
|
|
|
2109
2095
|
// Cast the vector, if needed for compatibility with `metric_`
|
|
2110
2096
|
thread_lock_t lock = thread_lock_(thread);
|
|
@@ -2181,7 +2167,8 @@ class index_dense_gt {
|
|
|
2181
2167
|
}
|
|
2182
2168
|
|
|
2183
2169
|
template <typename scalar_at>
|
|
2184
|
-
std::size_t get_(vector_key_t key, scalar_at* reconstructed, std::size_t vectors_limit,
|
|
2170
|
+
std::size_t get_(vector_key_t key, scalar_at* reconstructed, std::size_t vectors_limit,
|
|
2171
|
+
cast_punned_t const& cast) const {
|
|
2185
2172
|
|
|
2186
2173
|
if (!multi()) {
|
|
2187
2174
|
compressed_slot_t slot;
|
|
@@ -2216,36 +2203,6 @@ class index_dense_gt {
|
|
|
2216
2203
|
return count_exported;
|
|
2217
2204
|
}
|
|
2218
2205
|
}
|
|
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
2206
|
};
|
|
2250
2207
|
|
|
2251
2208
|
using index_dense_t = index_dense_gt<>;
|
|
@@ -42,20 +42,38 @@
|
|
|
42
42
|
|
|
43
43
|
#if USEARCH_USE_SIMSIMD
|
|
44
44
|
// Propagate the `f16` settings
|
|
45
|
-
#if
|
|
46
|
-
#
|
|
45
|
+
#if defined(USEARCH_CAN_COMPILE_FP16) || defined(USEARCH_CAN_COMPILE_FLOAT16)
|
|
46
|
+
#if USEARCH_CAN_COMPILE_FP16 || USEARCH_CAN_COMPILE_FLOAT16
|
|
47
|
+
#define SIMSIMD_NATIVE_F16 1
|
|
48
|
+
#else
|
|
49
|
+
#define SIMSIMD_NATIVE_F16 0
|
|
50
|
+
#endif
|
|
51
|
+
#endif
|
|
52
|
+
// Propagate the `bf16` settings
|
|
53
|
+
#if defined(USEARCH_CAN_COMPILE_BF16) || defined(USEARCH_CAN_COMPILE_BFLOAT16)
|
|
54
|
+
#if USEARCH_CAN_COMPILE_BF16 || USEARCH_CAN_COMPILE_BFLOAT16
|
|
55
|
+
#define SIMSIMD_NATIVE_BF16 1
|
|
56
|
+
#else
|
|
57
|
+
#define SIMSIMD_NATIVE_BF16 0
|
|
58
|
+
#endif
|
|
47
59
|
#endif
|
|
48
|
-
|
|
49
|
-
// Overwrite the dynamic dispatch settings
|
|
50
|
-
#undef SIMSIMD_DYNAMIC_DISPATCH
|
|
51
|
-
#define SIMSIMD_DYNAMIC_DISPATCH 0
|
|
52
60
|
// No problem, if some of the functions are unused or undefined
|
|
53
61
|
#pragma GCC diagnostic push
|
|
62
|
+
#pragma GCC diagnostic ignored "-Wall"
|
|
63
|
+
#pragma GCC diagnostic ignored "-Wunused"
|
|
54
64
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
65
|
+
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
66
|
+
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
67
|
+
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
68
|
+
#ifdef _MSC_VER
|
|
55
69
|
#pragma warning(push)
|
|
56
|
-
#pragma warning(disable : 4101)
|
|
70
|
+
#pragma warning(disable : 4101) // "Unused variables"
|
|
71
|
+
#pragma warning(disable : 4068) // "Unknown pragmas", when MSVC tries to read GCC pragmas
|
|
72
|
+
#endif // _MSC_VER
|
|
57
73
|
#include <simsimd/simsimd.h>
|
|
74
|
+
#ifdef _MSC_VER
|
|
58
75
|
#pragma warning(pop)
|
|
76
|
+
#endif // _MSC_VER
|
|
59
77
|
#pragma GCC diagnostic pop
|
|
60
78
|
#endif
|
|
61
79
|
|
|
@@ -380,9 +398,10 @@ inline float f16_to_f32(std::uint16_t u16) noexcept {
|
|
|
380
398
|
#if USEARCH_USE_FP16LIB
|
|
381
399
|
return fp16_ieee_to_fp32_value(u16);
|
|
382
400
|
#elif USEARCH_USE_SIMSIMD
|
|
383
|
-
return
|
|
401
|
+
return simsimd_f16_to_f32((simsimd_f16_t const*)&u16);
|
|
384
402
|
#else
|
|
385
|
-
|
|
403
|
+
#warning "It's recommended to use SimSIMD and fp16lib for half-precision numerics"
|
|
404
|
+
_Float16 f16;
|
|
386
405
|
std::memcpy(&f16, &u16, sizeof(std::uint16_t));
|
|
387
406
|
return float(f16);
|
|
388
407
|
#endif
|
|
@@ -395,9 +414,12 @@ inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
|
395
414
|
#if USEARCH_USE_FP16LIB
|
|
396
415
|
return fp16_ieee_from_fp32_value(f32);
|
|
397
416
|
#elif USEARCH_USE_SIMSIMD
|
|
398
|
-
|
|
417
|
+
std::uint16_t result;
|
|
418
|
+
simsimd_f32_to_f16(f32, (simsimd_f16_t*)&result);
|
|
419
|
+
return result;
|
|
399
420
|
#else
|
|
400
|
-
|
|
421
|
+
#warning "It's recommended to use SimSIMD and fp16lib for half-precision numerics"
|
|
422
|
+
_Float16 f16 = _Float16(f32);
|
|
401
423
|
std::uint16_t u16;
|
|
402
424
|
std::memcpy(&u16, &f16, sizeof(std::uint16_t));
|
|
403
425
|
return u16;
|
|
@@ -410,15 +432,14 @@ inline std::uint16_t f32_to_f16(float f32) noexcept {
|
|
|
410
432
|
*/
|
|
411
433
|
inline float bf16_to_f32(std::uint16_t u16) noexcept {
|
|
412
434
|
#if USEARCH_USE_SIMSIMD
|
|
413
|
-
return
|
|
435
|
+
return simsimd_bf16_to_f32((simsimd_bf16_t const*)&u16);
|
|
414
436
|
#else
|
|
415
437
|
union float_or_unsigned_int_t {
|
|
416
438
|
float f;
|
|
417
439
|
unsigned int i;
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return result_union.f;
|
|
440
|
+
} conv;
|
|
441
|
+
conv.i = u16 << 16; // Zero extends the mantissa
|
|
442
|
+
return conv.f;
|
|
422
443
|
#endif
|
|
423
444
|
}
|
|
424
445
|
|
|
@@ -428,17 +449,18 @@ inline float bf16_to_f32(std::uint16_t u16) noexcept {
|
|
|
428
449
|
*/
|
|
429
450
|
inline std::uint16_t f32_to_bf16(float f32) noexcept {
|
|
430
451
|
#if USEARCH_USE_SIMSIMD
|
|
431
|
-
|
|
452
|
+
std::uint16_t result;
|
|
453
|
+
simsimd_f32_to_bf16(f32, (simsimd_bf16_t*)&result);
|
|
454
|
+
return result;
|
|
432
455
|
#else
|
|
433
456
|
union float_or_unsigned_int_t {
|
|
434
457
|
float f;
|
|
435
458
|
unsigned int i;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
return (unsigned short)value.i;
|
|
459
|
+
} conv;
|
|
460
|
+
conv.f = f32;
|
|
461
|
+
conv.i >>= 16;
|
|
462
|
+
conv.i &= 0xFFFF;
|
|
463
|
+
return (unsigned short)conv.i;
|
|
442
464
|
#endif
|
|
443
465
|
}
|
|
444
466
|
|
|
@@ -774,9 +796,9 @@ class aligned_allocator_gt {
|
|
|
774
796
|
std::size_t alignment = alignment_ak;
|
|
775
797
|
#if defined(USEARCH_DEFINED_WINDOWS)
|
|
776
798
|
return (pointer)_aligned_malloc(length_bytes, alignment);
|
|
777
|
-
#elif defined(USEARCH_DEFINED_APPLE)
|
|
799
|
+
#elif defined(USEARCH_DEFINED_APPLE) || defined(USEARCH_DEFINED_ANDROID)
|
|
778
800
|
// Apple Clang keeps complaining that `aligned_alloc` is only available
|
|
779
|
-
// with macOS 10.15 and newer, so let's use `posix_memalign` there.
|
|
801
|
+
// with macOS 10.15 and newer or Android API >= 28, so let's use `posix_memalign` there.
|
|
780
802
|
void* result = nullptr;
|
|
781
803
|
int status = posix_memalign(&result, alignment, length_bytes);
|
|
782
804
|
return status == 0 ? (pointer)result : nullptr;
|
|
@@ -1183,6 +1205,71 @@ template <> struct cast_gt<f64_t, b1x8_t> : public cast_to_b1x8_gt<f64_t> {};
|
|
|
1183
1205
|
template <> struct cast_gt<b1x8_t, i8_t> : public cast_from_b1x8_gt<i8_t> {};
|
|
1184
1206
|
template <> struct cast_gt<i8_t, b1x8_t> : public cast_to_b1x8_gt<i8_t> {};
|
|
1185
1207
|
|
|
1208
|
+
/**
|
|
1209
|
+
* @brief Type-punned array casting function.
|
|
1210
|
+
* Arguments: input buffer, bytes in input buffer, output buffer.
|
|
1211
|
+
* Returns `true` if the casting was performed successfully, `false` otherwise.
|
|
1212
|
+
*/
|
|
1213
|
+
using cast_punned_t = bool (*)(byte_t const*, std::size_t, byte_t*);
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* @brief A collection of casting functions for typical vector types.
|
|
1217
|
+
* Covers to/from conversions for boolean, integer, half-precision,
|
|
1218
|
+
* single-precision, and double-precision scalars.
|
|
1219
|
+
*/
|
|
1220
|
+
struct casts_punned_t {
|
|
1221
|
+
struct group_t {
|
|
1222
|
+
cast_punned_t b1x8{};
|
|
1223
|
+
cast_punned_t i8{};
|
|
1224
|
+
cast_punned_t f16{};
|
|
1225
|
+
cast_punned_t f32{};
|
|
1226
|
+
cast_punned_t f64{};
|
|
1227
|
+
|
|
1228
|
+
cast_punned_t operator[](scalar_kind_t scalar_kind) const noexcept {
|
|
1229
|
+
switch (scalar_kind) {
|
|
1230
|
+
case scalar_kind_t::f64_k: return f64;
|
|
1231
|
+
case scalar_kind_t::f32_k: return f32;
|
|
1232
|
+
case scalar_kind_t::f16_k: return f16;
|
|
1233
|
+
case scalar_kind_t::bf16_k: return f16;
|
|
1234
|
+
case scalar_kind_t::i8_k: return i8;
|
|
1235
|
+
case scalar_kind_t::b1x8_k: return b1x8;
|
|
1236
|
+
default: return nullptr;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
} from, to;
|
|
1241
|
+
|
|
1242
|
+
template <typename scalar_at> static casts_punned_t make() noexcept {
|
|
1243
|
+
casts_punned_t result;
|
|
1244
|
+
|
|
1245
|
+
result.from.b1x8 = &cast_gt<b1x8_t, scalar_at>::try_;
|
|
1246
|
+
result.from.i8 = &cast_gt<i8_t, scalar_at>::try_;
|
|
1247
|
+
result.from.f16 = &cast_gt<f16_t, scalar_at>::try_;
|
|
1248
|
+
result.from.f32 = &cast_gt<f32_t, scalar_at>::try_;
|
|
1249
|
+
result.from.f64 = &cast_gt<f64_t, scalar_at>::try_;
|
|
1250
|
+
|
|
1251
|
+
result.to.b1x8 = &cast_gt<scalar_at, b1x8_t>::try_;
|
|
1252
|
+
result.to.i8 = &cast_gt<scalar_at, i8_t>::try_;
|
|
1253
|
+
result.to.f16 = &cast_gt<scalar_at, f16_t>::try_;
|
|
1254
|
+
result.to.f32 = &cast_gt<scalar_at, f32_t>::try_;
|
|
1255
|
+
result.to.f64 = &cast_gt<scalar_at, f64_t>::try_;
|
|
1256
|
+
|
|
1257
|
+
return result;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
static casts_punned_t make(scalar_kind_t scalar_kind) noexcept {
|
|
1261
|
+
switch (scalar_kind) {
|
|
1262
|
+
case scalar_kind_t::f64_k: return casts_punned_t::make<f64_t>();
|
|
1263
|
+
case scalar_kind_t::f32_k: return casts_punned_t::make<f32_t>();
|
|
1264
|
+
case scalar_kind_t::f16_k: return casts_punned_t::make<f16_t>();
|
|
1265
|
+
case scalar_kind_t::bf16_k: return casts_punned_t::make<bf16_t>();
|
|
1266
|
+
case scalar_kind_t::i8_k: return casts_punned_t::make<i8_t>();
|
|
1267
|
+
case scalar_kind_t::b1x8_k: return casts_punned_t::make<b1x8_t>();
|
|
1268
|
+
default: return {};
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1186
1273
|
/* Don't complain if the vectorization of the inner loops fails:
|
|
1187
1274
|
*
|
|
1188
1275
|
* > warning: loop not vectorized: the optimizer was unable to perform the requested transformation;
|
|
@@ -1910,32 +1997,33 @@ class metric_punned_t {
|
|
|
1910
1997
|
* @brief View over a potentially-strided memory buffer, containing a row-major matrix.
|
|
1911
1998
|
*/
|
|
1912
1999
|
template <typename scalar_at> //
|
|
1913
|
-
class
|
|
2000
|
+
class matrix_slice_gt {
|
|
1914
2001
|
using scalar_t = scalar_at;
|
|
2002
|
+
using byte_addressable_t = typename std::conditional<std::is_const<scalar_t>::value, byte_t const, byte_t>::type;
|
|
1915
2003
|
|
|
1916
|
-
scalar_t
|
|
2004
|
+
scalar_t* begin_{};
|
|
1917
2005
|
std::size_t dimensions_{};
|
|
1918
2006
|
std::size_t count_{};
|
|
1919
2007
|
std::size_t stride_bytes_{};
|
|
1920
2008
|
|
|
1921
2009
|
public:
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2010
|
+
matrix_slice_gt() noexcept = default;
|
|
2011
|
+
matrix_slice_gt(matrix_slice_gt const&) noexcept = default;
|
|
2012
|
+
matrix_slice_gt& operator=(matrix_slice_gt const&) noexcept = default;
|
|
1925
2013
|
|
|
1926
|
-
|
|
1927
|
-
:
|
|
2014
|
+
matrix_slice_gt(scalar_t* begin, std::size_t dimensions, std::size_t count = 1) noexcept
|
|
2015
|
+
: matrix_slice_gt(begin, dimensions, count, dimensions * sizeof(scalar_at)) {}
|
|
1928
2016
|
|
|
1929
|
-
|
|
2017
|
+
matrix_slice_gt(scalar_t* begin, std::size_t dimensions, std::size_t count, std::size_t stride_bytes) noexcept
|
|
1930
2018
|
: begin_(begin), dimensions_(dimensions), count_(count), stride_bytes_(stride_bytes) {}
|
|
1931
2019
|
|
|
1932
2020
|
explicit operator bool() const noexcept { return begin_; }
|
|
1933
2021
|
std::size_t size() const noexcept { return count_; }
|
|
1934
2022
|
std::size_t dimensions() const noexcept { return dimensions_; }
|
|
1935
2023
|
std::size_t stride() const noexcept { return stride_bytes_; }
|
|
1936
|
-
scalar_t
|
|
1937
|
-
scalar_t
|
|
1938
|
-
return reinterpret_cast<scalar_t
|
|
2024
|
+
scalar_t* data() const noexcept { return begin_; }
|
|
2025
|
+
scalar_t* at(std::size_t i) const noexcept {
|
|
2026
|
+
return reinterpret_cast<scalar_t*>(reinterpret_cast<byte_addressable_t*>(begin_) + i * stride_bytes_);
|
|
1939
2027
|
}
|
|
1940
2028
|
};
|
|
1941
2029
|
|
|
@@ -1944,7 +2032,7 @@ struct exact_offset_and_distance_t {
|
|
|
1944
2032
|
f32_t distance;
|
|
1945
2033
|
};
|
|
1946
2034
|
|
|
1947
|
-
using exact_search_results_t =
|
|
2035
|
+
using exact_search_results_t = matrix_slice_gt<exact_offset_and_distance_t>;
|
|
1948
2036
|
|
|
1949
2037
|
/**
|
|
1950
2038
|
* @brief Helper-structure for exact search operations.
|
|
@@ -1965,9 +2053,9 @@ class exact_search_t {
|
|
|
1965
2053
|
|
|
1966
2054
|
public:
|
|
1967
2055
|
template <typename scalar_at, typename executor_at = dummy_executor_t, typename progress_at = dummy_progress_t>
|
|
1968
|
-
exact_search_results_t operator()(
|
|
1969
|
-
|
|
1970
|
-
std::size_t wanted, metric_punned_t const& metric,
|
|
2056
|
+
exact_search_results_t operator()( //
|
|
2057
|
+
matrix_slice_gt<scalar_at const> dataset, matrix_slice_gt<scalar_at const> queries, //
|
|
2058
|
+
std::size_t wanted, metric_punned_t const& metric, //
|
|
1971
2059
|
executor_at&& executor = executor_at{}, progress_at&& progress = progress_at{}) {
|
|
1972
2060
|
return operator()( //
|
|
1973
2061
|
metric, //
|