usearch 2.9.0 → 2.9.2

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 (46) hide show
  1. package/CITATION.cff +1 -1
  2. package/CONTRIBUTING.md +36 -2
  3. package/Cargo.toml +1 -1
  4. package/README.md +12 -15
  5. package/VERSION +1 -1
  6. package/c/lib.cpp +19 -17
  7. package/c/test.c +80 -38
  8. package/c/usearch.h +15 -15
  9. package/conanfile.py +1 -1
  10. package/include/usearch/index.hpp +5 -5
  11. package/include/usearch/index_dense.hpp +8 -4
  12. package/include/usearch/index_plugins.hpp +52 -32
  13. package/javascript/types/usearch.d.ts.map +1 -1
  14. package/javascript/usearch.js +4 -1
  15. package/package.json +3 -2
  16. package/simsimd/CONTRIBUTING.md +88 -0
  17. package/simsimd/Cargo.lock +4 -3
  18. package/simsimd/Cargo.toml +10 -2
  19. package/simsimd/README.md +102 -60
  20. package/simsimd/include/simsimd/binary.h +44 -0
  21. package/simsimd/include/simsimd/complex.h +174 -0
  22. package/simsimd/include/simsimd/simsimd.h +18 -9
  23. package/simsimd/include/simsimd/spatial.h +128 -36
  24. package/simsimd/include/simsimd/types.h +3 -2
  25. package/simsimd/package-lock.json +5 -5
  26. package/simsimd/package.json +1 -1
  27. package/simsimd/rust/benches/native.rs +12 -9
  28. package/simsimd/rust/lib.c +32 -55
  29. package/simsimd/rust/lib.rs +374 -56
  30. package/stringzilla/CONTRIBUTING.md +17 -0
  31. package/stringzilla/Cargo.lock +1 -1
  32. package/stringzilla/Cargo.toml +1 -1
  33. package/stringzilla/README.md +175 -34
  34. package/stringzilla/VERSION +1 -1
  35. package/stringzilla/build.rs +77 -4
  36. package/stringzilla/c/lib.c +33 -0
  37. package/stringzilla/cli/README.md +30 -13
  38. package/stringzilla/cli/__init__.py +0 -0
  39. package/stringzilla/cli/split.py +66 -33
  40. package/stringzilla/cli/wc.py +101 -21
  41. package/stringzilla/include/stringzilla/experimental.h +122 -1
  42. package/stringzilla/include/stringzilla/stringzilla.h +262 -132
  43. package/stringzilla/include/stringzilla/stringzilla.hpp +13 -5
  44. package/stringzilla/package.json +1 -1
  45. package/stringzilla/rust/lib.rs +673 -162
  46. package/wasmer.toml +1 -1
package/CITATION.cff CHANGED
@@ -5,7 +5,7 @@ authors:
5
5
  given-names: "Ash"
6
6
  orcid: "https://orcid.org/0000-0002-4882-1815"
7
7
  title: "USearch by Unum Cloud"
8
- version: 2.9.0
8
+ version: 2.9.2
9
9
  doi: 10.5281/zenodo.7949416
10
10
  date-released: 2023-10-22
11
11
  url: "https://github.com/unum-cloud/usearch"
package/CONTRIBUTING.md CHANGED
@@ -104,6 +104,13 @@ cppcheck --enable=all --force --suppress=cstyleCast --suppress=unusedFunction \
104
104
  include/index_plugins.hpp
105
105
  ```
106
106
 
107
+ I'd recommend putting the following breakpoints when debugging the code in GDB:
108
+
109
+ - `__asan::ReportGenericError` - to detect illegal memory accesses.
110
+ - `__ubsan::ScopedReport::~ScopedReport` - to catch undefined behavior.
111
+ - `__GI_exit` - to stop at exit points - the end of running any executable.
112
+ - `__builtin_unreachable` - to catch all the places where the code is expected to be unreachable.
113
+
107
114
  ## Python 3
108
115
 
109
116
  Python bindings are built using PyBind11 and are available on [PyPi](https://pypi.org/project/usearch/).
@@ -215,10 +222,11 @@ USearch provides both Objective-C and Swift bindings through the [Swift Package
215
222
  The compilation settings are controlled by the `Package.swift` and are independent from CMake used for C/C++ builds.
216
223
 
217
224
  ```sh
218
- swift build
219
- swift test -v
225
+ swift build && swift test -v
220
226
  ```
221
227
 
228
+ Those depend on Apple's `Foundation` library and can only run on Apple devices.
229
+
222
230
  ## GoLang
223
231
 
224
232
  USearch provides GoLang bindings, that depend on the C library that must be installed beforehand.
@@ -291,6 +299,32 @@ cp cloud/unum/usearch/libusearch.* .
291
299
  java -cp . -Djava.library.path="$(pwd)" cloud.unum.usearch.Index
292
300
  ```
293
301
 
302
+ ## C#
303
+
304
+ Setup the .NET environment:
305
+
306
+ ```sh
307
+ dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
308
+ ```
309
+
310
+ USearch provides CSharp bindings, that depend on the C library that must be installed beforehand.
311
+ So one should first compile the C library, link it with CSharp, and only then run tests.
312
+
313
+ ```sh
314
+ cmake -B ./build_artifacts -DUSEARCH_BUILD_LIB_C=1 -DUSEARCH_BUILD_TEST_C=1 -DUSEARCH_USE_OPENMP=1 -DUSEARCH_USE_SIMSIMD=1
315
+ cmake --build ./build_artifacts --config Release -j
316
+ ```
317
+
318
+ Then, on Windows, copy the library to the CSharp project and run the tests:
319
+
320
+ ```sh
321
+ mkdir -p ".\csharp\lib\runtimes\win-x64\native"
322
+ cp ".\build_artifacts\libusearch_c.dll" ".\csharp\lib\runtimes\win-x64\native"
323
+ cd csharp
324
+ dotnet test -c Debug --logger "console;verbosity=detailed"
325
+ dotnet test -c Release
326
+ ```
327
+
294
328
  ## Wolfram
295
329
 
296
330
  ```sh
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "2.9.0"
3
+ version = "2.9.2"
4
4
  authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
5
5
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
6
6
  edition = "2021"
package/README.md CHANGED
@@ -30,19 +30,18 @@ Spatial • Binary • Probabilistic • User-Defined Metrics
30
30
  <a href="https://unum-cloud.github.io/usearch/swift">Swift</a> •
31
31
  <a href="https://unum-cloud.github.io/usearch/csharp">C#</a> •
32
32
  <a href="https://unum-cloud.github.io/usearch/golang">GoLang</a> •
33
- <a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a> •
34
- <a href="https://unum-cloud.github.io/usearch/sqlite">SQLite3</a>
33
+ <a href="https://unum-cloud.github.io/usearch/wolfram">Wolfram</a>
35
34
  <br/>
36
- Linux • MacOS • Windows • iOS • WebAssembly
35
+ Linux • MacOS • Windows • iOS • WebAssembly •
36
+ <a href="https://unum-cloud.github.io/usearch/sqlite">SQLite3</a>
37
37
  </p>
38
38
 
39
39
  <div align="center">
40
- <a href="https://pepy.tech/project/usearch"> <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/usearch?period=total&units=abbreviation&left_color=black&right_color=blue&left_text=USearch%20Python%20installs"> </a>
41
- <a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=NPM%20installs"> </a>
42
- <a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=Crate%20installs"> </a>
43
- <a href="https://www.nuget.org/packages/Cloud.Unum.USearch"> <img alt="NuGet" src="https://img.shields.io/nuget/dt/Cloud.Unum.USearch?label=NuGet%20installs"> </a>
44
- <a href="https://central.sonatype.com/artifact/cloud.unum/usearch/overview"> <img alt="Maven" src="https://img.shields.io/nexus/r/cloud.unum/usearch?server=https%3A%2F%2Fs01.oss.sonatype.org%2F&label=Maven%20version"> </a>
45
- <a href="https://hub.docker.com/r/unum/usearch"> <img alt="Docker" src="https://img.shields.io/docker/pulls/unum/usearch?label=Docker%20installs"> </a>
40
+ <a href="https://pepy.tech/project/usearch"> <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/usearch?period=total&units=abbreviation&left_color=black&right_color=blue&left_text=Python%20PyPi%20installs"> </a>
41
+ <a href="https://www.npmjs.com/package/usearch"> <img alt="NPM" src="https://img.shields.io/npm/dy/usearch?label=JavaScript%20NPM%20installs"> </a>
42
+ <a href="https://crates.io/crates/usearch"> <img alt="Crate" src="https://img.shields.io/crates/d/usearch?label=Rust%20Crate%20installs"> </a>
43
+ <a href="https://www.nuget.org/packages/Cloud.Unum.USearch"> <img alt="NuGet" src="https://img.shields.io/nuget/dt/Cloud.Unum.USearch?label=CSharp%20NuGet%20installs"> </a>
44
+ <a href="https://central.sonatype.com/artifact/cloud.unum/usearch/overview"> <img alt="Maven" src="https://img.shields.io/nexus/r/cloud.unum/usearch?server=https%3A%2F%2Fs01.oss.sonatype.org%2F&label=Java%20Maven%20version"> </a>
46
45
  <img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/unum-cloud/usearch?label=Repo%20size">
47
46
  </div>
48
47
 
@@ -50,18 +49,16 @@ Linux • MacOS • Windows • iOS • WebAssembly
50
49
 
51
50
  - ✅ __[10x faster][faster-than-faiss]__ [HNSW][hnsw-algorithm] implementation than [FAISS][faiss].
52
51
  - ✅ Simple and extensible [single C++11 header][usearch-header] __library__.
53
- - ✅ Compatible with a dozen programming languages out of the box.
54
52
  - ✅ [Trusted](#integrations) by giants like Google and databases like [ClickHouse][clickhouse-docs].
55
53
  - ✅ [SIMD][simd]-optimized and [user-defined metrics](#user-defined-functions) with JIT compilation.
56
54
  - ✅ Hardware-agnostic `f16` & `i8` - [half-precision & quarter-precision support](#memory-efficiency-downcasting-and-quantization).
57
55
  - ✅ [View large indexes from disk](#serving-index-from-disk) without loading into RAM.
58
56
  - ✅ Heterogeneous lookups, renaming/relabeling, and on-the-fly deletions.
59
- - ✅ Variable dimensionality vectors for unique applications, including search over compressed data.
60
57
  - ✅ Binary Tanimoto and Sorensen coefficients for [Genomics and Chemistry applications](#usearch--rdkit--molecular-search).
61
58
  - ✅ Space-efficient point-clouds with `uint40_t`, accommodating 4B+ size.
62
- - ✅ Compatible with OpenMP and custom "executors" for fine-grained control over CPU utilization.
63
- - ✅ Near-real-time [clustering and sub-clustering](#clustering) for Tens or Millions of clusters.
59
+ - ✅ Compatible with OpenMP and custom "executors" for fine-grained parallelism.
64
60
  - ✅ [Semantic Search](#usearch--ai--multi-modal-semantic-search) and [Joins](#joins-one-to-one-one-to-many-and-many-to-many-mappings).
61
+ - 🔄 Near-real-time [clustering and sub-clustering](#clustering) for Tens or Millions of clusters.
65
62
 
66
63
  [faiss]: https://github.com/facebookresearch/faiss
67
64
  [usearch-header]: https://github.com/unum-cloud/usearch/blob/main/include/usearch/index.hpp
@@ -133,7 +130,7 @@ matches = index.search(vector, 10)
133
130
 
134
131
  assert matches[0].key == 42
135
132
  assert matches[0].distance <= 0.001
136
- assert np.allclose(index[42], vector)
133
+ assert np.allclose(index[42], vector, atol=0.1) # Ensure high tolerance in mixed-precision comparisons
137
134
  ```
138
135
 
139
136
  More settings are always available, and the API is designed to be as flexible as possible.
@@ -434,7 +431,7 @@ doi = {10.5281/zenodo.7949416},
434
431
  author = {Vardanian, Ash},
435
432
  title = {{USearch by Unum Cloud}},
436
433
  url = {https://github.com/unum-cloud/usearch},
437
- version = {2.9.0},
434
+ version = {2.9.2},
438
435
  year = {2023},
439
436
  month = oct,
440
437
  }
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.9.0
1
+ 2.9.2
package/c/lib.cpp CHANGED
@@ -106,7 +106,7 @@ extern "C" {
106
106
 
107
107
  USEARCH_EXPORT usearch_index_t usearch_init(usearch_init_options_t* options, usearch_error_t* error) {
108
108
 
109
- assert(options && error);
109
+ assert(options && error && "Missing arguments");
110
110
 
111
111
  index_dense_config_t config(options->connectivity, options->expansion_add, options->expansion_search);
112
112
  config.multi = options->multi;
@@ -132,13 +132,13 @@ USEARCH_EXPORT void usearch_free(usearch_index_t index, usearch_error_t*) {
132
132
  }
133
133
 
134
134
  USEARCH_EXPORT size_t usearch_serialized_length(usearch_index_t index, usearch_error_t*) {
135
- assert(index);
135
+ assert(index && "Missing arguments");
136
136
  return reinterpret_cast<index_dense_t*>(index)->serialized_length();
137
137
  }
138
138
 
139
139
  USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearch_error_t* error) {
140
140
 
141
- assert(index && path && error);
141
+ assert(index && path && error && "Missing arguments");
142
142
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(path);
143
143
  if (!result)
144
144
  *error = result.error.release();
@@ -146,7 +146,7 @@ USEARCH_EXPORT void usearch_save(usearch_index_t index, char const* path, usearc
146
146
 
147
147
  USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearch_error_t* error) {
148
148
 
149
- assert(index && path && error);
149
+ assert(index && path && error && "Missing arguments");
150
150
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(path);
151
151
  if (!result)
152
152
  *error = result.error.release();
@@ -154,7 +154,7 @@ USEARCH_EXPORT void usearch_load(usearch_index_t index, char const* path, usearc
154
154
 
155
155
  USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearch_error_t* error) {
156
156
 
157
- assert(index && path && error);
157
+ assert(index && path && error && "Missing arguments");
158
158
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(path);
159
159
  if (!result)
160
160
  *error = result.error.release();
@@ -162,7 +162,7 @@ USEARCH_EXPORT void usearch_view(usearch_index_t index, char const* path, usearc
162
162
 
163
163
  USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* options, usearch_error_t* error) {
164
164
 
165
- assert(path && options && error);
165
+ assert(path && options && error && "Missing arguments");
166
166
  index_dense_metadata_result_t result = index_dense_metadata_from_path(path);
167
167
  if (!result)
168
168
  *error = result.error.release();
@@ -180,7 +180,7 @@ USEARCH_EXPORT void usearch_metadata(char const* path, usearch_init_options_t* o
180
180
 
181
181
  USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, size_t length, usearch_error_t* error) {
182
182
 
183
- assert(index && buffer && length && error);
183
+ assert(index && buffer && length && error && "Missing arguments");
184
184
  memory_mapped_file_t memory_map((byte_t*)buffer, length);
185
185
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->save(std::move(memory_map));
186
186
  if (!result)
@@ -190,7 +190,7 @@ USEARCH_EXPORT void usearch_save_buffer(usearch_index_t index, void* buffer, siz
190
190
  USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffer, size_t length,
191
191
  usearch_error_t* error) {
192
192
 
193
- assert(index && buffer && length && error);
193
+ assert(index && buffer && length && error && "Missing arguments");
194
194
  memory_mapped_file_t memory_map((byte_t*)buffer, length);
195
195
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->load(std::move(memory_map));
196
196
  if (!result)
@@ -200,7 +200,7 @@ USEARCH_EXPORT void usearch_load_buffer(usearch_index_t index, void const* buffe
200
200
  USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffer, size_t length,
201
201
  usearch_error_t* error) {
202
202
 
203
- assert(index && buffer && length && error);
203
+ assert(index && buffer && length && error && "Missing arguments");
204
204
  memory_mapped_file_t memory_map((byte_t*)buffer, length);
205
205
  serialization_result_t result = reinterpret_cast<index_dense_t*>(index)->view(std::move(memory_map));
206
206
  if (!result)
@@ -210,7 +210,7 @@ USEARCH_EXPORT void usearch_view_buffer(usearch_index_t index, void const* buffe
210
210
  USEARCH_EXPORT void usearch_metadata_buffer(void const* buffer, size_t length, usearch_init_options_t* options,
211
211
  usearch_error_t* error) {
212
212
 
213
- assert(buffer && length && options && error);
213
+ assert(buffer && length && options && error && "Missing arguments");
214
214
  index_dense_metadata_result_t result =
215
215
  index_dense_metadata_from_buffer(memory_mapped_file_t((byte_t*)(buffer), length));
216
216
  if (!result)
@@ -244,7 +244,7 @@ USEARCH_EXPORT size_t usearch_connectivity(usearch_index_t index, usearch_error_
244
244
  }
245
245
 
246
246
  USEARCH_EXPORT void usearch_reserve(usearch_index_t index, size_t capacity, usearch_error_t* error) {
247
- assert(index && error);
247
+ assert(index && error && "Missing arguments");
248
248
  if (!reinterpret_cast<index_dense_t*>(index)->reserve(capacity))
249
249
  *error = "Out of memory!";
250
250
  }
@@ -253,19 +253,19 @@ USEARCH_EXPORT void usearch_add(
253
253
  usearch_index_t index, usearch_key_t key, void const* vector, usearch_scalar_kind_t kind, //
254
254
  usearch_error_t* error) {
255
255
 
256
- assert(index && vector && error);
256
+ assert(index && vector && error && "Missing arguments");
257
257
  add_result_t result = add_(reinterpret_cast<index_dense_t*>(index), key, vector, scalar_kind_to_cpp(kind));
258
258
  if (!result)
259
259
  *error = result.error.release();
260
260
  }
261
261
 
262
262
  USEARCH_EXPORT bool usearch_contains(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
263
- assert(index);
263
+ assert(index && "Missing arguments");
264
264
  return reinterpret_cast<index_dense_t*>(index)->contains(key);
265
265
  }
266
266
 
267
267
  USEARCH_EXPORT size_t usearch_count(usearch_index_t index, usearch_key_t key, usearch_error_t*) {
268
- assert(index);
268
+ assert(index && "Missing arguments");
269
269
  return reinterpret_cast<index_dense_t*>(index)->count(key);
270
270
  }
271
271
 
@@ -273,7 +273,7 @@ USEARCH_EXPORT size_t usearch_search(
273
273
  usearch_index_t index, void const* vector, usearch_scalar_kind_t kind, size_t results_limit, //
274
274
  usearch_key_t* found_keys, usearch_distance_t* found_distances, usearch_error_t* error) {
275
275
 
276
- assert(index && vector && error);
276
+ assert(index && vector && error && "Missing arguments");
277
277
  search_result_t result =
278
278
  search_(reinterpret_cast<index_dense_t*>(index), vector, scalar_kind_to_cpp(kind), results_limit);
279
279
  if (!result) {
@@ -294,7 +294,7 @@ USEARCH_EXPORT size_t usearch_get( //
294
294
 
295
295
  USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, usearch_error_t* error) {
296
296
 
297
- assert(index && error);
297
+ assert(index && error && "Missing arguments");
298
298
  labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->remove(key);
299
299
  if (!result)
300
300
  *error = result.error.release();
@@ -304,7 +304,7 @@ USEARCH_EXPORT size_t usearch_remove(usearch_index_t index, usearch_key_t key, u
304
304
  USEARCH_EXPORT size_t usearch_rename( //
305
305
  usearch_index_t index, usearch_key_t from, usearch_key_t to, usearch_error_t* error) {
306
306
 
307
- assert(index && error);
307
+ assert(index && error && "Missing arguments");
308
308
  labeling_result_t result = reinterpret_cast<index_dense_t*>(index)->rename(from, to);
309
309
  if (!result)
310
310
  *error = result.error.release();
@@ -330,6 +330,8 @@ USEARCH_EXPORT void usearch_exact_search( //
330
330
  usearch_distance_t* distances, size_t distances_stride, //
331
331
  usearch_error_t* error) {
332
332
 
333
+ assert(dataset && queries && keys && distances && error && "Missing arguments");
334
+
333
335
  metric_punned_t metric(dimensions, metric_kind_to_cpp(metric_kind), scalar_kind_to_cpp(scalar_kind));
334
336
  executor_default_t executor(threads);
335
337
  static exact_search_t search;
package/c/test.c CHANGED
@@ -1,5 +1,5 @@
1
1
  #include <errno.h>
2
- #include <stdio.h>
2
+ #include <stdio.h> // `remove`
3
3
  #include <stdlib.h>
4
4
  #include <sys/stat.h>
5
5
 
@@ -35,6 +35,7 @@ usearch_init_options_t create_options(size_t const dimensions) {
35
35
  opts.metric_kind = usearch_metric_ip_k;
36
36
  opts.metric = NULL;
37
37
  opts.quantization = usearch_scalar_f32_k;
38
+ opts.multi = false;
38
39
  return opts;
39
40
  }
40
41
 
@@ -45,7 +46,7 @@ usearch_init_options_t create_options(size_t const dimensions) {
45
46
  * dimensions, and connectivity after each operation.
46
47
  */
47
48
  void test_init(size_t const collection_size, size_t const dimensions) {
48
- printf("Test: Index Initialization...\n");
49
+ printf("Test: Index Initialization... %zu vectors, %zu dimensions \n", collection_size, dimensions);
49
50
 
50
51
  // Init index
51
52
  usearch_error_t error = NULL;
@@ -84,7 +85,7 @@ void test_init(size_t const collection_size, size_t const dimensions) {
84
85
  * size, capacity, and presence of each vector in the index.
85
86
  */
86
87
  void test_add_vector(size_t const collection_size, size_t const dimensions) {
87
- printf("Test: Add Vector...\n");
88
+ printf("Test: Add Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
88
89
 
89
90
  usearch_error_t error = NULL;
90
91
  usearch_init_options_t opts = create_options(dimensions);
@@ -120,7 +121,7 @@ void test_add_vector(size_t const collection_size, size_t const dimensions) {
120
121
  * found in the index, validating the count of found vectors.
121
122
  */
122
123
  void test_find_vector(size_t const collection_size, size_t const dimensions) {
123
- printf("Test: Find Vector...\n");
124
+ printf("Test: Find Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
124
125
 
125
126
  usearch_error_t error = NULL;
126
127
  usearch_init_options_t opts = create_options(dimensions);
@@ -161,7 +162,7 @@ void test_find_vector(size_t const collection_size, size_t const dimensions) {
161
162
  * retrieves vectors associated with the key from the index and checks the count of retrieved vectors.
162
163
  */
163
164
  void test_get_vector(size_t const collection_size, size_t const dimensions) {
164
- printf("Test: Get Vector...\n");
165
+ printf("Test: Get Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
165
166
 
166
167
  usearch_error_t error = NULL;
167
168
  usearch_init_options_t opts = create_options(dimensions);
@@ -198,7 +199,7 @@ void test_get_vector(size_t const collection_size, size_t const dimensions) {
198
199
  * the assert in this test expects an error, indicating that the remove functionality is not currently supported.
199
200
  */
200
201
  void test_remove_vector(size_t const collection_size, size_t const dimensions) {
201
- printf("Test: Remove Vector...\n");
202
+ printf("Test: Remove Vector... %zu vectors, %zu dimensions \n", collection_size, dimensions);
202
203
 
203
204
  usearch_error_t error = NULL;
204
205
  usearch_init_options_t opts = create_options(dimensions);
@@ -232,48 +233,89 @@ void test_remove_vector(size_t const collection_size, size_t const dimensions) {
232
233
  * from the saved index.
233
234
  */
234
235
  void test_save_load(size_t const collection_size, size_t const dimensions) {
235
- printf("Test: Save/Load...\n");
236
+ printf("Test: Save/Load... %zu vectors, %zu dimensions \n", collection_size, dimensions);
237
+ float* data = create_vectors(collection_size, dimensions);
236
238
 
237
239
  usearch_error_t error = NULL;
238
240
  usearch_init_options_t opts = create_options(dimensions);
239
- usearch_index_t index = usearch_init(&opts, &error);
240
- usearch_reserve(index, collection_size, &error);
241
+ usearch_init_options_t weird_ops = opts;
242
+ weird_ops.connectivity = 11;
243
+ weird_ops.expansion_add = 15;
244
+ weird_ops.expansion_search = 19;
245
+ weird_ops.metric_kind = usearch_metric_pearson_k;
246
+ weird_ops.quantization = usearch_scalar_f64_k;
247
+
248
+ {
249
+
250
+ usearch_index_t index = usearch_init(&weird_ops, &error);
251
+ usearch_reserve(index, collection_size, &error);
252
+
253
+ // Add vectors
254
+ for (size_t i = 0; i < collection_size; ++i) {
255
+ usearch_key_t key = i;
256
+ usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
257
+ ASSERT(!error, error);
258
+ }
241
259
 
242
- // Add vectors
243
- float* data = create_vectors(collection_size, dimensions);
244
- for (size_t i = 0; i < collection_size; ++i) {
245
- usearch_key_t key = i;
246
- usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
260
+ // Save and free the index
261
+ usearch_save(index, "tmp.usearch", &error);
262
+ ASSERT(!error, error);
263
+ usearch_free(index, &error);
247
264
  ASSERT(!error, error);
248
265
  }
249
266
 
250
- // Save and free the index
251
- usearch_save(index, "usearch_index.bin", &error);
252
- ASSERT(!error, error);
253
- usearch_free(index, &error);
254
- ASSERT(!error, error);
267
+ // Reset the options
268
+ opts.connectivity = 0;
269
+ opts.dimensions = 0;
270
+ opts.expansion_add = 0;
271
+ opts.expansion_search = 0;
272
+ opts.metric = NULL;
273
+ opts.quantization = usearch_scalar_unknown_k;
274
+ opts.metric_kind = usearch_metric_unknown_k;
255
275
 
256
276
  // Reinit
257
- index = usearch_init(&opts, &error);
258
- ASSERT(!error, error);
259
- ASSERT(usearch_size(index, &error) == 0, error);
277
+ {
260
278
 
261
- // Load
262
- usearch_load(index, "usearch_index.bin", &error);
263
- ASSERT(!error, error);
264
- ASSERT(usearch_size(index, &error) == collection_size, error);
265
- ASSERT(usearch_capacity(index, &error) == collection_size, error);
266
- ASSERT(usearch_dimensions(index, &error) == dimensions, error);
267
- ASSERT(usearch_connectivity(index, &error) == opts.connectivity, error);
279
+ usearch_index_t index = usearch_init(&opts, &error);
280
+ ASSERT(!error, error);
281
+ ASSERT(usearch_size(index, &error) == 0, error);
268
282
 
269
- // Check vectors in the index
270
- for (size_t i = 0; i < collection_size; ++i) {
271
- usearch_key_t key = i;
272
- ASSERT(usearch_contains(index, key, &error), error);
283
+ // Load
284
+ usearch_load(index, "tmp.usearch", &error);
285
+ ASSERT(!error, error);
286
+ ASSERT(usearch_size(index, &error) == collection_size, error);
287
+ ASSERT(usearch_capacity(index, &error) == collection_size, error);
288
+ ASSERT(usearch_dimensions(index, &error) == dimensions, error);
289
+ ASSERT(usearch_connectivity(index, &error) == weird_ops.connectivity, error);
290
+
291
+ // Check vectors in the index
292
+ for (size_t i = 0; i < collection_size; ++i) {
293
+ usearch_key_t key = i;
294
+ ASSERT(usearch_contains(index, key, &error), error);
295
+ }
296
+
297
+ // Create result buffers
298
+ usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
299
+ float* distances = (float*)malloc(collection_size * sizeof(float));
300
+ ASSERT(keys && distances, "Failed to allocate memory");
301
+
302
+ // Find the vectors
303
+ for (size_t i = 0; i < collection_size; i++) {
304
+ size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size,
305
+ keys, distances, &error);
306
+ ASSERT(!error, error);
307
+ ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
308
+ }
309
+
310
+ free(keys);
311
+ free(distances);
312
+ usearch_free(index, &error);
273
313
  }
274
314
 
275
315
  free(data);
276
- usearch_free(index, &error);
316
+
317
+ // Remove the file from disk
318
+ remove("tmp.usearch");
277
319
  printf("Test: Save/Load - PASSED\n");
278
320
  }
279
321
 
@@ -284,7 +326,7 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
284
326
  * not verify the properties or contents of the viewed index.
285
327
  */
286
328
  void test_view(size_t const collection_size, size_t const dimensions) {
287
- printf("Test: View...\n");
329
+ printf("Test: View... %zu vectors, %zu dimensions \n", collection_size, dimensions);
288
330
 
289
331
  usearch_error_t error = NULL;
290
332
  usearch_init_options_t opts = create_options(dimensions);
@@ -300,7 +342,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
300
342
  }
301
343
 
302
344
  // Save and free the index
303
- usearch_save(index, "usearch_index.bin", &error);
345
+ usearch_save(index, "tmp.usearch", &error);
304
346
  ASSERT(!error, error);
305
347
  usearch_free(index, &error);
306
348
  ASSERT(!error, error);
@@ -310,7 +352,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
310
352
  ASSERT(!error, error);
311
353
 
312
354
  // View
313
- usearch_view(index, "usearch_index.bin", &error);
355
+ usearch_view(index, "tmp.usearch", &error);
314
356
  ASSERT(!error, error);
315
357
 
316
358
  free(data);
@@ -321,7 +363,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
321
363
  int main(int argc, char const* argv[]) {
322
364
 
323
365
  size_t collection_sizes[] = {11, 512};
324
- size_t dimensions[] = {83, 1};
366
+ size_t dimensions[] = {83, 2}; // Not all distance functions make sense for 1 dimensional data
325
367
  for (size_t index = 0; index < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++index) {
326
368
  for (size_t jdx = 0; jdx < sizeof(dimensions) / sizeof(dimensions[0]); ++jdx) {
327
369
  test_init(collection_sizes[index], dimensions[jdx]);
package/c/usearch.h CHANGED
@@ -39,25 +39,25 @@ USEARCH_EXPORT typedef usearch_distance_t (*usearch_metric_t)(void const*, void
39
39
  */
40
40
  USEARCH_EXPORT typedef enum usearch_metric_kind_t {
41
41
  usearch_metric_unknown_k = 0,
42
- usearch_metric_cos_k,
43
- usearch_metric_ip_k,
44
- usearch_metric_l2sq_k,
45
- usearch_metric_haversine_k,
46
- usearch_metric_divergence_k,
47
- usearch_metric_pearson_k,
48
- usearch_metric_jaccard_k,
49
- usearch_metric_hamming_k,
50
- usearch_metric_tanimoto_k,
51
- usearch_metric_sorensen_k,
42
+ usearch_metric_cos_k = 1,
43
+ usearch_metric_ip_k = 2,
44
+ usearch_metric_l2sq_k = 3,
45
+ usearch_metric_haversine_k = 4,
46
+ usearch_metric_divergence_k = 5,
47
+ usearch_metric_pearson_k = 6,
48
+ usearch_metric_jaccard_k = 7,
49
+ usearch_metric_hamming_k = 8,
50
+ usearch_metric_tanimoto_k = 9,
51
+ usearch_metric_sorensen_k = 10,
52
52
  } usearch_metric_kind_t;
53
53
 
54
54
  USEARCH_EXPORT typedef enum usearch_scalar_kind_t {
55
55
  usearch_scalar_unknown_k = 0,
56
- usearch_scalar_f32_k,
57
- usearch_scalar_f64_k,
58
- usearch_scalar_f16_k,
59
- usearch_scalar_i8_k,
60
- usearch_scalar_b1_k,
56
+ usearch_scalar_f32_k = 1,
57
+ usearch_scalar_f64_k = 2,
58
+ usearch_scalar_f16_k = 3,
59
+ usearch_scalar_i8_k = 4,
60
+ usearch_scalar_b1_k = 5,
61
61
  } usearch_scalar_kind_t;
62
62
 
63
63
  USEARCH_EXPORT typedef struct usearch_init_options_t {
package/conanfile.py CHANGED
@@ -7,7 +7,7 @@ required_conan_version = ">=1.53.0"
7
7
  class USearchConan(ConanFile):
8
8
 
9
9
  name = "usearch"
10
- version = '2.9.0'
10
+ version = '2.9.2'
11
11
  license = "Apache-2.0"
12
12
  description = "Smaller & Faster Single-File Vector Search Engine from Unum"
13
13
  homepage = "https://github.com/unum-cloud/usearch"
@@ -11,7 +11,7 @@
11
11
 
12
12
  #define USEARCH_VERSION_MAJOR 2
13
13
  #define USEARCH_VERSION_MINOR 9
14
- #define USEARCH_VERSION_PATCH 0
14
+ #define USEARCH_VERSION_PATCH 2
15
15
 
16
16
  // Inferring C++ version
17
17
  // https://stackoverflow.com/a/61552074
@@ -2543,8 +2543,8 @@ class index_gt {
2543
2543
  if (!next.reserve(expansion))
2544
2544
  return result.failed("Out of memory!");
2545
2545
 
2546
- result.cluster.member =
2547
- at(search_for_one_(query, metric, prefetch, entry_slot_, max_level_, level - 1, context));
2546
+ result.cluster.member = at(search_for_one_(query, metric, prefetch, entry_slot_, max_level_,
2547
+ static_cast<level_t>(level - 1), context));
2548
2548
  result.cluster.distance = context.measure(query, result.cluster.member, metric);
2549
2549
 
2550
2550
  // Normalize stats
@@ -3422,7 +3422,7 @@ class index_gt {
3422
3422
  distance_t radius = context.measure(query, citerator_at(start_slot), metric);
3423
3423
  next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
3424
3424
  top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
3425
- visits.set(start_slot);
3425
+ visits.set(static_cast<compressed_slot_t>(start_slot));
3426
3426
 
3427
3427
  while (!next.empty()) {
3428
3428
 
@@ -3496,7 +3496,7 @@ class index_gt {
3496
3496
  distance_t radius = context.measure(query, citerator_at(start_slot), metric);
3497
3497
  next.insert_reserved({-radius, static_cast<compressed_slot_t>(start_slot)});
3498
3498
  top.insert_reserved({radius, static_cast<compressed_slot_t>(start_slot)});
3499
- visits.set(start_slot);
3499
+ visits.set(static_cast<compressed_slot_t>(start_slot));
3500
3500
 
3501
3501
  while (!next.empty()) {
3502
3502
 
@@ -199,7 +199,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
199
199
 
200
200
  // Check if it starts with 32-bit
201
201
  if (offset_if_u32 + sizeof(index_dense_head_buffer_t) < file_size) {
202
- if (std::fseek(file.get(), offset_if_u32, SEEK_SET) != 0)
202
+ if (std::fseek(file.get(), static_cast<long>(offset_if_u32), SEEK_SET) != 0)
203
203
  return result.failed(std::strerror(errno));
204
204
  read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
205
205
  if (!read)
@@ -213,7 +213,7 @@ inline index_dense_metadata_result_t index_dense_metadata_from_path(char const*
213
213
 
214
214
  // Check if it starts with 64-bit
215
215
  if (offset_if_u64 + sizeof(index_dense_head_buffer_t) < file_size) {
216
- if (std::fseek(file.get(), offset_if_u64, SEEK_SET) != 0)
216
+ if (std::fseek(file.get(), static_cast<long>(offset_if_u64), SEEK_SET) != 0)
217
217
  return result.failed(std::strerror(errno));
218
218
  read = std::fread(result.head_buffer, sizeof(index_dense_head_buffer_t), 1, file.get());
219
219
  if (!read)
@@ -929,8 +929,10 @@ class index_dense_gt {
929
929
  if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
930
930
  return result.failed("Slot type doesn't match, consider rebuilding");
931
931
 
932
- metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
933
932
  config_.multi = head.multi;
933
+ metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
934
+ cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
935
+ casts_ = make_casts_(head.kind_scalar);
934
936
  }
935
937
 
936
938
  // Pull the actual proximity graph
@@ -1013,8 +1015,10 @@ class index_dense_gt {
1013
1015
  if (head.kind_compressed_slot != unum::usearch::scalar_kind<compressed_slot_t>())
1014
1016
  return result.failed("Slot type doesn't match, consider rebuilding");
1015
1017
 
1016
- metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
1017
1018
  config_.multi = head.multi;
1019
+ metric_ = metric_t(head.dimensions, head.kind_metric, head.kind_scalar);
1020
+ cast_buffer_.resize(available_threads_.size() * metric_.bytes_per_vector());
1021
+ casts_ = make_casts_(head.kind_scalar);
1018
1022
  offset += sizeof(buffer);
1019
1023
  }
1020
1024