usearch 2.9.1 → 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 (43) hide show
  1. package/CITATION.cff +1 -1
  2. package/CONTRIBUTING.md +10 -2
  3. package/Cargo.toml +1 -1
  4. package/README.md +12 -15
  5. package/VERSION +1 -1
  6. package/c/test.c +58 -50
  7. package/conanfile.py +1 -1
  8. package/include/usearch/index.hpp +1 -1
  9. package/include/usearch/index_plugins.hpp +20 -10
  10. package/javascript/types/usearch.d.ts.map +1 -1
  11. package/javascript/usearch.js +4 -1
  12. package/package.json +1 -1
  13. package/simsimd/CONTRIBUTING.md +88 -0
  14. package/simsimd/Cargo.lock +4 -3
  15. package/simsimd/Cargo.toml +10 -2
  16. package/simsimd/README.md +102 -60
  17. package/simsimd/include/simsimd/binary.h +44 -0
  18. package/simsimd/include/simsimd/complex.h +174 -0
  19. package/simsimd/include/simsimd/simsimd.h +18 -9
  20. package/simsimd/include/simsimd/spatial.h +128 -36
  21. package/simsimd/include/simsimd/types.h +3 -2
  22. package/simsimd/package-lock.json +5 -5
  23. package/simsimd/package.json +1 -1
  24. package/simsimd/rust/benches/native.rs +12 -9
  25. package/simsimd/rust/lib.c +32 -55
  26. package/simsimd/rust/lib.rs +374 -56
  27. package/stringzilla/CONTRIBUTING.md +17 -0
  28. package/stringzilla/Cargo.lock +1 -1
  29. package/stringzilla/Cargo.toml +1 -1
  30. package/stringzilla/README.md +175 -34
  31. package/stringzilla/VERSION +1 -1
  32. package/stringzilla/build.rs +77 -4
  33. package/stringzilla/c/lib.c +33 -0
  34. package/stringzilla/cli/README.md +30 -13
  35. package/stringzilla/cli/__init__.py +0 -0
  36. package/stringzilla/cli/split.py +66 -33
  37. package/stringzilla/cli/wc.py +101 -21
  38. package/stringzilla/include/stringzilla/experimental.h +122 -1
  39. package/stringzilla/include/stringzilla/stringzilla.h +262 -132
  40. package/stringzilla/include/stringzilla/stringzilla.hpp +13 -5
  41. package/stringzilla/package.json +1 -1
  42. package/stringzilla/rust/lib.rs +673 -162
  43. 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.1
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.
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "usearch"
3
- version = "2.9.1"
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.1},
434
+ version = {2.9.2},
438
435
  year = {2023},
439
436
  month = oct,
440
437
  }
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.9.1
1
+ 2.9.2
package/c/test.c CHANGED
@@ -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,7 +233,8 @@ 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);
@@ -242,23 +244,26 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
242
244
  weird_ops.expansion_search = 19;
243
245
  weird_ops.metric_kind = usearch_metric_pearson_k;
244
246
  weird_ops.quantization = usearch_scalar_f64_k;
245
- usearch_index_t index = usearch_init(&weird_ops, &error);
246
- usearch_reserve(index, collection_size, &error);
247
247
 
248
- // Add vectors
249
- float* data = create_vectors(collection_size, dimensions);
250
- for (size_t i = 0; i < collection_size; ++i) {
251
- usearch_key_t key = i;
252
- usearch_add(index, key, data + i * dimensions, usearch_scalar_f32_k, &error);
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
+ }
259
+
260
+ // Save and free the index
261
+ usearch_save(index, "tmp.usearch", &error);
262
+ ASSERT(!error, error);
263
+ usearch_free(index, &error);
253
264
  ASSERT(!error, error);
254
265
  }
255
266
 
256
- // Save and free the index
257
- usearch_save(index, "tmp.usearch", &error);
258
- ASSERT(!error, error);
259
- usearch_free(index, &error);
260
- ASSERT(!error, error);
261
-
262
267
  // Reset the options
263
268
  opts.connectivity = 0;
264
269
  opts.dimensions = 0;
@@ -269,45 +274,48 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
269
274
  opts.metric_kind = usearch_metric_unknown_k;
270
275
 
271
276
  // Reinit
272
- index = usearch_init(&opts, &error);
273
- ASSERT(!error, error);
274
- ASSERT(usearch_size(index, &error) == 0, error);
277
+ {
275
278
 
276
- // Load
277
- usearch_load(index, "tmp.usearch", &error);
278
- ASSERT(!error, error);
279
- ASSERT(usearch_size(index, &error) == collection_size, error);
280
- ASSERT(usearch_capacity(index, &error) == collection_size, error);
281
- ASSERT(usearch_dimensions(index, &error) == dimensions, error);
282
- ASSERT(usearch_connectivity(index, &error) == weird_ops.connectivity, error);
279
+ usearch_index_t index = usearch_init(&opts, &error);
280
+ ASSERT(!error, error);
281
+ ASSERT(usearch_size(index, &error) == 0, error);
283
282
 
284
- // Check vectors in the index
285
- for (size_t i = 0; i < collection_size; ++i) {
286
- usearch_key_t key = i;
287
- ASSERT(usearch_contains(index, key, &error), error);
288
- }
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
+ }
289
296
 
290
- // Create result buffers
291
- usearch_key_t* keys = (usearch_key_t*)malloc(collection_size * sizeof(usearch_key_t));
292
- float* distances = (float*)malloc(collection_size * sizeof(float));
293
- ASSERT(keys && distances, "Failed to allocate memory");
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
+ }
294
309
 
295
- // Find the vectors
296
- for (size_t i = 0; i < collection_size; i++) {
297
- size_t found_count = usearch_search(index, data + i * dimensions, usearch_scalar_f32_k, collection_size, keys,
298
- distances, &error);
299
- ASSERT(!error, error);
300
- ASSERT(found_count >= 1 && found_count <= collection_size, "Vector is missing");
310
+ free(keys);
311
+ free(distances);
312
+ usearch_free(index, &error);
301
313
  }
302
314
 
303
315
  free(data);
304
- free(keys);
305
- free(distances);
306
- usearch_free(index, &error);
307
316
 
308
317
  // Remove the file from disk
309
318
  remove("tmp.usearch");
310
-
311
319
  printf("Test: Save/Load - PASSED\n");
312
320
  }
313
321
 
@@ -318,7 +326,7 @@ void test_save_load(size_t const collection_size, size_t const dimensions) {
318
326
  * not verify the properties or contents of the viewed index.
319
327
  */
320
328
  void test_view(size_t const collection_size, size_t const dimensions) {
321
- printf("Test: View...\n");
329
+ printf("Test: View... %zu vectors, %zu dimensions \n", collection_size, dimensions);
322
330
 
323
331
  usearch_error_t error = NULL;
324
332
  usearch_init_options_t opts = create_options(dimensions);
@@ -355,7 +363,7 @@ void test_view(size_t const collection_size, size_t const dimensions) {
355
363
  int main(int argc, char const* argv[]) {
356
364
 
357
365
  size_t collection_sizes[] = {11, 512};
358
- size_t dimensions[] = {83, 1};
366
+ size_t dimensions[] = {83, 2}; // Not all distance functions make sense for 1 dimensional data
359
367
  for (size_t index = 0; index < sizeof(collection_sizes) / sizeof(collection_sizes[0]); ++index) {
360
368
  for (size_t jdx = 0; jdx < sizeof(dimensions) / sizeof(dimensions[0]); ++jdx) {
361
369
  test_init(collection_sizes[index], dimensions[jdx]);
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.1'
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 1
14
+ #define USEARCH_VERSION_PATCH 2
15
15
 
16
16
  // Inferring C++ version
17
17
  // https://stackoverflow.com/a/61552074
@@ -926,7 +926,7 @@ class i8_converted_t {
926
926
  std::int8_t int8_{};
927
927
 
928
928
  public:
929
- constexpr static float divisor_k = 100.f;
929
+ constexpr static f32_t divisor_k = 100.f;
930
930
  constexpr static std::int8_t min_k = -100;
931
931
  constexpr static std::int8_t max_k = 100;
932
932
 
@@ -938,9 +938,9 @@ class i8_converted_t {
938
938
  inline i8_converted_t(i8_converted_t const&) = default;
939
939
  inline i8_converted_t& operator=(i8_converted_t const&) = default;
940
940
 
941
- inline operator float() const noexcept { return float(int8_) / divisor_k; }
942
- inline operator f16_t() const noexcept { return float(int8_) / divisor_k; }
943
- inline operator double() const noexcept { return double(int8_) / divisor_k; }
941
+ inline operator f16_t() const noexcept { return static_cast<f16_t>(f32_t(int8_) / divisor_k); }
942
+ inline operator f32_t() const noexcept { return f32_t(int8_) / divisor_k; }
943
+ inline operator f64_t() const noexcept { return f64_t(int8_) / divisor_k; }
944
944
  inline explicit operator bool() const noexcept { return int8_ > (max_k / 2); }
945
945
  inline explicit operator std::int8_t() const noexcept { return int8_; }
946
946
  inline explicit operator std::int16_t() const noexcept { return int8_; }
@@ -949,9 +949,9 @@ class i8_converted_t {
949
949
 
950
950
  inline i8_converted_t(f16_t v)
951
951
  : int8_(usearch::clamp<std::int8_t>(static_cast<std::int8_t>(v * divisor_k), min_k, max_k)) {}
952
- inline i8_converted_t(float v)
952
+ inline i8_converted_t(f32_t v)
953
953
  : int8_(usearch::clamp<std::int8_t>(static_cast<std::int8_t>(v * divisor_k), min_k, max_k)) {}
954
- inline i8_converted_t(double v)
954
+ inline i8_converted_t(f64_t v)
955
955
  : int8_(usearch::clamp<std::int8_t>(static_cast<std::int8_t>(v * divisor_k), min_k, max_k)) {}
956
956
  };
957
957
 
@@ -1165,13 +1165,20 @@ template <typename scalar_at = std::int32_t, typename result_at = float> struct
1165
1165
  };
1166
1166
 
1167
1167
  /**
1168
- * @brief Measures Pearson Correlation between two sequences.
1168
+ * @brief Measures Pearson Correlation between two sequences in a single pass.
1169
1169
  */
1170
1170
  template <typename scalar_at = float, typename result_at = float> struct metric_pearson_gt {
1171
1171
  using scalar_t = scalar_at;
1172
1172
  using result_t = result_at;
1173
1173
 
1174
1174
  inline result_t operator()(scalar_t const* a, scalar_t const* b, std::size_t dim) const noexcept {
1175
+ // The correlation coefficient can't be defined for one or zero-dimensional data.
1176
+ if (dim <= 1)
1177
+ return 0;
1178
+ // Conventional Pearson Correlation Coefficient definiton subtracts the mean value of each
1179
+ // sequence from each element, before dividing them. WikiPedia article suggests a convenient
1180
+ // single-pass algorithm for calculating sample correlations, though depending on the numbers
1181
+ // involved, it can sometimes be numerically unstable.
1175
1182
  result_t a_sum{}, b_sum{}, ab_sum{};
1176
1183
  result_t a_sq_sum{}, b_sq_sum{};
1177
1184
  #if USEARCH_USE_OPENMP
@@ -1190,9 +1197,12 @@ template <typename scalar_at = float, typename result_at = float> struct metric_
1190
1197
  a_sq_sum += ai * ai;
1191
1198
  b_sq_sum += bi * bi;
1192
1199
  }
1193
- result_t denom = std::sqrt((dim * a_sq_sum - a_sum * a_sum) * (dim * b_sq_sum - b_sum * b_sum));
1194
- result_t corr = (dim * ab_sum - a_sum * b_sum) / denom;
1195
- return -corr;
1200
+ result_t denom = (dim * a_sq_sum - a_sum * a_sum) * (dim * b_sq_sum - b_sum * b_sum);
1201
+ if (denom == 0)
1202
+ return 0;
1203
+ result_t corr = dim * ab_sum - a_sum * b_sum;
1204
+ denom = std::sqrt(denom);
1205
+ return -corr / denom;
1196
1206
  }
1197
1207
  };
1198
1208
 
@@ -1 +1 @@
1
- {"version":3,"file":"usearch.d.ts","sourceRoot":"","sources":["../usearch.js"],"names":[],"mappings":"AAsIA;IAEI;;;;;;;;;;;OAWG;IACH;oBATkC,MAAM;gBAAU,UAAU;sBAAiC,UAAU;sBAAiC,MAAM;uBAAqB,MAAM;0BAAwB,MAAM;eAAa,OAAO;gBAChN,UAAU,iBACV,UAAU,iBACV,MAAM,kBACN,MAAM,qBACN,MAAM,UACN,OAAO,EAgCjB;IADG,oBAAwI;IAG5I;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAPW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,WAE9B,YAAY,GAAC,YAAY,GAAC,SAAS,QAoB7C;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBANW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,KACxD,MAAM,GACL,OAAO,GAAC,YAAY,CAqB/B;IAED;;;;;;;;;OASG;IACH,eAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,OAAO,GAAC,OAAO,EAAE,CAU5B;IAED;;;;;;OAMG;IACH,YAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;;;;;;;OASG;IACH,aAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;OAGG;IACH,cAFY,MAAM,CAEsC;IAExD;;;OAGG;IACH,gBAFY,MAAM,CAE0C;IAE5D;;;OAGG;IACH,QAFY,MAAM,CAE0B;IAE5C;;;OAGG;IACH,YAFY,MAAM,CAEkC;IAEpD;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;CACJ;;;;yBAzWS,MAAM;;;;;;;;;;;;;;;;;yBAmBN,MAAM;;;;;;;;;;AAYhB;;GAEG;AACH;IACI;;;;;OAKG;IACH,kBAHW,cAAc,aACd,YAAY,EAKtB;IAFG,qBAAgB;IAChB,wBAA0B;CAEjC;AAED;;GAEG;AACH;IACI;;;;;;;OAOG;IACH,kBALW,cAAc,aACd,YAAY,UACZ,cAAc,KACd,MAAM,EAOhB;IAJG,qBAAgB;IAChB,wBAA0B;IAC1B,uBAAoB;IACpB,UAAU;IAGd;;;;;OAKG;IACH,OAHW,MAAM,GACJ,OAAO,CAQnB;CACJ;AA4RD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qCA1BW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,WACxD,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,cACxD,MAAM,SACN,MAAM,UACN,UAAU,GACT,OAAO,GAAC,YAAY,CA0D/B"}
1
+ {"version":3,"file":"usearch.d.ts","sourceRoot":"","sources":["../usearch.js"],"names":[],"mappings":"AAsIA;IAEI;;;;;;;;;;;OAWG;IACH;oBATkC,MAAM;gBAAU,UAAU;sBAAiC,UAAU;sBAAiC,MAAM;uBAAqB,MAAM;0BAAwB,MAAM;eAAa,OAAO;gBAChN,UAAU,iBACV,UAAU,iBACV,MAAM,kBACN,MAAM,qBACN,MAAM,UACN,OAAO,EAmCjB;IADG,oBAAwI;IAG5I;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,UAPW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,WAE9B,YAAY,GAAC,YAAY,GAAC,SAAS,QAoB7C;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBANW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,KACxD,MAAM,GACL,OAAO,GAAC,YAAY,CAqB/B;IAED;;;;;;;;;OASG;IACH,eAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,OAAO,GAAC,OAAO,EAAE,CAU5B;IAED;;;;;;OAMG;IACH,YAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;;;;;;;OASG;IACH,aAJW,MAAM,GAAC,MAAM,EAAE,GAAC,cAAc,GAC7B,MAAM,GAAC,MAAM,EAAE,CAU1B;IAED;;;OAGG;IACH,cAFY,MAAM,CAEsC;IAExD;;;OAGG;IACH,gBAFY,MAAM,CAE0C;IAE5D;;;OAGG;IACH,QAFY,MAAM,CAE0B;IAE5C;;;OAGG;IACH,YAFY,MAAM,CAEkC;IAEpD;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;IAED;;;;OAIG;IACH,WAHW,MAAM,QAMhB;CACJ;;;;yBA5WS,MAAM;;;;;;;;;;;;;;;;;yBAmBN,MAAM;;;;;;;;;;AAYhB;;GAEG;AACH;IACI;;;;;OAKG;IACH,kBAHW,cAAc,aACd,YAAY,EAKtB;IAFG,qBAAgB;IAChB,wBAA0B;CAEjC;AAED;;GAEG;AACH;IACI;;;;;;;OAOG;IACH,kBALW,cAAc,aACd,YAAY,UACZ,cAAc,KACd,MAAM,EAOhB;IAJG,qBAAgB;IAChB,wBAA0B;IAC1B,uBAAoB;IACpB,UAAU;IAGd;;;;;OAKG;IACH,OAHW,MAAM,GACJ,OAAO,CAQnB;CACJ;AA+RD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qCA1BW,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,WACxD,YAAY,GAAC,YAAY,GAAC,SAAS,GAAC,MAAM,MAAM,MAAM,CAAC,CAAC,cACxD,MAAM,SACN,MAAM,UACN,UAAU,GACT,OAAO,GAAC,YAAY,CA0D/B"}
@@ -158,7 +158,10 @@ class Index {
158
158
  throw new Error("Invalid arguments. Expected either individual arguments or a single object argument.");
159
159
  }
160
160
 
161
- if (!Number.isInteger(dimensions) || !Number.isInteger(connectivity) || !Number.isInteger(expansion_add) || !Number.isInteger(expansion_search) || dimensions <= 0 || connectivity < 0 || expansion_add < 0 || expansion_search < 0) {
161
+ if ((typeof dimensions !== 'bigint' && (!Number.isInteger(dimensions) || dimensions <= 0)) ||
162
+ (typeof connectivity !== 'bigint' && (!Number.isInteger(connectivity) || connectivity < 0)) ||
163
+ (typeof expansion_add !== 'bigint' && (!Number.isInteger(expansion_add) || expansion_add < 0)) ||
164
+ (typeof expansion_search !== 'bigint' && (!Number.isInteger(expansion_search) || expansion_search < 0))) {
162
165
  throw new Error("`dimensions`, `connectivity`, `expansion_add`, and `expansion_search` must be non-negative integers, with `dimensions` being positive.");
163
166
  }
164
167
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usearch",
3
- "version": "2.9.1",
3
+ "version": "2.9.2",
4
4
  "description": "Smaller & Faster Single-File Vector Search Engine from Unum",
5
5
  "author": "Ash Vardanian (https://ashvardanian.com/)",
6
6
  "license": "Apache 2.0",
@@ -0,0 +1,88 @@
1
+ # Contributing
2
+
3
+ ## C and C++
4
+
5
+ To rerun experiments utilize the following command:
6
+
7
+ ```sh
8
+ cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release
9
+ cmake --build build_release --config Release
10
+ ./build_release/simsimd_bench
11
+ ./build_release/simsimd_bench --benchmark_filter=js
12
+ ```
13
+
14
+ ## Python
15
+
16
+ Testing:
17
+
18
+ ```sh
19
+ pip install -e . # to install the package in editable mode
20
+ pytest python/test.py -s -x -Wd # to run tests
21
+ ```
22
+
23
+ Here, `-s` will output the logs.
24
+ The `-x` will stop on the first failure.
25
+ The `-Wd` will silence overflows and runtime warnings.
26
+
27
+ Benchmarking:
28
+
29
+ ```sh
30
+ pip install numpy scipy scikit-learn # for comparison baselines
31
+ python python/bench.py # to run default benchmarks
32
+ python python/bench.py --n 1000 --ndim 1000000 # batch size and dimensions
33
+ ```
34
+
35
+ Before merging your changes you may want to test your changes against the entire matrix of Python versions USearch supports.
36
+ For that you need the `cibuildwheel`, which is tricky to use on MacOS and Windows, as it would target just the local environment.
37
+ Still, if you have Docker running on any desktop OS, you can use it to build and test the Python bindings for all Python versions for Linux:
38
+
39
+ ```sh
40
+ pip install cibuildwheel
41
+ cibuildwheel
42
+ cibuildwheel --platform linux # works on any OS and builds all Linux backends
43
+ cibuildwheel --platform linux --archs x86_64 # 64-bit x86, the most common on desktop and servers
44
+ cibuildwheel --platform linux --archs aarch64 # 64-bit Arm for mobile devices, Apple M-series, and AWS Graviton
45
+ cibuildwheel --platform linux --archs i686 # 32-bit Linux
46
+ cibuildwheel --platform linux --archs s390x # emulating big-endian IBM Z
47
+ cibuildwheel --platform macos # works only on MacOS
48
+ cibuildwheel --platform windows # works only on Windows
49
+ ```
50
+
51
+ You may need root previligies for multi-architecture builds:
52
+
53
+ ```sh
54
+ sudo $(which cibuildwheel) --platform linux
55
+ ```
56
+
57
+ On Windows and MacOS, to avoid frequent path resolution issues, you may want to use:
58
+
59
+ ```sh
60
+ python -m cibuildwheel --platform windows
61
+ ```
62
+
63
+ ## Rust
64
+
65
+ ```sh
66
+ cargo test
67
+ cargo bench
68
+ open ./target/criterion/report/index.html
69
+ ```
70
+
71
+ ## JavaScript
72
+
73
+ Testing and benchmarking:
74
+
75
+ ```sh
76
+ npm install --dev
77
+ npm test
78
+ npm run bench
79
+ ```
80
+
81
+ ## GoLang
82
+
83
+ ```sh
84
+ cd golang
85
+ go test # To test
86
+ go test -run=^$ -bench=. -benchmem # To benchmark
87
+ ```
88
+
@@ -210,9 +210,9 @@ dependencies = [
210
210
 
211
211
  [[package]]
212
212
  name = "half"
213
- version = "2.3.1"
213
+ version = "2.4.0"
214
214
  source = "registry+https://github.com/rust-lang/crates.io-index"
215
- checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
215
+ checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
216
216
  dependencies = [
217
217
  "cfg-if",
218
218
  "crunchy",
@@ -496,10 +496,11 @@ dependencies = [
496
496
 
497
497
  [[package]]
498
498
  name = "simsimd"
499
- version = "3.7.7"
499
+ version = "3.8.1"
500
500
  dependencies = [
501
501
  "cc",
502
502
  "criterion",
503
+ "half",
503
504
  "rand",
504
505
  ]
505
506
 
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  name = "simsimd"
3
3
  description = "Fastest SIMD-Accelerated Vector Similarity Functions for x86 and Arm"
4
- version = "3.8.0"
4
+ version = "3.9.0"
5
5
  edition = "2021"
6
6
  license = "Apache-2.0"
7
7
  authors = [
@@ -12,7 +12,13 @@ repository = "https://github.com/ashvardanian/SimSIMD"
12
12
  documentation = "https://docs.rs/simsimd"
13
13
  homepage = "https://ashvardanian.com/posts/simsimd-faster-scipy"
14
14
  keywords = ["simd", "search", "linear-algebra", "vector"]
15
- categories = ["mathematics", "hardware-support", "no-std", "wasm", "external-ffi-bindings"]
15
+ categories = [
16
+ "mathematics",
17
+ "hardware-support",
18
+ "no-std",
19
+ "wasm",
20
+ "external-ffi-bindings",
21
+ ]
16
22
 
17
23
  [lib]
18
24
  name = "simsimd"
@@ -21,6 +27,7 @@ path = "rust/lib.rs"
21
27
  [build-dependencies]
22
28
  cc = "1.0.83"
23
29
 
30
+
24
31
  [[bench]]
25
32
  name = "cosine"
26
33
  harness = false
@@ -34,3 +41,4 @@ path = "rust/benches/sqeuclidean.rs"
34
41
  [dev-dependencies]
35
42
  criterion = { version = "0.5.1" }
36
43
  rand = { version = "0.8.5" }
44
+ half = { version = "2.4.0" }