usearch 2.10.2 → 2.10.5

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 (112) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +34 -0
  5. package/c/lib.cpp +362 -0
  6. package/c/test.c +382 -0
  7. package/c/usearch.h +356 -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 +324 -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 +2004 -0
  64. package/include/usearch/index_plugins.hpp +2255 -0
  65. package/javascript/dist-package-cjs.json +3 -0
  66. package/javascript/dist-package-esm.json +3 -0
  67. package/javascript/lib.cpp +350 -0
  68. package/javascript/node-gyp-build.d.ts +1 -0
  69. package/javascript/tsconfig-base.json +25 -0
  70. package/javascript/tsconfig-cjs.json +8 -0
  71. package/javascript/tsconfig-esm.json +8 -0
  72. package/javascript/usearch.test.js +84 -0
  73. package/javascript/usearch.ts +640 -0
  74. package/package.json +10 -3
  75. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  76. package/prebuilds/linux-arm64/usearch.node +0 -0
  77. package/prebuilds/linux-x64/usearch.node +0 -0
  78. package/prebuilds/win32-ia32/usearch.node +0 -0
  79. package/prebuilds/win32-x64/usearch.node +0 -0
  80. package/simsimd/.releaserc +87 -0
  81. package/simsimd/CONTRIBUTING.md +114 -0
  82. package/simsimd/Cargo.lock +709 -0
  83. package/simsimd/Cargo.toml +44 -0
  84. package/simsimd/LICENSE +201 -0
  85. package/simsimd/README.md +708 -0
  86. package/simsimd/binding.gyp +36 -0
  87. package/simsimd/build.rs +51 -0
  88. package/simsimd/c/lib.c +110 -0
  89. package/simsimd/include/simsimd/binary.h +258 -0
  90. package/simsimd/include/simsimd/dot.h +1237 -0
  91. package/simsimd/include/simsimd/geospatial.h +37 -0
  92. package/simsimd/include/simsimd/probability.h +563 -0
  93. package/simsimd/include/simsimd/simsimd.h +1129 -0
  94. package/simsimd/include/simsimd/spatial.h +1019 -0
  95. package/simsimd/include/simsimd/types.h +309 -0
  96. package/simsimd/javascript/bench.js +127 -0
  97. package/simsimd/javascript/dist-package-cjs.json +3 -0
  98. package/simsimd/javascript/dist-package-esm.json +3 -0
  99. package/simsimd/javascript/fallback.ts +203 -0
  100. package/simsimd/javascript/lib.c +107 -0
  101. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  102. package/simsimd/javascript/simsimd.ts +135 -0
  103. package/simsimd/javascript/test.mjs +179 -0
  104. package/simsimd/javascript/tsconfig-base.json +37 -0
  105. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  106. package/simsimd/javascript/tsconfig-esm.json +8 -0
  107. package/simsimd/package-lock.json +1709 -0
  108. package/simsimd/package.json +61 -0
  109. package/simsimd/rust/benches/cosine.rs +27 -0
  110. package/simsimd/rust/benches/native.rs +30 -0
  111. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  112. package/simsimd/rust/lib.rs +778 -0
@@ -0,0 +1,708 @@
1
+ # SimSIMD 📏
2
+
3
+ _Computing dot-products, similarity measures, and distances between low- and high-dimensional vectors is ubiquitous in Machine Learning, Scientific Computing, Geo-Spatial Analysis, and Information Retrieval.
4
+ These algorithms generally have linear complexity in time, constant complexity in space, and are data-parallel.
5
+ In other words, it is easily parallelizable and vectorizable and often available in packages like BLAS and LAPACK, as well as higher-level `numpy` and `scipy` Python libraries.
6
+ Ironically, even with decades of evolution in compilers and numerical computing, [most libraries can be 3-200x slower than hardware potential][benchmarks] even on the most popular hardware, like 64-bit x86 and Arm CPUs.
7
+ SimSIMD attempts to fill that gap.
8
+ 1️⃣ SimSIMD functions are practically as fast as `memcpy`.
9
+ 2️⃣ SimSIMD [compiles to more platforms than NumPy (105 vs 35)][compatibility] and has more backends than most BLAS implementations._
10
+
11
+ [benchmarks]: https://ashvardanian.com/posts/simsimd-faster-scipy
12
+ [compatibility]: https://pypi.org/project/simsimd/#files
13
+
14
+ <div>
15
+ <a href="https://pepy.tech/project/simsimd">
16
+ <img alt="PyPI" src="https://static.pepy.tech/personalized-badge/simsimd?period=total&units=abbreviation&left_color=black&right_color=blue&left_text=SimSIMD%20Python%20installs" />
17
+ </a>
18
+ <a href="https://www.npmjs.com/package/simsimd">
19
+ <img alt="npm" src="https://img.shields.io/npm/dy/simsimd?label=JavaScript%20NPM%20installs" />
20
+ </a>
21
+ <a href="https://crates.io/crates/simsimd">
22
+ <img alt="rust" src="https://img.shields.io/crates/d/simsimd?label=Rust%20Crate%20installs" />
23
+ </a>
24
+ <img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/ashvardanian/simsimd">
25
+ <a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
26
+ <img alt="GitHub Actions Ubuntu" src="https://img.shields.io/github/actions/workflow/status/ashvardanian/SimSIMD/release.yml?branch=main&label=Ubuntu&logo=github&color=blue">
27
+ </a>
28
+ <a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
29
+ <img alt="GitHub Actions Windows" src="https://img.shields.io/github/actions/workflow/status/ashvardanian/SimSIMD/release.yml?branch=main&label=Windows&logo=windows&color=blue">
30
+ </a>
31
+ <a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
32
+ <img alt="GitHub Actions MacOS" src="https://img.shields.io/github/actions/workflow/status/ashvardanian/SimSIMD/release.yml?branch=main&label=MacOS&logo=apple&color=blue">
33
+ </a>
34
+ <a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
35
+ <img alt="GitHub Actions CentOS Linux" src="https://img.shields.io/github/actions/workflow/status/ashvardanian/SimSIMD/release.yml?branch=main&label=CentOS&logo=centos&color=blue">
36
+ </a>
37
+
38
+ </div>
39
+
40
+ ## Features
41
+
42
+ SimSIMD provides over 100 SIMD-optimized kernels for various distance and similarity measures, accelerating search in [USearch](https://github.com/unum-cloud/usearch) and several DBMS products.
43
+ Implemented distance functions include:
44
+
45
+ - Euclidean (L2) and Cosine (Angular) spatial distances for Vector Search.
46
+ - Dot-Products for real & complex vectors for DSP & Quantum computing.
47
+ - Hamming (~ Manhattan) and Jaccard (~ Tanimoto) bit-level distances.
48
+ - Kullback-Leibler and Jensen–Shannon divergences for probability distributions.
49
+ - Haversine and Vincenty's formulae for Geospatial Analysis.
50
+ - For Levenshtein, Needleman–Wunsch and other text metrics, check [StringZilla][stringzilla].
51
+
52
+ [scipy]: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html#module-scipy.spatial.distance
53
+ [numpy]: https://numpy.org/doc/stable/reference/generated/numpy.inner.html
54
+ [stringzilla]: https://github.com/ashvardanian/stringzilla
55
+
56
+ Moreover, SimSIMD...
57
+
58
+ - handles `f64`, `f32`, and `f16` real & complex vectors.
59
+ - handles `i8` integral and `b8` binary vectors.
60
+ - is a zero-dependency [header-only C 99](#using-simsimd-in-c) library.
61
+ - has bindings for [Python](#using-simsimd-in-python), [Rust](#using-simsimd-in-rust) and [JavaScript](#using-simsimd-in-javascript).
62
+ - has Arm backends for NEON and Scalable Vector Extensions (SVE).
63
+ - has x86 backends for Haswell, Skylake, Ice Lake, and Sapphire Rapids.
64
+
65
+ Due to the high-level of fragmentation of SIMD support in different x86 CPUs, SimSIMD uses the names of select Intel CPU generations for its backends.
66
+ They, however, also work on AMD CPUs.
67
+ Inel Haswell is compatible with AMD Zen 1/2/3, while AMD Genoa Zen 4 covers AVX-512 instructions added to Intel Skylake and Ice Lake.
68
+ You can learn more about the technical implementation details in the following blogposts:
69
+
70
+ - [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
71
+ - [Uses Arm SVE and x86 AVX-512's masked loads to eliminate tail `for`-loops](https://ashvardanian.com/posts/simsimd-faster-scipy/#tails-of-the-past-the-significance-of-masked-loads).
72
+ - [Uses AVX-512 FP16 for half-precision operations, that few compilers vectorize](https://ashvardanian.com/posts/simsimd-faster-scipy/#the-challenge-of-f16).
73
+ - [Substitutes LibC's `sqrt` calls with bit-hacks using Jan Kadlec's constant](https://ashvardanian.com/posts/simsimd-faster-scipy/#bonus-section-bypassing-sqrt-and-libc-dependencies).
74
+ - [For Python avoids slow PyBind11, SWIG, and even `PyArg_ParseTuple` for speed](https://ashvardanian.com/posts/pybind11-cpython-tutorial/).
75
+ - [For JavaScript uses typed arrays and NAPI for zero-copy calls](https://ashvardanian.com/posts/javascript-ai-vector-search/).
76
+
77
+ ## Benchmarks
78
+
79
+ ### Against NumPy and SciPy
80
+
81
+ Given 1000 embeddings from OpenAI Ada API with 1536 dimensions, running on the Apple M2 Pro Arm CPU with NEON support, here's how SimSIMD performs against conventional methods:
82
+
83
+ | Kind | `f32` improvement | `f16` improvement | `i8` improvement | Conventional method | SimSIMD |
84
+ | :------------------------ | ----------------: | ----------------: | ---------------: | :------------------------------------- | :-------------- |
85
+ | Inner Product | __2 x__ | __9 x__ | __18 x__ | `numpy.inner` | `inner` |
86
+ | Cosine Distance | __32 x__ | __79 x__ | __133 x__ | `scipy.spatial.distance.cosine` | `cosine` |
87
+ | Euclidean Distance ² | __5 x__ | __26 x__ | __17 x__ | `scipy.spatial.distance.sqeuclidean` | `sqeuclidean` |
88
+ | Jensen-Shannon Divergence | __31 x__ | __53 x__ | | `scipy.spatial.distance.jensenshannon` | `jensenshannon` |
89
+
90
+ ### Against GCC Auto-Vectorization
91
+
92
+ On the Intel Sapphire Rapids platform, SimSIMD was benchmarked against auto-vectorized code using GCC 12.
93
+ GCC handles single-precision `float` but might not be the best choice for `int8` and `_Float16` arrays, which have been part of the C language since 2011.
94
+
95
+ | Kind | GCC 12 `f32` | GCC 12 `f16` | SimSIMD `f16` | `f16` improvement |
96
+ | :------------------------ | -----------: | -----------: | ------------: | ----------------: |
97
+ | Inner Product | 3,810 K/s | 192 K/s | 5,990 K/s | __31 x__ |
98
+ | Cosine Distance | 3,280 K/s | 336 K/s | 6,880 K/s | __20 x__ |
99
+ | Euclidean Distance ² | 4,620 K/s | 147 K/s | 5,320 K/s | __36 x__ |
100
+ | Jensen-Shannon Divergence | 1,180 K/s | 18 K/s | 2,140 K/s | __118 x__ |
101
+
102
+ __Broader Benchmarking Results__:
103
+
104
+ - [Apple M2 Pro](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-1-performance-on-apple-m2-pro).
105
+ - [4th Gen Intel Xeon Platinum](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-2-performance-on-4th-gen-intel-xeon-platinum-8480).
106
+ - [AWS Graviton 3](https://ashvardanian.com/posts/simsimd-faster-scipy/#appendix-3-performance-on-aws-graviton-3).
107
+
108
+ ## Using SimSIMD in Python
109
+
110
+ The package is intended to replace the usage of `numpy.inner`, `numpy.dot`, and `scipy.spatial.distance`.
111
+ Aside from drastic performance improvements, SimSIMD significantly improves accuracy in mixed precision setups.
112
+ NumPy and SciPy, processing `i8` or `f16` vectors, will use the same types for accumulators, while SimSIMD can combine `i8` enumeration, `i16` multiplication, and `i32` accumulation to avoid overflows entirely.
113
+ The same applies to processing `f16` values with `f32` precision.
114
+
115
+ ### Installation
116
+
117
+ Use the following snippet to install SimSIMD and list available hardware acceleration options available on your machine:
118
+
119
+ ```sh
120
+ pip install simsimd
121
+ python -c "import simsimd; print(simsimd.get_capabilities())"
122
+ ```
123
+
124
+ ### One-to-One Distance
125
+
126
+ ```py
127
+ import simsimd
128
+ import numpy as np
129
+
130
+ vec1 = np.random.randn(1536).astype(np.float32)
131
+ vec2 = np.random.randn(1536).astype(np.float32)
132
+ dist = simsimd.cosine(vec1, vec2)
133
+ ```
134
+
135
+ Supported functions include `cosine`, `inner`, `sqeuclidean`, `hamming`, and `jaccard`.
136
+ Dot products are supported for both real and complex numbers:
137
+
138
+ ```py
139
+ vec1 = np.random.randn(768).astype(np.float64) + 1j * np.random.randn(768).astype(np.float64)
140
+ vec2 = np.random.randn(768).astype(np.float64) + 1j * np.random.randn(768).astype(np.float64)
141
+
142
+ dist = simsimd.dot(vec1.astype(np.complex128), vec2.astype(np.complex128))
143
+ dist = simsimd.dot(vec1.astype(np.complex64), vec2.astype(np.complex64))
144
+ dist = simsimd.vdot(vec1.astype(np.complex64), vec2.astype(np.complex64)) # conjugate, same as `np.vdot`
145
+ ```
146
+
147
+ Unlike SciPy, SimSIMD allows explicitly stating the precision of the input vectors, which is especially useful for mixed-precision setups.
148
+
149
+ ```py
150
+ dist = simsimd.cosine(vec1, vec2, "i8")
151
+ dist = simsimd.cosine(vec1, vec2, "f16")
152
+ dist = simsimd.cosine(vec1, vec2, "f32")
153
+ dist = simsimd.cosine(vec1, vec2, "f64")
154
+ ```
155
+
156
+ It also allows using SimSIMD for half-precision complex numbers, which NumPy does not support.
157
+ For that, view data as continuous even-length `np.float16` vectors and override type-resolution with `complex32` string.
158
+
159
+ ```py
160
+ vec1 = np.random.randn(1536).astype(np.float16)
161
+ vec2 = np.random.randn(1536).astype(np.float16)
162
+ simd.dot(vec1, vec2, "complex32")
163
+ simd.vdot(vec1, vec2, "complex32")
164
+ ```
165
+
166
+ ### One-to-Many Distances
167
+
168
+ Every distance function can be used not only for one-to-one but also one-to-many and many-to-many distance calculations.
169
+ For one-to-many:
170
+
171
+ ```py
172
+ vec1 = np.random.randn(1536).astype(np.float32) # rank 1 tensor
173
+ batch1 = np.random.randn(1, 1536).astype(np.float32) # rank 2 tensor
174
+ batch2 = np.random.randn(100, 1536).astype(np.float32)
175
+
176
+ dist_rank1 = simsimd.cosine(vec1, batch2)
177
+ dist_rank2 = simsimd.cosine(batch1, batch2)
178
+ ```
179
+
180
+ ### Many-to-Many Distances
181
+
182
+ All distance functions in SimSIMD can be used to compute many-to-many distances.
183
+ For two batches of 100 vectors to compute 100 distances, one would call it like this:
184
+
185
+ ```py
186
+ batch1 = np.random.randn(100, 1536).astype(np.float32)
187
+ batch2 = np.random.randn(100, 1536).astype(np.float32)
188
+ dist = simsimd.cosine(batch1, batch2)
189
+ ```
190
+
191
+ Input matrices must have identical shapes.
192
+ This functionality isn't natively present in NumPy or SciPy, and generally requires creating intermediate arrays, which is inefficient and memory-consuming.
193
+
194
+ ### Many-to-Many All-Pairs Distances
195
+
196
+ One can use SimSIMD to compute distances between all possible pairs of rows across two matrices (akin to [`scipy.spatial.distance.cdist`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html)).
197
+ The resulting object will have a type `DistancesTensor`, zero-copy compatible with NumPy and other libraries.
198
+ For two arrays of 10 and 1,000 entries, the resulting tensor will have 10,000 cells:
199
+
200
+ ```py
201
+ import numpy as np
202
+ from simsimd import cdist, DistancesTensor
203
+
204
+ matrix1 = np.random.randn(1000, 1536).astype(np.float32)
205
+ matrix2 = np.random.randn(10, 1536).astype(np.float32)
206
+ distances: DistancesTensor = simsimd.cdist(matrix1, matrix2, metric="cosine") # zero-copy
207
+ distances_array: np.ndarray = np.array(distances, copy=True) # now managed by NumPy
208
+ ```
209
+
210
+ ### Multithreading
211
+
212
+ By default, computations use a single CPU core.
213
+ To optimize and utilize all CPU cores on Linux systems, add the `threads=0` argument.
214
+ Alternatively, specify a custom number of threads:
215
+
216
+ ```py
217
+ distances = simsimd.cdist(matrix1, matrix2, metric="cosine", threads=0)
218
+ ```
219
+
220
+ ### Using Python API with USearch
221
+
222
+ Want to use it in Python with [USearch](https://github.com/unum-cloud/usearch)?
223
+ You can wrap the raw C function pointers SimSIMD backends into a `CompiledMetric` and pass it to USearch, similar to how it handles Numba's JIT-compiled code.
224
+
225
+ ```py
226
+ from usearch.index import Index, CompiledMetric, MetricKind, MetricSignature
227
+ from simsimd import pointer_to_sqeuclidean, pointer_to_cosine, pointer_to_inner
228
+
229
+ metric = CompiledMetric(
230
+ pointer=pointer_to_cosine("f16"),
231
+ kind=MetricKind.Cos,
232
+ signature=MetricSignature.ArrayArraySize,
233
+ )
234
+
235
+ index = Index(256, metric=metric)
236
+ ```
237
+
238
+ ## Using SimSIMD in Rust
239
+
240
+ To install, add the following to your `Cargo.toml`:
241
+
242
+ ```toml
243
+ [dependencies]
244
+ simsimd = "..."
245
+ ```
246
+
247
+ Before using the SimSIMD library, ensure you have imported the necessary traits and types into your Rust source file.
248
+ The library provides several traits for different distance/similarity kinds - `SpatialSimilarity`, `BinarySimilarity`, and `ProbabilitySimilarity`.
249
+
250
+ ### Spatial Similarity: Cosine and Euclidean Distances
251
+
252
+ ```rust
253
+ use simsimd::SpatialSimilarity;
254
+
255
+ fn main() {
256
+ let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
257
+ let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
258
+
259
+ // Compute the cosine similarity between vector_a and vector_b
260
+ let cosine_similarity = f32::cosine(&vector_a, &vector_b)
261
+ .expect("Vectors must be of the same length");
262
+
263
+ println!("Cosine Similarity: {}", cosine_similarity);
264
+
265
+ // Compute the squared Euclidean distance between vector_a and vector_b
266
+ let sq_euclidean_distance = f32::sqeuclidean(&vector_a, &vector_b)
267
+ .expect("Vectors must be of the same length");
268
+
269
+ println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
270
+ }
271
+ ```
272
+
273
+ Spatial similarity functions are available for `f64`, `f32`, `f16`, and `i8` types.
274
+
275
+ ### Dot-Products: Inner and Complex Inner Products
276
+
277
+ ```rust
278
+ use simsimd::SpatialSimilarity;
279
+ use simsimd::ComplexProducts;
280
+
281
+ fn main() {
282
+ let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0];
283
+ let vector_b: Vec<f32> = vec![5.0, 6.0, 7.0, 8.0];
284
+
285
+ // Compute the inner product between vector_a and vector_b
286
+ let inner_product = SpatialSimilarity::dot(&vector_a, &vector_b)
287
+ .expect("Vectors must be of the same length");
288
+
289
+ println!("Inner Product: {}", inner_product);
290
+
291
+ // Compute the complex inner product between complex_vector_a and complex_vector_b
292
+ let complex_inner_product = ComplexProducts::dot(&vector_a, &vector_b)
293
+ .expect("Vectors must be of the same length");
294
+
295
+ let complex_conjugate_inner_product = ComplexProducts::vdot(&vector_a, &vector_b)
296
+ .expect("Vectors must be of the same length");
297
+
298
+ println!("Complex Inner Product: {:?}", complex_inner_product); // -18, 69
299
+ println!("Complex C. Inner Product: {:?}", complex_conjugate_inner_product); // 70, -8
300
+ }
301
+ ```
302
+
303
+ Complex inner products are available for `f64`, `f32`, and `f16` types.
304
+
305
+ ### Probability Distributions: Jensen-Shannon and Kullback-Leibler Divergences
306
+
307
+ ```rust
308
+ use simsimd::SpatialSimilarity;
309
+
310
+ fn main() {
311
+ let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
312
+ let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
313
+
314
+ let cosine_similarity = f32::jensenshannon(&vector_a, &vector_b)
315
+ .expect("Vectors must be of the same length");
316
+
317
+ println!("Cosine Similarity: {}", cosine_similarity);
318
+
319
+ let sq_euclidean_distance = f32::kullbackleibler(&vector_a, &vector_b)
320
+ .expect("Vectors must be of the same length");
321
+
322
+ println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
323
+ }
324
+ ```
325
+
326
+ Probability similarity functions are available for `f64`, `f32`, and `f16` types.
327
+
328
+ ### Binary Similarity: Hamming and Jaccard Distances
329
+
330
+ Similar to spatial distances, one can compute bit-level distance functions between slices of unsigned integers:
331
+
332
+ ```rust
333
+ use simsimd::BinarySimilarity;
334
+
335
+ fn main() {
336
+ let vector_a = &[0b11110000, 0b00001111, 0b10101010];
337
+ let vector_b = &[0b11110000, 0b00001111, 0b01010101];
338
+
339
+ // Compute the Hamming distance between vector_a and vector_b
340
+ let hamming_distance = u8::hamming(&vector_a, &vector_b)
341
+ .expect("Vectors must be of the same length");
342
+
343
+ println!("Hamming Distance: {}", hamming_distance);
344
+
345
+ // Compute the Jaccard distance between vector_a and vector_b
346
+ let jaccard_distance = u8::jaccard(&vector_a, &vector_b)
347
+ .expect("Vectors must be of the same length");
348
+
349
+ println!("Jaccard Distance: {}", jaccard_distance);
350
+ }
351
+ ```
352
+
353
+ Binary similarity functions are available only for `u8` types.
354
+
355
+ ### Half-Precision Floating-Point Numbers
356
+
357
+ Rust has no native support for half-precision floating-point numbers, but SimSIMD provides a `f16` type.
358
+ It has no functionality - it is a `transparent` wrapper around `u16` and can be used with `half` or any other half-precision library.
359
+
360
+ ```rust
361
+ use simsimd::SpatialSimilarity;
362
+ use simsimd::f16 as SimF16;
363
+ use half::f16 as HalfF16;
364
+
365
+ fn main() {
366
+ let vector_a: Vec<HalfF16> = ...
367
+ let vector_b: Vec<HalfF16> = ...
368
+
369
+ let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const SimF16, a_half.len()) };
370
+ let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const SimF16, b_half.len()) };
371
+
372
+ // Compute the cosine similarity between vector_a and vector_b
373
+ let cosine_similarity = SimF16::cosine(&vector_a, &vector_b)
374
+ .expect("Vectors must be of the same length");
375
+
376
+ println!("Cosine Similarity: {}", cosine_similarity);
377
+ }
378
+ ```
379
+
380
+ ### Dynamic Dispatch
381
+
382
+ SimSIMD provides a dynamic dispatch mechanism to select the most advanced micro-kernel for the current CPU.
383
+ You can query supported backends and use the `SimSIMD::capabilities` function to select the best one.
384
+
385
+ ```rust
386
+ println!("uses neon: {}", capabilties::uses_neon());
387
+ println!("uses sve: {}", capabilties::uses_sve());
388
+ println!("uses haswell: {}", capabilties::uses_haswell());
389
+ println!("uses skylake: {}", capabilties::uses_skylake());
390
+ println!("uses ice: {}", capabilties::uses_ice());
391
+ println!("uses sapphire: {}", capabilties::uses_sapphire());
392
+ ```
393
+
394
+ ## Using SimSIMD in JavaScript
395
+
396
+ To install, choose one of the following options depending on your environment:
397
+
398
+ - `npm install --save simsimd`
399
+ - `yarn add simsimd`
400
+ - `pnpm add simsimd`
401
+ - `bun install simsimd`
402
+
403
+ The package is distributed with prebuilt binaries for Node.js v10 and above for Linux (x86_64, arm64), macOS (x86_64, arm64), and Windows (i386, x86_64).
404
+ If your platform is not supported, you can build the package from the source via `npm run build`.
405
+ This will automatically happen unless you install the package with the `--ignore-scripts` flag or use Bun.
406
+ After you install it, you will be able to call the SimSIMD functions on various `TypedArray` variants:
407
+
408
+ ```js
409
+ const { sqeuclidean, cosine, inner, hamming, jaccard } = require('simsimd');
410
+
411
+ const vectorA = new Float32Array([1.0, 2.0, 3.0]);
412
+ const vectorB = new Float32Array([4.0, 5.0, 6.0]);
413
+
414
+ const distance = sqeuclidean(vectorA, vectorB);
415
+ console.log('Squared Euclidean Distance:', distance);
416
+ ```
417
+
418
+ Other numeric types and precision levels are supported as well:
419
+
420
+ ```js
421
+ const vectorA = new Float64Array([1.0, 2.0, 3.0]);
422
+ const vectorB = new Float64Array([4.0, 5.0, 6.0]);
423
+
424
+ const distance = cosine(vectorA, vectorB);
425
+ console.log('Cosine Similarity:', distance);
426
+ ```
427
+
428
+ ## Using SimSIMD in C
429
+
430
+ For integration within a CMake-based project, add the following segment to your `CMakeLists.txt`:
431
+
432
+ ```cmake
433
+ FetchContent_Declare(
434
+ simsimd
435
+ GIT_REPOSITORY https://github.com/ashvardanian/simsimd.git
436
+ GIT_SHALLOW TRUE
437
+ )
438
+ FetchContent_MakeAvailable(simsimd)
439
+ ```
440
+
441
+ After that, you can use the SimSIMD library in your C code in several ways.
442
+ Simplest of all, you can include the headers, and the compiler will automatically select the most recent CPU extensions that SimSIMD will use.
443
+
444
+ ```c
445
+ #include <simsimd/simsimd.h>
446
+
447
+ int main() {
448
+ simsimd_f32_t vector_a[1536];
449
+ simsimd_f32_t vector_b[1536];
450
+ simsimd_metric_punned_t distance_function = simsimd_metric_punned(
451
+ simsimd_metric_cos_k, // Metric kind, like the angular cosine distance
452
+ simsimd_datatype_f32_k, // Data type, like: f16, f32, f64, i8, b8, and complex variants
453
+ simsimd_cap_any_k); // Which CPU capabilities are we allowed to use
454
+ simsimd_distance_t distance;
455
+ distance_function(vector_a, vector_b, 1536, &distance);
456
+ return 0;
457
+ }
458
+ ```
459
+
460
+ ### Dynamic Dispatch
461
+
462
+ To avoid hard-coding the backend, you can rely on `c/lib.c` to prepackage all possible backends in one binary, and select the most recent CPU features at runtime.
463
+ That feature of the C library is called dynamic dispatch and is extensively used in the Python, JavaScript, and Rust bindings.
464
+ To test which CPU features are available on the machine at runtime, use the following APIs:
465
+
466
+ ```c
467
+ int uses_neon = simsimd_uses_neon();
468
+ int uses_sve = simsimd_uses_sve();
469
+ int uses_haswell = simsimd_uses_haswell();
470
+ int uses_skylake = simsimd_uses_skylake();
471
+ int uses_ice = simsimd_uses_ice();
472
+ int uses_sapphire = simsimd_uses_sapphire();
473
+
474
+ simsimd_capability_t capabilities = simsimd_capabilities();
475
+ ```
476
+
477
+ To differentiate between runtime and compile-time dispatch, define the following macro:
478
+
479
+ ```c
480
+ #define SIMSIMD_DYNAMIC_DISPATCH 1 // or 0
481
+ ```
482
+
483
+ ### Spatial Distances: Cosine and Euclidean Distances
484
+
485
+ ```c
486
+ #include <simsimd/simsimd.h>
487
+
488
+ int main() {
489
+ simsimd_f64_t f64s[1536];
490
+ simsimd_f32_t f32s[1536];
491
+ simsimd_f16_t f16s[1536];
492
+ simsimd_i8_t i8[1536];
493
+ simsimd_distance_t distance;
494
+
495
+ // Cosine distance between two vectors
496
+ simsimd_cos_i8(i8s, i8s, 1536, &distance);
497
+ simsimd_cos_f16(f16s, f16s, 1536, &distance);
498
+ simsimd_cos_f32(f32s, f32s, 1536, &distance);
499
+ simsimd_cos_f64(f64s, f64s, 1536, &distance);
500
+
501
+ // Euclidean distance between two vectors
502
+ simsimd_l2sq_i8(i8s, i8s, 1536, &distance);
503
+ simsimd_l2sq_f16(f16s, f16s, 1536, &distance);
504
+ simsimd_l2sq_f32(f32s, f32s, 1536, &distance);
505
+ simsimd_l2sq_f64(f64s, f64s, 1536, &distance);
506
+
507
+ return 0;
508
+ }
509
+ ```
510
+
511
+ ### Dot-Products: Inner and Complex Inner Products
512
+
513
+ ```c
514
+ #include <simsimd/simsimd.h>
515
+
516
+ int main() {
517
+ simsimd_f64_t f64s[1536];
518
+ simsimd_f32_t f32s[1536];
519
+ simsimd_f16_t f16s[1536];
520
+ simsimd_distance_t distance;
521
+
522
+ // Inner product between two vectors
523
+ simsimd_dot_f16(f16s, f16s, 1536, &distance);
524
+ simsimd_dot_f32(f32s, f32s, 1536, &distance);
525
+ simsimd_dot_f64(f64s, f64s, 1536, &distance);
526
+
527
+ // Complex inner product between two vectors
528
+ simsimd_dot_f16c(f16s, f16s, 1536, &distance);
529
+ simsimd_dot_f32c(f32s, f32s, 1536, &distance);
530
+ simsimd_dot_f64c(f64s, f64s, 1536, &distance);
531
+
532
+ // Complex conjugate inner product between two vectors
533
+ simsimd_vdot_f16c(f16s, f16s, 1536, &distance);
534
+ simsimd_vdot_f32c(f32s, f32s, 1536, &distance);
535
+ simsimd_vdot_f64c(f64s, f64s, 1536, &distance);
536
+
537
+ return 0;
538
+ }
539
+ ```
540
+
541
+ ### Binary Distances: Hamming and Jaccard Distances
542
+
543
+ ```c
544
+ #include <simsimd/simsimd.h>
545
+
546
+ int main() {
547
+ simsimd_b8_t b8s[1536 / 8]; // 8 bits per word
548
+ simsimd_distance_t distance;
549
+
550
+ // Hamming distance between two vectors
551
+ simsimd_hamming_b8(b8s, b8s, 1536 / 8, &distance);
552
+
553
+ // Jaccard distance between two vectors
554
+ simsimd_jaccard_b8(b8s, b8s, 1536 / 8, &distance);
555
+
556
+ return 0;
557
+ }
558
+ ```
559
+
560
+ ### Probability Distributions: Jensen-Shannon and Kullback-Leibler Divergences
561
+
562
+ ```c
563
+ #include <simsimd/simsimd.h>
564
+
565
+ int main() {
566
+ simsimd_f64_t f64s[1536];
567
+ simsimd_f32_t f32s[1536];
568
+ simsimd_f16_t f16s[1536];
569
+ simsimd_distance_t distance;
570
+
571
+ // Jensen-Shannon divergence between two vectors
572
+ simsimd_js_f16(f16s, f16s, 1536, &distance);
573
+ simsimd_js_f32(f32s, f32s, 1536, &distance);
574
+ simsimd_js_f64(f64s, f64s, 1536, &distance);
575
+
576
+ // Kullback-Leibler divergence between two vectors
577
+ simsimd_kl_f16(f16s, f16s, 1536, &distance);
578
+ simsimd_kl_f32(f32s, f32s, 1536, &distance);
579
+ simsimd_kl_f64(f64s, f64s, 1536, &distance);
580
+
581
+ return 0;
582
+ }
583
+ ```
584
+
585
+ ### Half-Precision Floating-Point Numbers
586
+
587
+ If you aim to utilize the `_Float16` functionality with SimSIMD, ensure your development environment is compatible with C 11.
588
+ For other SimSIMD functionalities, C 99 compatibility will suffice.
589
+ To explicitly disable half-precision support, define the following macro before imports:
590
+
591
+ ```c
592
+ #define SIMSIMD_NATIVE_F16 0 // or 1
593
+ #include <simsimd/simsimd.h>
594
+ ```
595
+
596
+ ### Target Specific Backends
597
+
598
+ SimSIMD exposes all kernels for all backends, and you can select the most advanced one for the current CPU without relying on built-in dispatch mechanisms.
599
+ All of the function names follow the same pattern: `simsimd_{function}_{type}_{backend}`.
600
+
601
+ - The backend can be `serial`, `haswell`, `skylake`, `ice`, `sapphire`, `neon`, or `sve`.
602
+ - The type can be `f64`, `f32`, `f16`, `f64c`, `f32c`, `f16c`, `i8`, or `b8`.
603
+ - The function can be `dot`, `vdot`, `cos`, `l2sq`, `hamming`, `jaccard`, `kl`, or `js`.
604
+
605
+ To avoid hard-coding the backend, you can use the `simsimd_metric_punned_t` to pun the function pointer and the `simsimd_capabilities` function to get the available backends at runtime.
606
+
607
+ ```c
608
+ simsimd_dot_f64_sve
609
+ simsimd_cos_f64_sve
610
+ simsimd_l2sq_f64_sve
611
+ simsimd_dot_f64_skylake
612
+ simsimd_cos_f64_skylake
613
+ simsimd_l2sq_f64_skylake
614
+ simsimd_dot_f64_serial
615
+ simsimd_cos_f64_serial
616
+ simsimd_l2sq_f64_serial
617
+ simsimd_js_f64_serial
618
+ simsimd_kl_f64_serial
619
+ simsimd_dot_f32_sve
620
+ simsimd_cos_f32_sve
621
+ simsimd_l2sq_f32_sve
622
+ simsimd_dot_f32_neon
623
+ simsimd_cos_f32_neon
624
+ simsimd_l2sq_f32_neon
625
+ simsimd_js_f32_neon
626
+ simsimd_kl_f32_neon
627
+ simsimd_dot_f32_skylake
628
+ simsimd_cos_f32_skylake
629
+ simsimd_l2sq_f32_skylake
630
+ simsimd_js_f32_skylake
631
+ simsimd_kl_f32_skylake
632
+ simsimd_dot_f32_serial
633
+ simsimd_cos_f32_serial
634
+ simsimd_l2sq_f32_serial
635
+ simsimd_js_f32_serial
636
+ simsimd_kl_f32_serial
637
+ simsimd_dot_f16_sve
638
+ simsimd_cos_f16_sve
639
+ simsimd_l2sq_f16_sve
640
+ simsimd_dot_f16_neon
641
+ simsimd_cos_f16_neon
642
+ simsimd_l2sq_f16_neon
643
+ simsimd_js_f16_neon
644
+ simsimd_kl_f16_neon
645
+ simsimd_dot_f16_sapphire
646
+ simsimd_cos_f16_sapphire
647
+ simsimd_l2sq_f16_sapphire
648
+ simsimd_js_f16_sapphire
649
+ simsimd_kl_f16_sapphire
650
+ simsimd_dot_f16_haswell
651
+ simsimd_cos_f16_haswell
652
+ simsimd_l2sq_f16_haswell
653
+ simsimd_js_f16_haswell
654
+ simsimd_kl_f16_haswell
655
+ simsimd_dot_f16_serial
656
+ simsimd_cos_f16_serial
657
+ simsimd_l2sq_f16_serial
658
+ simsimd_js_f16_serial
659
+ simsimd_kl_f16_serial
660
+ simsimd_cos_i8_neon
661
+ simsimd_cos_i8_neon
662
+ simsimd_l2sq_i8_neon
663
+ simsimd_cos_i8_ice
664
+ simsimd_cos_i8_ice
665
+ simsimd_l2sq_i8_ice
666
+ simsimd_cos_i8_haswell
667
+ simsimd_cos_i8_haswell
668
+ simsimd_l2sq_i8_haswell
669
+ simsimd_cos_i8_serial
670
+ simsimd_cos_i8_serial
671
+ simsimd_l2sq_i8_serial
672
+ simsimd_hamming_b8_sve
673
+ simsimd_jaccard_b8_sve
674
+ simsimd_hamming_b8_neon
675
+ simsimd_jaccard_b8_neon
676
+ simsimd_hamming_b8_ice
677
+ simsimd_jaccard_b8_ice
678
+ simsimd_hamming_b8_haswell
679
+ simsimd_jaccard_b8_haswell
680
+ simsimd_hamming_b8_serial
681
+ simsimd_jaccard_b8_serial
682
+ simsimd_dot_f32c_sve
683
+ simsimd_vdot_f32c_sve
684
+ simsimd_dot_f32c_neon
685
+ simsimd_vdot_f32c_neon
686
+ simsimd_dot_f32c_haswell
687
+ simsimd_vdot_f32c_haswell
688
+ simsimd_dot_f32c_skylake
689
+ simsimd_vdot_f32c_skylake
690
+ simsimd_dot_f32c_serial
691
+ simsimd_vdot_f32c_serial
692
+ simsimd_dot_f64c_sve
693
+ simsimd_vdot_f64c_sve
694
+ simsimd_dot_f64c_skylake
695
+ simsimd_vdot_f64c_skylake
696
+ simsimd_dot_f64c_serial
697
+ simsimd_vdot_f64c_serial
698
+ simsimd_dot_f16c_sve
699
+ simsimd_vdot_f16c_sve
700
+ simsimd_dot_f16c_neon
701
+ simsimd_vdot_f16c_neon
702
+ simsimd_dot_f16c_haswell
703
+ simsimd_vdot_f16c_haswell
704
+ simsimd_dot_f16c_sapphire
705
+ simsimd_vdot_f16c_sapphire
706
+ simsimd_dot_f16c_serial
707
+ simsimd_vdot_f16c_serial
708
+ ```