usearch 1.2.2 → 2.0.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 +71 -0
- package/.github/workflows/release.yml +77 -2
- package/.github/workflows/update_version.sh +1 -0
- package/.gitignore +11 -1
- package/.vscode/settings.json +5 -1
- package/CMakeLists.txt +1 -0
- package/Cargo.toml +1 -1
- package/README.md +31 -18
- package/c/lib.cpp +38 -4
- package/c/test.c +117 -114
- package/c/usearch.h +28 -1
- package/conanfile.py +1 -1
- package/cpp/test.cpp +2 -2
- package/csharp/.editorconfig +361 -0
- package/csharp/Cloud.Unum.USearch.sln +34 -0
- package/csharp/DEV_SETUP.md +51 -0
- package/csharp/Directory.Build.props +29 -0
- package/csharp/README.md +77 -0
- package/csharp/build_and_test.cmd +31 -0
- package/csharp/build_and_test.sh +30 -0
- package/csharp/eng/build_lib.sh +65 -0
- package/csharp/eng/clean.sh +12 -0
- package/csharp/nuget/NUGET.md +11 -0
- package/csharp/nuget/nuget-package.props +33 -0
- package/csharp/pack.sh +56 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/Cloud.Unum.USearch.Tests.csproj +39 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/InitTests.cs +306 -0
- package/csharp/src/Cloud.Unum.USearch.Tests/Usings.cs +1 -0
- package/docs/benchmarks.md +6 -6
- package/docs/compilation.md +1 -1
- package/include/usearch/index.hpp +172 -52
- package/include/usearch/index_dense.hpp +201 -64
- package/include/usearch/index_plugins.hpp +90 -69
- package/package.json +2 -2
- package/python/lib.cpp +99 -37
- package/python/scripts/test_index.py +38 -10
- package/python/usearch/index.py +87 -37
- package/rust/README.md +2 -2
- package/rust/lib.cpp +42 -19
- package/rust/lib.hpp +14 -0
- package/rust/lib.rs +85 -43
- package/wasmer.toml +1 -1
|
@@ -154,10 +154,10 @@ struct index_dense_metadata_result_t {
|
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
* @brief Extracts metadata from pre-constructed index on disk,
|
|
157
|
+
* @brief Extracts metadata from a pre-constructed index on disk,
|
|
158
158
|
* without loading it or mapping the whole binary file.
|
|
159
159
|
*/
|
|
160
|
-
inline index_dense_metadata_result_t
|
|
160
|
+
inline index_dense_metadata_result_t index_dense_metadata_from_path(char const* file_path) noexcept {
|
|
161
161
|
index_dense_metadata_result_t result;
|
|
162
162
|
std::unique_ptr<std::FILE, int (*)(std::FILE*)> file(std::fopen(file_path, "rb"), &std::fclose);
|
|
163
163
|
if (!file)
|
|
@@ -219,6 +219,56 @@ inline index_dense_metadata_result_t index_dense_metadata(char const* file_path)
|
|
|
219
219
|
return result.failed("Not a dense USearch index!");
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* @brief Extracts metadata from a pre-constructed index serialized into an in-memory buffer.
|
|
224
|
+
*/
|
|
225
|
+
inline index_dense_metadata_result_t index_dense_metadata_from_buffer(memory_mapped_file_t file,
|
|
226
|
+
std::size_t offset = 0) noexcept {
|
|
227
|
+
index_dense_metadata_result_t result;
|
|
228
|
+
|
|
229
|
+
// Read the header
|
|
230
|
+
if (offset + sizeof(index_dense_head_buffer_t) >= file.size())
|
|
231
|
+
return result.failed("End of file reached!");
|
|
232
|
+
|
|
233
|
+
byte_t* const file_data = file.data() + offset;
|
|
234
|
+
std::size_t const file_size = file.size() - offset;
|
|
235
|
+
std::memcpy(&result.head_buffer, file_data, sizeof(index_dense_head_buffer_t));
|
|
236
|
+
|
|
237
|
+
// Check if the file immediately starts with the index, instead of vectors
|
|
238
|
+
result.config.exclude_vectors = true;
|
|
239
|
+
if (std::memcmp(result.head_buffer, default_magic(), std::strlen(default_magic())) == 0)
|
|
240
|
+
return result;
|
|
241
|
+
|
|
242
|
+
// Check if it starts with 32-bit
|
|
243
|
+
std::uint32_t dimensions_u32[2]{0};
|
|
244
|
+
std::memcpy(dimensions_u32, result.head_buffer, sizeof(dimensions_u32));
|
|
245
|
+
std::size_t offset_if_u32 = std::size_t(dimensions_u32[0]) * dimensions_u32[1] + sizeof(dimensions_u32);
|
|
246
|
+
|
|
247
|
+
std::uint64_t dimensions_u64[2]{0};
|
|
248
|
+
std::memcpy(dimensions_u64, result.head_buffer, sizeof(dimensions_u64));
|
|
249
|
+
std::size_t offset_if_u64 = std::size_t(dimensions_u64[0]) * dimensions_u64[1] + sizeof(dimensions_u64);
|
|
250
|
+
|
|
251
|
+
// Check if it starts with 32-bit
|
|
252
|
+
if (offset_if_u32 + sizeof(index_dense_head_buffer_t) < file_size) {
|
|
253
|
+
std::memcpy(&result.head_buffer, file_data + offset_if_u32, sizeof(index_dense_head_buffer_t));
|
|
254
|
+
result.config.exclude_vectors = false;
|
|
255
|
+
result.config.use_64_bit_dimensions = false;
|
|
256
|
+
if (std::memcmp(result.head_buffer, default_magic(), std::strlen(default_magic())) == 0)
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check if it starts with 64-bit
|
|
261
|
+
if (offset_if_u64 + sizeof(index_dense_head_buffer_t) < file_size) {
|
|
262
|
+
std::memcpy(&result.head_buffer, file_data + offset_if_u64, sizeof(index_dense_head_buffer_t));
|
|
263
|
+
result.config.exclude_vectors = false;
|
|
264
|
+
result.config.use_64_bit_dimensions = true;
|
|
265
|
+
if (std::memcmp(result.head_buffer, default_magic(), std::strlen(default_magic())) == 0)
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return result.failed("Not a dense USearch index!");
|
|
270
|
+
}
|
|
271
|
+
|
|
222
272
|
/**
|
|
223
273
|
* @brief Oversimplified type-punned index for equidimensional vectors
|
|
224
274
|
* with automatic @b down-casting, hardware-specific @b SIMD metrics,
|
|
@@ -454,7 +504,7 @@ class index_dense_gt {
|
|
|
454
504
|
}
|
|
455
505
|
|
|
456
506
|
static index_dense_gt make(char const* path, bool view = false) {
|
|
457
|
-
index_dense_metadata_result_t meta =
|
|
507
|
+
index_dense_metadata_result_t meta = index_dense_metadata_from_path(path);
|
|
458
508
|
if (!meta)
|
|
459
509
|
return {};
|
|
460
510
|
metric_punned_t metric(meta.head.dimensions, meta.head.kind_metric, meta.head.kind_scalar);
|
|
@@ -501,6 +551,9 @@ class index_dense_gt {
|
|
|
501
551
|
|
|
502
552
|
stats_t stats() const { return typed_->stats(); }
|
|
503
553
|
stats_t stats(std::size_t level) const { return typed_->stats(level); }
|
|
554
|
+
stats_t stats(stats_t* stats_per_level, std::size_t max_level) const {
|
|
555
|
+
return typed_->stats(stats_per_level, max_level);
|
|
556
|
+
}
|
|
504
557
|
|
|
505
558
|
dynamic_allocator_t const& allocator() const { return typed_->dynamic_allocator(); }
|
|
506
559
|
key_t const& free_key() const { return free_key_; }
|
|
@@ -509,7 +562,7 @@ class index_dense_gt {
|
|
|
509
562
|
* @brief A relatively accurate lower bound on the amount of memory consumed by the system.
|
|
510
563
|
* In practice it's error will be below 10%.
|
|
511
564
|
*
|
|
512
|
-
* @see `
|
|
565
|
+
* @see `serialized_length` for the length of the binary serialized representation.
|
|
513
566
|
*/
|
|
514
567
|
std::size_t memory_usage() const {
|
|
515
568
|
return //
|
|
@@ -531,31 +584,31 @@ class index_dense_gt {
|
|
|
531
584
|
|
|
532
585
|
// clang-format off
|
|
533
586
|
add_result_t add(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); }
|
|
534
|
-
add_result_t add(key_t key,
|
|
587
|
+
add_result_t add(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); }
|
|
535
588
|
add_result_t add(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); }
|
|
536
589
|
add_result_t add(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); }
|
|
537
590
|
add_result_t add(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); }
|
|
538
591
|
|
|
539
592
|
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, thread, exact, casts_.from_b1x8); }
|
|
540
|
-
search_result_t search(
|
|
593
|
+
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, thread, exact, casts_.from_i8); }
|
|
541
594
|
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, thread, exact, casts_.from_f16); }
|
|
542
595
|
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, thread, exact, casts_.from_f32); }
|
|
543
596
|
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, thread, exact, casts_.from_f64); }
|
|
544
597
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
598
|
+
std::size_t get(key_t key, b1x8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_b1x8); }
|
|
599
|
+
std::size_t get(key_t key, i8_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_i8); }
|
|
600
|
+
std::size_t get(key_t key, f16_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f16); }
|
|
601
|
+
std::size_t get(key_t key, f32_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f32); }
|
|
602
|
+
std::size_t get(key_t key, f64_t* vector, std::size_t vectors_count = 1) const { return get_(key, vector, vectors_count, casts_.to_f64); }
|
|
550
603
|
|
|
551
604
|
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); }
|
|
552
|
-
cluster_result_t cluster(
|
|
605
|
+
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); }
|
|
553
606
|
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); }
|
|
554
607
|
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); }
|
|
555
608
|
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); }
|
|
556
609
|
|
|
557
610
|
aggregated_distances_t distance_between(key_t key, b1x8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_b1x8); }
|
|
558
|
-
aggregated_distances_t distance_between(key_t key,
|
|
611
|
+
aggregated_distances_t distance_between(key_t key, i8_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_i8); }
|
|
559
612
|
aggregated_distances_t distance_between(key_t key, f16_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f16); }
|
|
560
613
|
aggregated_distances_t distance_between(key_t key, f32_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f32); }
|
|
561
614
|
aggregated_distances_t distance_between(key_t key, f64_t const* vector, std::size_t thread = any_thread()) const { return distance_between_(key, vector, thread, casts_.to_f64); }
|
|
@@ -710,29 +763,11 @@ class index_dense_gt {
|
|
|
710
763
|
std::iota(available_threads_.begin(), available_threads_.end(), 0ul);
|
|
711
764
|
}
|
|
712
765
|
|
|
713
|
-
/**
|
|
714
|
-
* @brief Saves the index to a file.
|
|
715
|
-
* @param[in] path The path to the file.
|
|
716
|
-
* @param[in] config Configuration parameters for exports.
|
|
717
|
-
* @return Outcome descriptor explicitly convertible to boolean.
|
|
718
|
-
*/
|
|
719
|
-
serialization_result_t save(output_file_t file, serialization_config_t config = {}) const {
|
|
720
|
-
serialization_result_t result = file.open_if_not();
|
|
721
|
-
if (result)
|
|
722
|
-
stream(
|
|
723
|
-
[&](void* buffer, std::size_t length) {
|
|
724
|
-
result = file.write(buffer, length);
|
|
725
|
-
return !!result;
|
|
726
|
-
},
|
|
727
|
-
config);
|
|
728
|
-
return result;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
766
|
/**
|
|
732
767
|
* @brief Saves serialized binary index representation to a stream.
|
|
733
768
|
*/
|
|
734
769
|
template <typename output_callback_at, typename progress_at = dummy_progress_t>
|
|
735
|
-
serialization_result_t
|
|
770
|
+
serialization_result_t save_to_stream(output_callback_at&& output, serialization_config_t config = {}) const {
|
|
736
771
|
|
|
737
772
|
serialization_result_t result;
|
|
738
773
|
std::uint64_t matrix_rows = 0;
|
|
@@ -745,7 +780,7 @@ class index_dense_gt {
|
|
|
745
780
|
std::uint32_t dimensions[2];
|
|
746
781
|
dimensions[0] = static_cast<std::uint32_t>(typed_->size());
|
|
747
782
|
dimensions[1] = static_cast<std::uint32_t>(metric_.bytes_per_vector());
|
|
748
|
-
if (!
|
|
783
|
+
if (!output(&dimensions, sizeof(dimensions)))
|
|
749
784
|
return result.failed("Failed to serialize into stream");
|
|
750
785
|
matrix_rows = dimensions[0];
|
|
751
786
|
matrix_cols = dimensions[1];
|
|
@@ -753,7 +788,7 @@ class index_dense_gt {
|
|
|
753
788
|
std::uint64_t dimensions[2];
|
|
754
789
|
dimensions[0] = static_cast<std::uint64_t>(typed_->size());
|
|
755
790
|
dimensions[1] = static_cast<std::uint64_t>(metric_.bytes_per_vector());
|
|
756
|
-
if (!
|
|
791
|
+
if (!output(&dimensions, sizeof(dimensions)))
|
|
757
792
|
return result.failed("Failed to serialize into stream");
|
|
758
793
|
matrix_rows = dimensions[0];
|
|
759
794
|
matrix_cols = dimensions[1];
|
|
@@ -762,7 +797,7 @@ class index_dense_gt {
|
|
|
762
797
|
// Dump the vectors one after another
|
|
763
798
|
for (std::uint64_t i = 0; i != matrix_rows; ++i) {
|
|
764
799
|
byte_t* vector = vectors_lookup_[i];
|
|
765
|
-
if (!
|
|
800
|
+
if (!output(vector, matrix_cols))
|
|
766
801
|
return result.failed("Failed to serialize into stream");
|
|
767
802
|
}
|
|
768
803
|
}
|
|
@@ -791,25 +826,25 @@ class index_dense_gt {
|
|
|
791
826
|
head.dimensions = dimensions();
|
|
792
827
|
head.multi = multi();
|
|
793
828
|
|
|
794
|
-
if (!
|
|
829
|
+
if (!output(&buffer, sizeof(buffer)))
|
|
795
830
|
return result.failed("Failed to serialize into stream");
|
|
796
831
|
}
|
|
797
832
|
|
|
798
833
|
// Save the actual proximity graph
|
|
799
|
-
return typed_->
|
|
834
|
+
return typed_->save_to_stream(std::forward<output_callback_at>(output));
|
|
800
835
|
}
|
|
801
836
|
|
|
802
837
|
/**
|
|
803
838
|
* @brief Estimate the binary length (in bytes) of the serialized index.
|
|
804
839
|
*/
|
|
805
|
-
std::size_t
|
|
840
|
+
std::size_t serialized_length(serialization_config_t config = {}) const noexcept {
|
|
806
841
|
std::size_t dimensions_length = 0;
|
|
807
842
|
std::size_t matrix_length = 0;
|
|
808
843
|
if (!config.exclude_vectors) {
|
|
809
844
|
dimensions_length = config.use_64_bit_dimensions ? sizeof(std::uint64_t) * 2 : sizeof(std::uint32_t) * 2;
|
|
810
845
|
matrix_length = typed_->size() * metric_.bytes_per_vector();
|
|
811
846
|
}
|
|
812
|
-
return dimensions_length + matrix_length + sizeof(index_dense_head_buffer_t) + typed_->
|
|
847
|
+
return dimensions_length + matrix_length + sizeof(index_dense_head_buffer_t) + typed_->serialized_length();
|
|
813
848
|
}
|
|
814
849
|
|
|
815
850
|
/**
|
|
@@ -818,12 +853,14 @@ class index_dense_gt {
|
|
|
818
853
|
* @param[in] config Configuration parameters for imports.
|
|
819
854
|
* @return Outcome descriptor explicitly convertible to boolean.
|
|
820
855
|
*/
|
|
821
|
-
|
|
856
|
+
template <typename input_callback_at>
|
|
857
|
+
serialization_result_t load_from_stream(input_callback_at&& input, serialization_config_t config = {}) {
|
|
822
858
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
return result;
|
|
859
|
+
// Discard all previous memory allocations of `vectors_tape_allocator_`
|
|
860
|
+
reset();
|
|
826
861
|
|
|
862
|
+
// Infer the new index size
|
|
863
|
+
serialization_result_t result;
|
|
827
864
|
std::uint64_t matrix_rows = 0;
|
|
828
865
|
std::uint64_t matrix_cols = 0;
|
|
829
866
|
|
|
@@ -832,16 +869,14 @@ class index_dense_gt {
|
|
|
832
869
|
// Save the matrix size
|
|
833
870
|
if (!config.use_64_bit_dimensions) {
|
|
834
871
|
std::uint32_t dimensions[2];
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
return result;
|
|
872
|
+
if (!input(&dimensions, sizeof(dimensions)))
|
|
873
|
+
return result.failed("Failed to read 32-bit dimensions of the matrix");
|
|
838
874
|
matrix_rows = dimensions[0];
|
|
839
875
|
matrix_cols = dimensions[1];
|
|
840
876
|
} else {
|
|
841
877
|
std::uint64_t dimensions[2];
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
return result;
|
|
878
|
+
if (!input(&dimensions, sizeof(dimensions)))
|
|
879
|
+
return result.failed("Failed to read 64-bit dimensions of the matrix");
|
|
845
880
|
matrix_rows = dimensions[0];
|
|
846
881
|
matrix_cols = dimensions[1];
|
|
847
882
|
}
|
|
@@ -849,9 +884,8 @@ class index_dense_gt {
|
|
|
849
884
|
vectors_lookup_.resize(matrix_rows);
|
|
850
885
|
for (std::uint64_t slot = 0; slot != matrix_rows; ++slot) {
|
|
851
886
|
byte_t* vector = vectors_tape_allocator_.allocate(matrix_cols);
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return result;
|
|
887
|
+
if (!input(vector, matrix_cols))
|
|
888
|
+
return result.failed("Failed to read vectors");
|
|
855
889
|
vectors_lookup_[slot] = vector;
|
|
856
890
|
}
|
|
857
891
|
}
|
|
@@ -859,9 +893,8 @@ class index_dense_gt {
|
|
|
859
893
|
// Load metadata and choose the right metric
|
|
860
894
|
{
|
|
861
895
|
index_dense_head_buffer_t buffer;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
return result;
|
|
896
|
+
if (!input(buffer, sizeof(buffer)))
|
|
897
|
+
return result.failed("Failed to read the index ");
|
|
865
898
|
|
|
866
899
|
index_dense_head_t head{buffer};
|
|
867
900
|
if (std::memcmp(buffer, default_magic(), std::strlen(default_magic())) != 0)
|
|
@@ -882,7 +915,7 @@ class index_dense_gt {
|
|
|
882
915
|
}
|
|
883
916
|
|
|
884
917
|
// Pull the actual proximity graph
|
|
885
|
-
result = typed_->
|
|
918
|
+
result = typed_->load_from_stream(std::forward<input_callback_at>(input));
|
|
886
919
|
if (!result)
|
|
887
920
|
return result;
|
|
888
921
|
if (typed_->size() != static_cast<std::size_t>(matrix_rows))
|
|
@@ -900,10 +933,14 @@ class index_dense_gt {
|
|
|
900
933
|
*/
|
|
901
934
|
serialization_result_t view(memory_mapped_file_t file, std::size_t offset = 0, serialization_config_t config = {}) {
|
|
902
935
|
|
|
936
|
+
// Discard all previous memory allocations of `vectors_tape_allocator_`
|
|
937
|
+
reset();
|
|
938
|
+
|
|
903
939
|
serialization_result_t result = file.open_if_not();
|
|
904
940
|
if (!result)
|
|
905
941
|
return result;
|
|
906
942
|
|
|
943
|
+
// Infer the new index size
|
|
907
944
|
std::uint64_t matrix_rows = 0;
|
|
908
945
|
std::uint64_t matrix_cols = 0;
|
|
909
946
|
span_punned_t vectors_buffer;
|
|
@@ -976,6 +1013,109 @@ class index_dense_gt {
|
|
|
976
1013
|
return result;
|
|
977
1014
|
}
|
|
978
1015
|
|
|
1016
|
+
/**
|
|
1017
|
+
* @brief Saves the index to a file.
|
|
1018
|
+
* @param[in] path The path to the file.
|
|
1019
|
+
* @param[in] config Configuration parameters for exports.
|
|
1020
|
+
* @return Outcome descriptor explicitly convertible to boolean.
|
|
1021
|
+
*/
|
|
1022
|
+
serialization_result_t save(output_file_t file, serialization_config_t config = {}) const {
|
|
1023
|
+
|
|
1024
|
+
serialization_result_t io_result = file.open_if_not();
|
|
1025
|
+
if (!io_result)
|
|
1026
|
+
return io_result;
|
|
1027
|
+
|
|
1028
|
+
serialization_result_t stream_result = save_to_stream(
|
|
1029
|
+
[&](void const* buffer, std::size_t length) {
|
|
1030
|
+
io_result = file.write(buffer, length);
|
|
1031
|
+
return !!io_result;
|
|
1032
|
+
},
|
|
1033
|
+
config);
|
|
1034
|
+
|
|
1035
|
+
if (!stream_result)
|
|
1036
|
+
return stream_result;
|
|
1037
|
+
return io_result;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @brief Memory-maps the serialized binary index representation from disk,
|
|
1042
|
+
* @b without copying data into RAM, and fetching it on-demand.
|
|
1043
|
+
*/
|
|
1044
|
+
serialization_result_t save(memory_mapped_file_t file, std::size_t offset = 0,
|
|
1045
|
+
serialization_config_t config = {}) const {
|
|
1046
|
+
|
|
1047
|
+
serialization_result_t io_result = file.open_if_not();
|
|
1048
|
+
if (!io_result)
|
|
1049
|
+
return io_result;
|
|
1050
|
+
|
|
1051
|
+
serialization_result_t stream_result = save_to_stream(
|
|
1052
|
+
[&](void const* buffer, std::size_t length) {
|
|
1053
|
+
if (offset + length > file.size())
|
|
1054
|
+
return false;
|
|
1055
|
+
std::memcpy(file.data() + offset, buffer, length);
|
|
1056
|
+
offset += length;
|
|
1057
|
+
return true;
|
|
1058
|
+
},
|
|
1059
|
+
config);
|
|
1060
|
+
|
|
1061
|
+
return stream_result;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* @brief Parses the index from file to RAM.
|
|
1066
|
+
* @param[in] path The path to the file.
|
|
1067
|
+
* @param[in] config Configuration parameters for imports.
|
|
1068
|
+
* @return Outcome descriptor explicitly convertible to boolean.
|
|
1069
|
+
*/
|
|
1070
|
+
serialization_result_t load(input_file_t file, serialization_config_t config = {}) {
|
|
1071
|
+
|
|
1072
|
+
serialization_result_t io_result = file.open_if_not();
|
|
1073
|
+
if (!io_result)
|
|
1074
|
+
return io_result;
|
|
1075
|
+
|
|
1076
|
+
serialization_result_t stream_result = load_from_stream(
|
|
1077
|
+
[&](void* buffer, std::size_t length) {
|
|
1078
|
+
io_result = file.read(buffer, length);
|
|
1079
|
+
return !!io_result;
|
|
1080
|
+
},
|
|
1081
|
+
config);
|
|
1082
|
+
|
|
1083
|
+
if (!stream_result)
|
|
1084
|
+
return stream_result;
|
|
1085
|
+
return io_result;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* @brief Memory-maps the serialized binary index representation from disk,
|
|
1090
|
+
* @b without copying data into RAM, and fetching it on-demand.
|
|
1091
|
+
*/
|
|
1092
|
+
serialization_result_t load(memory_mapped_file_t file, std::size_t offset = 0, serialization_config_t config = {}) {
|
|
1093
|
+
|
|
1094
|
+
serialization_result_t io_result = file.open_if_not();
|
|
1095
|
+
if (!io_result)
|
|
1096
|
+
return io_result;
|
|
1097
|
+
|
|
1098
|
+
serialization_result_t stream_result = load_from_stream(
|
|
1099
|
+
[&](void* buffer, std::size_t length) {
|
|
1100
|
+
if (offset + length > file.size())
|
|
1101
|
+
return false;
|
|
1102
|
+
std::memcpy(buffer, file.data() + offset, length);
|
|
1103
|
+
offset += length;
|
|
1104
|
+
return true;
|
|
1105
|
+
},
|
|
1106
|
+
config);
|
|
1107
|
+
|
|
1108
|
+
return stream_result;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
serialization_result_t save(char const* file_path, serialization_config_t config = {}) const {
|
|
1112
|
+
return save(output_file_t(file_path), config);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
serialization_result_t load(char const* file_path, serialization_config_t config = {}) {
|
|
1116
|
+
return load(input_file_t(file_path), config);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
979
1119
|
/**
|
|
980
1120
|
* @brief Checks if a vector with specified key is present.
|
|
981
1121
|
* @return `true` if the key is present in the index, `false` otherwise.
|
|
@@ -1489,6 +1629,8 @@ class index_dense_gt {
|
|
|
1489
1629
|
|
|
1490
1630
|
result.computed_distances = computed_distances;
|
|
1491
1631
|
result.visited_members = visited_members;
|
|
1632
|
+
|
|
1633
|
+
(void)progress;
|
|
1492
1634
|
return result;
|
|
1493
1635
|
}
|
|
1494
1636
|
|
|
@@ -1663,11 +1805,6 @@ class index_dense_gt {
|
|
|
1663
1805
|
return result;
|
|
1664
1806
|
}
|
|
1665
1807
|
|
|
1666
|
-
compressed_slot_t lookup_id_(key_t key) const {
|
|
1667
|
-
shared_lock_t lock(slot_lookup_mutex_);
|
|
1668
|
-
return slot_lookup_.at(key);
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
1808
|
void reindex_keys_() {
|
|
1672
1809
|
|
|
1673
1810
|
// Estimate number of entries first
|
|
@@ -1735,13 +1872,13 @@ class index_dense_gt {
|
|
|
1735
1872
|
casts_t result;
|
|
1736
1873
|
|
|
1737
1874
|
result.from_b1x8 = cast_gt<b1x8_t, to_scalar_at>{};
|
|
1738
|
-
result.from_i8 = cast_gt<
|
|
1875
|
+
result.from_i8 = cast_gt<i8_t, to_scalar_at>{};
|
|
1739
1876
|
result.from_f16 = cast_gt<f16_t, to_scalar_at>{};
|
|
1740
1877
|
result.from_f32 = cast_gt<f32_t, to_scalar_at>{};
|
|
1741
1878
|
result.from_f64 = cast_gt<f64_t, to_scalar_at>{};
|
|
1742
1879
|
|
|
1743
1880
|
result.to_b1x8 = cast_gt<to_scalar_at, b1x8_t>{};
|
|
1744
|
-
result.to_i8 = cast_gt<to_scalar_at,
|
|
1881
|
+
result.to_i8 = cast_gt<to_scalar_at, i8_t>{};
|
|
1745
1882
|
result.to_f16 = cast_gt<to_scalar_at, f16_t>{};
|
|
1746
1883
|
result.to_f32 = cast_gt<to_scalar_at, f32_t>{};
|
|
1747
1884
|
result.to_f64 = cast_gt<to_scalar_at, f64_t>{};
|
|
@@ -1754,7 +1891,7 @@ class index_dense_gt {
|
|
|
1754
1891
|
case scalar_kind_t::f64_k: return make_casts_<f64_t>();
|
|
1755
1892
|
case scalar_kind_t::f32_k: return make_casts_<f32_t>();
|
|
1756
1893
|
case scalar_kind_t::f16_k: return make_casts_<f16_t>();
|
|
1757
|
-
case scalar_kind_t::i8_k: return make_casts_<
|
|
1894
|
+
case scalar_kind_t::i8_k: return make_casts_<i8_t>();
|
|
1758
1895
|
case scalar_kind_t::b1x8_k: return make_casts_<b1x8_t>();
|
|
1759
1896
|
default: return {};
|
|
1760
1897
|
}
|