usearch 2.10.3 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +28 -8
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +244 -0
  5. package/c/lib.cpp +459 -0
  6. package/c/test.c +386 -0
  7. package/c/usearch.h +451 -0
  8. package/cmake/clipp.cmake +7 -0
  9. package/cmake/config.cmake.in +15 -0
  10. package/cmake/emscripten.cmake +8 -0
  11. package/cmake/jemalloc.cmake +62 -0
  12. package/cmake/pkg-config.pc.in +4 -0
  13. package/cmake/usearchConfigVersion.cmake.in +20 -0
  14. package/cpp/CMakeLists.txt +14 -0
  15. package/cpp/README.md +161 -0
  16. package/cpp/bench.cpp +616 -0
  17. package/cpp/test.cpp +335 -0
  18. package/fp16/CMakeLists.txt +176 -0
  19. package/fp16/LICENSE +11 -0
  20. package/fp16/README.md +20 -0
  21. package/fp16/bench/alt-element.cc +89 -0
  22. package/fp16/bench/from-alt-array.cc +175 -0
  23. package/fp16/bench/from-ieee-array.cc +399 -0
  24. package/fp16/bench/ieee-element.cc +272 -0
  25. package/fp16/bench/to-alt-array.cc +91 -0
  26. package/fp16/bench/to-ieee-array.cc +303 -0
  27. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  28. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  29. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  30. package/fp16/configure.py +65 -0
  31. package/fp16/confu.yaml +8 -0
  32. package/fp16/include/fp16/__init__.py +0 -0
  33. package/fp16/include/fp16/avx.py +50 -0
  34. package/fp16/include/fp16/avx2.py +53 -0
  35. package/fp16/include/fp16/bitcasts.h +92 -0
  36. package/fp16/include/fp16/fp16.h +451 -0
  37. package/fp16/include/fp16/psimd.h +131 -0
  38. package/fp16/include/fp16.h +11 -0
  39. package/fp16/jni/Android.mk +6 -0
  40. package/fp16/jni/Application.mk +4 -0
  41. package/fp16/test/alt-from-fp32-value.cc +498 -0
  42. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  43. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  44. package/fp16/test/alt-to-fp32-value.cc +348 -0
  45. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  46. package/fp16/test/bitcasts.cc +56 -0
  47. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  48. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  49. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  50. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  51. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  52. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  53. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  54. package/fp16/test/peachpy/stubs.py +38 -0
  55. package/fp16/test/tables.cc +530 -0
  56. package/fp16/test/tables.h +13 -0
  57. package/fp16/third-party/THHalf.h +92 -0
  58. package/fp16/third-party/eigen-half.h +170 -0
  59. package/fp16/third-party/float16-compressor.h +80 -0
  60. package/fp16/third-party/half.hpp +2916 -0
  61. package/fp16/third-party/npy-halffloat.h +328 -0
  62. package/include/usearch/index.hpp +3838 -0
  63. package/include/usearch/index_dense.hpp +2022 -0
  64. package/include/usearch/index_plugins.hpp +2317 -0
  65. package/javascript/dist/cjs/usearch.js +5 -5
  66. package/javascript/dist/esm/usearch.js +2 -2
  67. package/javascript/dist-package-cjs.json +3 -0
  68. package/javascript/dist-package-esm.json +3 -0
  69. package/javascript/lib.cpp +350 -0
  70. package/javascript/node-gyp-build.d.ts +1 -0
  71. package/javascript/tsconfig-base.json +25 -0
  72. package/javascript/tsconfig-cjs.json +8 -0
  73. package/javascript/tsconfig-esm.json +8 -0
  74. package/javascript/usearch.test.js +84 -0
  75. package/javascript/usearch.ts +640 -0
  76. package/package.json +14 -7
  77. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  78. package/prebuilds/linux-arm64/usearch.node +0 -0
  79. package/prebuilds/linux-x64/usearch.node +0 -0
  80. package/prebuilds/win32-ia32/usearch.node +0 -0
  81. package/prebuilds/win32-x64/usearch.node +0 -0
  82. package/simsimd/.releaserc +87 -0
  83. package/simsimd/CONTRIBUTING.md +114 -0
  84. package/simsimd/Cargo.lock +709 -0
  85. package/simsimd/Cargo.toml +44 -0
  86. package/simsimd/LICENSE +201 -0
  87. package/simsimd/README.md +708 -0
  88. package/simsimd/binding.gyp +36 -0
  89. package/simsimd/build.rs +51 -0
  90. package/simsimd/c/lib.c +110 -0
  91. package/simsimd/include/simsimd/binary.h +258 -0
  92. package/simsimd/include/simsimd/dot.h +1237 -0
  93. package/simsimd/include/simsimd/geospatial.h +37 -0
  94. package/simsimd/include/simsimd/probability.h +563 -0
  95. package/simsimd/include/simsimd/simsimd.h +1129 -0
  96. package/simsimd/include/simsimd/spatial.h +1019 -0
  97. package/simsimd/include/simsimd/types.h +309 -0
  98. package/simsimd/javascript/bench.js +127 -0
  99. package/simsimd/javascript/dist-package-cjs.json +3 -0
  100. package/simsimd/javascript/dist-package-esm.json +3 -0
  101. package/simsimd/javascript/fallback.ts +203 -0
  102. package/simsimd/javascript/lib.c +107 -0
  103. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  104. package/simsimd/javascript/simsimd.ts +135 -0
  105. package/simsimd/javascript/test.mjs +179 -0
  106. package/simsimd/javascript/tsconfig-base.json +37 -0
  107. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  108. package/simsimd/javascript/tsconfig-esm.json +8 -0
  109. package/simsimd/package-lock.json +1709 -0
  110. package/simsimd/package.json +61 -0
  111. package/simsimd/rust/benches/cosine.rs +27 -0
  112. package/simsimd/rust/benches/native.rs +30 -0
  113. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  114. package/simsimd/rust/lib.rs +778 -0
package/README.md CHANGED
@@ -194,21 +194,40 @@ When compared to FAISS's `IndexFlatL2` in Google Colab, __[USearch may offer up
194
194
 
195
195
  ## User-Defined Functions
196
196
 
197
- While most vector search packages concentrate on just a few metrics - "Inner Product distance" and "Euclidean distance," USearch extends this list to include any user-defined metrics.
198
- This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models.
197
+ While most vector search packages concentrate on just two metrics, "Inner Product distance" and "Euclidean distance", USearch allows arbitrary user-defined metrics.
198
+ This flexibility allows you to customize your search for various applications, from computing geospatial coordinates with the rare [Haversine][haversine] distance to creating custom metrics for composite embeddings from multiple AI models, like joint image-text embeddings.
199
+ You can use [Numba][numba], [Cppyy][cppyy], or [PeachPy][peachpy] to define your [custom metric even in Python](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python):
199
200
 
200
- ![USearch: Vector Search Approaches](https://github.com/unum-cloud/usearch/blob/main/assets/usearch-approaches-white.png?raw=true)
201
+ ```py
202
+ from numba import cfunc, types, carray
203
+ from usearch.index import Index, MetricKind, MetricSignature, CompiledMetric
204
+
205
+ @cfunc(types.float32(types.CPointer(types.float32), types.CPointer(types.float32)))
206
+ def python_inner_product(a, b):
207
+ a_array = carray(a, ndim)
208
+ b_array = carray(b, ndim)
209
+ c = 0.0
210
+ for i in range(ndim):
211
+ c += a_array[i] * b_array[i]
212
+ return 1 - c
213
+
214
+ metric = CompiledMetric(pointer=python_inner_product.address, kind=MetricKind.IP, signature=MetricSignature.ArrayArray)
215
+ index = Index(ndim=ndim, metric=metric, dtype=np.float32)
216
+ ```
201
217
 
202
- Unlike older approaches indexing high-dimensional spaces, like KD-Trees and Locality Sensitive Hashing, HNSW doesn't require vectors to be identical in length.
218
+ Similar effect is even easier to achieve in C, C++, and Rust interfaces.
219
+ Moreover, unlike older approaches indexing high-dimensional spaces, like KD-Trees and Locality Sensitive Hashing, HNSW doesn't require vectors to be identical in length.
203
220
  They only have to be comparable.
204
- So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] as a distance function.
205
-
206
- > Read more about [JIT and UDF in USearch Python SDK](https://unum-cloud.github.io/usearch/python#user-defined-metrics-and-jit-in-python).
221
+ So you can apply it in [obscure][obscure] applications, like searching for similar sets or fuzzy text matching, using [GZip][gzip-similarity] compression-ratio as a distance function.
207
222
 
208
223
  [haversine]: https://ashvardanian.com/posts/abusing-vector-search#geo-spatial-indexing
209
224
  [obscure]: https://ashvardanian.com/posts/abusing-vector-search
210
225
  [gzip-similarity]: https://twitter.com/LukeGessler/status/1679211291292889100?s=20
211
226
 
227
+ [numba]: https://numba.readthedocs.io/en/stable/reference/jit-compilation.html#c-callbacks
228
+ [cppyy]: https://cppyy.readthedocs.io/en/latest/
229
+ [peachpy]: https://github.com/Maratyszcza/PeachPy
230
+
212
231
  ## Memory Efficiency, Downcasting, and Quantization
213
232
 
214
233
  Training a quantization model and dimension-reduction is a common approach to accelerate vector search.
@@ -315,6 +334,7 @@ In some cases, like Batch operations, feature parity is meaningless, as the host
315
334
  | Save, load, view | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
316
335
  | User-defined metrics | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
317
336
  | Batch operations | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
337
+ | Filter predicates | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
318
338
  | Joins | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
319
339
  | Variable-length vectors | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
320
340
  | 4B+ capacities | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
@@ -432,7 +452,7 @@ doi = {10.5281/zenodo.7949416},
432
452
  author = {Vardanian, Ash},
433
453
  title = {{USearch by Unum Cloud}},
434
454
  url = {https://github.com/unum-cloud/usearch},
435
- version = {2.10.3},
455
+ version = {2.11.0},
436
456
  year = {2023},
437
457
  month = oct,
438
458
  }
package/binding.gyp ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "usearch",
5
+ "sources": ["javascript/lib.cpp"],
6
+ "cflags": [
7
+ "-fexceptions",
8
+ "-Wno-unknown-pragmas",
9
+ "-Wno-maybe-uninitialized",
10
+ ],
11
+ "cflags_cc": [
12
+ "-fexceptions",
13
+ "-Wno-unknown-pragmas",
14
+ "-Wno-maybe-uninitialized",
15
+ "-std=c++17",
16
+ ],
17
+ "include_dirs": [
18
+ "<!@(node -p \"require('node-addon-api').include\")",
19
+ "include",
20
+ "fp16/include",
21
+ "simsimd/include",
22
+ ],
23
+ "dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
24
+ "xcode_settings": {
25
+ "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
26
+ "CLANG_CXX_LIBRARY": "libc++",
27
+ "MACOSX_DEPLOYMENT_TARGET": "11.0",
28
+ "OTHER_CFLAGS": [
29
+ "-arch arm64",
30
+ "-arch x86_64"
31
+ ],
32
+ "OTHER_LDFLAGS": [
33
+ "-arch arm64",
34
+ "-arch x86_64"
35
+ ]
36
+ },
37
+ "msvs_settings": {
38
+ "VCCLCompilerTool": {
39
+ "ExceptionHandling": 1,
40
+ "AdditionalOptions": ["-std:c++17"],
41
+ }
42
+ },
43
+ "conditions": [
44
+ [
45
+ 'OS=="linux"',
46
+ {
47
+ "cflags_cc": [
48
+ '<!(if [ "$USEARCH_USE_OPENMP" = "1" ]; then echo \'-fopenmp\'; fi)',
49
+ ],
50
+ "ldflags": [
51
+ '<!(if [ "$USEARCH_USE_OPENMP" = "1" ]; then echo \'-lgomp\'; fi)'
52
+ ],
53
+ "defines": [
54
+ "USEARCH_USE_OPENMP=<!(echo ${USEARCH_USE_OPENMP:-0})",
55
+ "USEARCH_USE_SIMSIMD=<!(echo ${USEARCH_USE_SIMSIMD:-1})",
56
+ "USEARCH_USE_FP16LIB=<!(echo ${USEARCH_USE_FP16LIB:-1})",
57
+ "SIMSIMD_TARGET_NEON=<!(echo ${SIMSIMD_TARGET_NEON:-1})",
58
+ "SIMSIMD_TARGET_SVE=<!(echo ${SIMSIMD_TARGET_SVE:-1})",
59
+ "SIMSIMD_TARGET_HASWELL=<!(echo ${SIMSIMD_TARGET_HASWELL:-1})",
60
+ "SIMSIMD_TARGET_SKYLAKE=<!(echo ${SIMSIMD_TARGET_SKYLAKE:-1})",
61
+ "SIMSIMD_TARGET_ICE=<!(echo ${SIMSIMD_TARGET_ICE:-1})",
62
+ "SIMSIMD_TARGET_SAPPHIRE=<!(echo ${SIMSIMD_TARGET_SAPPHIRE:-1})",
63
+ ],
64
+ },
65
+ ],
66
+ [
67
+ 'OS=="mac"',
68
+ {
69
+ "defines": [
70
+ "USEARCH_USE_OPENMP=<!(echo ${USEARCH_USE_OPENMP:-0})",
71
+ "USEARCH_USE_SIMSIMD=<!(echo ${USEARCH_USE_SIMSIMD:-0})",
72
+ "USEARCH_USE_FP16LIB=<!(echo ${USEARCH_USE_FP16LIB:-1})",
73
+ "SIMSIMD_TARGET_NEON=<!(echo ${SIMSIMD_TARGET_NEON:-1})",
74
+ "SIMSIMD_TARGET_SVE=<!(echo ${SIMSIMD_TARGET_SVE:-0})",
75
+ "SIMSIMD_TARGET_HASWELL=<!(echo ${SIMSIMD_TARGET_HASWELL:-1})",
76
+ "SIMSIMD_TARGET_SKYLAKE=<!(echo ${SIMSIMD_TARGET_SKYLAKE:-0})",
77
+ "SIMSIMD_TARGET_ICE=<!(echo ${SIMSIMD_TARGET_ICE:-0})",
78
+ "SIMSIMD_TARGET_SAPPHIRE=<!(echo ${SIMSIMD_TARGET_SAPPHIRE:-0})",
79
+ ],
80
+ },
81
+ ],
82
+ [
83
+ 'OS=="win"',
84
+ {
85
+ "defines": [
86
+ "USEARCH_USE_OPENMP=0",
87
+ "USEARCH_USE_SIMSIMD=0",
88
+ "USEARCH_USE_FP16LIB=1",
89
+ "SIMSIMD_TARGET_SKYLAKE=0",
90
+ "SIMSIMD_TARGET_ICE=0",
91
+ "SIMSIMD_TARGET_SAPPHIRE=0",
92
+ "SIMSIMD_TARGET_SVE=0",
93
+ "SIMSIMD_TARGET_HASWELL=0",
94
+ "SIMSIMD_TARGET_NEON=0",
95
+ ],
96
+ },
97
+ ],
98
+ ],
99
+ }
100
+ ]
101
+ }
@@ -0,0 +1,27 @@
1
+ if (USEARCH_BUILD_TEST_C)
2
+ add_executable(test_c test.c lib.cpp)
3
+ setup_target(test_c)
4
+ include(CTest)
5
+ enable_testing()
6
+ add_test(NAME test_c COMMAND test_c)
7
+ endif ()
8
+
9
+ # This article discusses a better way to allow building either static or shared libraries:
10
+ # https://alexreinking.com/blog/building-a-dual-shared-and-static-library-with-cmake.html
11
+ if (USEARCH_BUILD_LIB_C)
12
+ add_library(usearch_c SHARED lib.cpp)
13
+ add_library(usearch_static_c STATIC lib.cpp)
14
+
15
+ # Set the compile options for the static library
16
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
17
+ target_compile_options(usearch_static_c PRIVATE -static-libstdc++)
18
+ target_link_options(usearch_static_c PRIVATE -static-libstdc++)
19
+ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
20
+ target_compile_options(usearch_static_c PRIVATE -static)
21
+ target_link_options(usearch_static_c PRIVATE -static)
22
+ endif ()
23
+
24
+ setup_target(usearch_c)
25
+ setup_target(usearch_static_c)
26
+
27
+ endif ()
package/c/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # USearch for C
2
+
3
+ ## Installation
4
+
5
+ USearch vector-search engine can be integrated into your project using CMake.
6
+ Alternatively, you can download one of the precompiled binaries from the [releases page](https://github.com/unum-cloud/usearch/releases).
7
+
8
+ ## Quickstart
9
+
10
+ ```c
11
+ #include <stdio.h> // For printing.
12
+ #include <assert.h> // For assertions!
13
+ #include <usearch/usearch.h> // For the win 🚀
14
+
15
+ int main() {
16
+
17
+ // Construct:
18
+ size_t dimensions = 128;
19
+ usearch_error_t error = NULL;
20
+ usearch_init_options_t opts = {
21
+ .metric_kind = usearch_metric_cos_k,
22
+ .scalar_kind = usearch_scalar_f16_k,
23
+ .dimensions = dimensions,
24
+ .expansion_add = 0, // for defaults
25
+ .expansion_search = 0 // for defaults
26
+ };
27
+ usearch_index_t index = usearch_init(&opts, &error);
28
+
29
+ size_t vectors_count = 1000;
30
+ usearch_reserve(index, vectors_count, &error);
31
+ if (error) goto cleanup;
32
+
33
+ // Populate:
34
+ float vector[dimensions]; // don't forget to fill the vector with data
35
+ usearch_add(index, 42, &vector[0], usearch_scalar_f32_k, &error);
36
+ if (error) goto cleanup;
37
+
38
+ // Check up:
39
+ assert(usearch_size(index, &error) == vectors_count);
40
+ assert(usearch_capacity(index, &error) == vectors_count);
41
+ assert(usearch_contains(index, 42, &error));
42
+
43
+ // Search:
44
+ usearch_key_t found_keys[10];
45
+ float found_distances[10];
46
+ size_t found_count = usearch_search(
47
+ index, &vector[0], usearch_scalar_f32_k, 10,
48
+ &found_keys[0], &found_distances[0], &error);
49
+
50
+ usearch_free(index, &error);
51
+
52
+ cleanup:
53
+ if (error) fprintf(stderr, "Error: %s\n", error);
54
+ if (index) usearch_free(index, &error);
55
+ return error ? 1 : 0;
56
+ }
57
+ ```
58
+
59
+ ## Serialization
60
+
61
+ To save and load the index from disk, use the following methods:
62
+
63
+ ```c
64
+ usearch_save(index, "index.usearch", &error);
65
+ usearch_load(index, "index.usearch", &error);
66
+ usearch_view(index, "index.usearch", &error);
67
+ ```
68
+
69
+ Before saving them do disk, you can determine exactly how much disk space the index will take:
70
+
71
+ ```c
72
+ size_t bytes = usearch_serialized_length(index, &error);
73
+ ```
74
+
75
+ With a known size, you can serialize the index into an in-memory a buffer:
76
+
77
+ ```c
78
+ void* buffer = malloc(bytes);
79
+ usearch_save_buffer(index, buffer, bytes, &error);
80
+ usearch_load_buffer(index, buffer, bytes, &error);
81
+ usearch_view_buffer(index, buffer, bytes, &error);
82
+ ```
83
+
84
+ One can also retrieve index metadata from the buffer or from disk:
85
+
86
+ ```c
87
+ usearch_init_options_t opts;
88
+ usearch_metadata("index.usearch", &opts, &error);
89
+ usearch_metadata_buffer(buffer, bytes, &opts, &error);
90
+ ```
91
+
92
+ ## Metrics
93
+
94
+ USearch comes pre-packaged with SimSIMD, bringing over 100 SIMD-accelerated distance kernels for x86 and ARM architectures.
95
+ That includes:
96
+
97
+ - `usearch_metric_cos_k` - Cosine Similarity metric, defined as `Cos = 1 - sum(a[i] * b[i]) / (sqrt(sum(a[i]^2) * sqrt(sum(b[i]^2)))`.
98
+ - `usearch_metric_ip_k` - Inner Product metric, defined as `IP = 1 - sum(a[i] * b[i])`.
99
+ - `usearch_metric_l2sq_k` - Squared Euclidean Distance metric, defined as `L2 = sum((a[i] - b[i])^2)`.
100
+ - `usearch_metric_haversine_k` - Haversine (Great Circle) Distance metric.
101
+ - `usearch_metric_divergence_k` - Jensen Shannon Divergence metric.
102
+ - `usearch_metric_pearson_k` - Pearson Correlation metric.
103
+ - `usearch_metric_hamming_k` - Bit-level Hamming Distance metric, defined as the number of differing bits.
104
+ - `usearch_metric_tanimoto_k` - Bit-level Tanimoto (Jaccard) metric, defined as the number of intersecting bits divided by the number of union bits.
105
+ - `usearch_metric_sorensen_k` - Bit-level Sorensen metric.
106
+
107
+ ### User-Defined Metrics
108
+
109
+ You can also define your own metrics by implementing the `usearch_metric_t` interface:
110
+
111
+ ```c
112
+ simsimd_distance_t callback(void const* a, void const* b, void* state) {
113
+ // Your custom metric implementation here
114
+ }
115
+
116
+ usearch_change_metric(index, callback, NULL, usearch_metric_unknown_k, &error);
117
+ ```
118
+
119
+ You can always revert back to one of the native metrics by calling:
120
+
121
+ ```c
122
+ usearch_change_metric_kind(index, usearch_metric_cos_k, &error);
123
+ ```
124
+
125
+ ## Filtering with Predicates
126
+
127
+ Sometimes you may want to cross-reference search-results against some external database or filter them based on some criteria.
128
+ In most engines, you'd have to manually perform paging requests, successively filtering the results.
129
+ In USearch you can simply pass a predicate function to the search method, which will be applied directly during graph traversal.
130
+
131
+ ```c
132
+ int is_odd(usearch_key_t key, void* state) {
133
+ return key % 2;
134
+ }
135
+
136
+ usearch_filtered_search(
137
+ index, &vector[0], usearch_scalar_f32_k, 10,
138
+ &is_odd, NULL, // no state needed for this callback
139
+ &found_keys[0], &found_distances[0], &error);
140
+ ```
141
+
142
+ ## Extracting, Updating, and Removing Values
143
+
144
+ It is generally not recommended to use HNSW indexes in case of frequent removals or major distribution shifts.
145
+ For small updates, you can use the following methods:
146
+
147
+ ```c
148
+ float recovered_vector[dimensions];
149
+ size_t count_retrieved = usearch_get(index, 42, 1,
150
+ &recovered_vector[0], usearch_scalar_f32_k, &error);
151
+ assert(count_retrieved <= 1);
152
+
153
+ size_t count_renamed = usearch_rename(index, 42, 43, &error);
154
+ size_t count_removed = usearch_remove(index, 43, &error);
155
+ assert(count_renamed == count_removed);
156
+ ```
157
+
158
+ USearch can also be used for multi-indicies, where each key may map to multiple vectors.
159
+ That's a common case when implementing semantic search for long documents, where the entire documents can't fit into one input sequence in a transformer-model, and you have to split them into chunks.
160
+
161
+ ```c
162
+ float many_vectors[10][dimensions];
163
+ size_t count_retrieved = usearch_get(index, 42, 10,
164
+ &many_vectors[0][0], usearch_scalar_f32_k, &error);
165
+ assert(count_retrieved <= 10);
166
+ ```
167
+
168
+ ## Exact Search
169
+
170
+ USearch exposes its internal SIMD-accelerated distance functions for exact search.
171
+ To invoke them for a pair of vectors, use the following methods:
172
+
173
+ ```c
174
+ float vector_a[dimensions], vector_b[dimensions];
175
+ usearch_distance_t distance = usearch_distance(
176
+ &vector_a[0], &vector_b[0], usearch_scalar_f32_k, dimensions, usearch_metric_cos_k, &error);
177
+ ```
178
+
179
+ Alternatively, you can benefir from faster thread-pools and priority queues for parallel exact batch-search.
180
+
181
+ ```c
182
+ size_t threads = 0;
183
+ size_t top_k = 10;
184
+ size_t dataset_count = 1000, queries_count = 10;
185
+ simsimd_f16_t dataset[dataset_count][dimensions];
186
+ simsimd_f16_t queries[queries_count][dimensions];
187
+
188
+ usearch_key_t resulting_keys[queries_count][top_k];
189
+ usearch_distance_t resulting_distances[queries_count][top_k];
190
+
191
+ usearch_exact_search(
192
+ &dataset[0][0], dataset_count, dimensions * sizeof(simsimd_f16_t),
193
+ &queries[0][0], queries_count, dimensions * sizeof(simsimd_f16_t),
194
+ usearch_scalar_f16_k, top_k, threads,
195
+ &resulting_keys[0][0], sizeof(usearch_key_t) * top_k,
196
+ &resulting_distances[0][0], sizeof(usearch_distance_t) * top_k,
197
+ &error);
198
+ ```
199
+
200
+ ## Concurrency and Parallelism
201
+
202
+ USearch manages a pool of "thread local" data-structures to avoid contention and improve performance.
203
+ By default, as many of these structures are created as there are logical cores on the machine.
204
+ Then, one can construct the index in parallel using many cores using third-party thread-pools or tools like OpenACC and OpenMP.
205
+
206
+ ```c
207
+ #pragma omp parallel for
208
+ for (size_t i = 0; i < 1000; i++) {
209
+ usearch_add(index, i, &vector[0], usearch_scalar_f32_k, &error);
210
+ }
211
+
212
+ #pragma omp parallel for
213
+ for (size_t i = 0; i < 1000; i++) {
214
+ usearch_key_t found_keys[10];
215
+ usearch_distance_t found_distances[10];
216
+ size_t found_matches = usearch_search(index,
217
+ &vector[0], usearch_scalar_f32_k, 10,
218
+ &found_keys[0], &found_distances[0], &error);
219
+ }
220
+ ```
221
+
222
+ ## Performance Tuning
223
+
224
+ To optimize the performance of the index, you can adjust the expansion values used during index creation and search operations.
225
+ Higher expansion values will lead to better search accuracy at the cost of slightly increased memory usage, but potentially much higher search times.
226
+ Following methods are available to adjust the expansion values:
227
+
228
+ ```c
229
+ printf("Connectivity: %zu\n", usearch_connectivity(index, &error)); // can't change
230
+ printf("Add expansion: %zu\n", usearch_expansion_add(index, &error)); // can change
231
+ printf("Search expansion: %zu\n", usearch_expansion_search(index, &error)); // can change
232
+ usearch_change_expansion_add(index, 32, &error);
233
+ usearch_change_expansion_search(index, 32, &error);
234
+ ```
235
+
236
+ Optimizing hardware utilization, you may want to check the SIMD hardware acceleration capabilities of the index and memory consumption.
237
+ The first will print the codename of the most advanced SIMD instruction set supported by the CPU and used by the index.
238
+ The second will print the memory usage of the index in bytes.
239
+
240
+ ```c
241
+ printf("Hardware acceleration: %s\n", usearch_hardware_acceleration(index, &error));
242
+ printf("Memory usage: %zu\n", usearch_memory_usage(index, &error));
243
+ ```
244
+