usearch 2.21.2 → 2.21.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CONTRIBUTING.md +16 -9
- package/simsimd/Cargo.toml +7 -11
- package/simsimd/MANIFEST.in +1 -0
- package/simsimd/README.md +21 -20
- package/simsimd/build.rs +4 -3
- package/simsimd/c/lib.c +1 -1
- package/simsimd/include/simsimd/binary.h +11 -10
- package/simsimd/include/simsimd/dot.h +4 -4
- package/simsimd/include/simsimd/geospatial.h +1 -1
- package/simsimd/include/simsimd/mesh.h +1 -1
- package/simsimd/include/simsimd/probability.h +14 -14
- package/simsimd/include/simsimd/simsimd.h +86 -6
- package/simsimd/include/simsimd/sparse.h +60 -105
- package/simsimd/include/simsimd/types.h +4 -3
- package/simsimd/javascript/fallback.ts +10 -14
- package/simsimd/javascript/lib.c +1 -1
- package/simsimd/javascript/simsimd.ts +4 -4
- package/simsimd/package.json +1 -1
- package/simsimd/rust/lib.rs +288 -130
- package/simsimd/scripts/bench.cxx +38 -7
- package/simsimd/scripts/bench.rs +876 -0
- package/simsimd/scripts/test.py +1 -1
- package/simsimd/Cargo.lock +0 -713
- package/simsimd/scripts/bench_cosine.rs +0 -41
- package/simsimd/scripts/bench_native.rs +0 -272
- package/simsimd/scripts/bench_sqeuclidean.rs +0 -41
package/README.md
CHANGED
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/simsimd/CONTRIBUTING.md
CHANGED
|
@@ -49,7 +49,7 @@ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100
|
|
|
49
49
|
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
To compile with the default Apple Clang on
|
|
52
|
+
To compile with the default Apple Clang on macOS, use:
|
|
53
53
|
|
|
54
54
|
```sh
|
|
55
55
|
brew install openblas
|
|
@@ -63,8 +63,8 @@ cmake -D CMAKE_BUILD_TYPE=Release \
|
|
|
63
63
|
cmake --build build_release --config Release
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
On
|
|
67
|
-
Replacing the default compiler across the entire system is not recommended on
|
|
66
|
+
On macOS it's recommended to use Homebrew and install Clang, as opposed to "Apple Clang".
|
|
67
|
+
Replacing the default compiler across the entire system is not recommended on macOS, as it may break the system, but you can pass it as an environment variable:
|
|
68
68
|
|
|
69
69
|
```sh
|
|
70
70
|
brew install llvm openblas
|
|
@@ -124,7 +124,7 @@ Here, `-s` will output the logs.
|
|
|
124
124
|
The `-x` will stop on the first failure.
|
|
125
125
|
The `-Wd` will silence overflows and runtime warnings.
|
|
126
126
|
|
|
127
|
-
When building on
|
|
127
|
+
When building on macOS, same as with C/C++, use non-Apple Clang version:
|
|
128
128
|
|
|
129
129
|
```sh
|
|
130
130
|
brew install llvm
|
|
@@ -174,7 +174,7 @@ $ python scripts/bench_vectors.py --help
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
Before merging your changes you may want to test your changes against the entire matrix of Python versions USearch supports.
|
|
177
|
-
For that you need the `cibuildwheel`, which is tricky to use on
|
|
177
|
+
For that you need the `cibuildwheel`, which is tricky to use on macOS and Windows, as it would target just the local environment.
|
|
178
178
|
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:
|
|
179
179
|
|
|
180
180
|
```sh
|
|
@@ -184,7 +184,7 @@ cibuildwheel --platform linux # works on any OS and builds all
|
|
|
184
184
|
cibuildwheel --platform linux --archs x86_64 # 64-bit x86, the most common on desktop and servers
|
|
185
185
|
cibuildwheel --platform linux --archs aarch64 # 64-bit Arm for mobile devices, Apple M-series, and AWS Graviton
|
|
186
186
|
cibuildwheel --platform linux --archs i686 # 32-bit Linux
|
|
187
|
-
cibuildwheel --platform macos # works only on
|
|
187
|
+
cibuildwheel --platform macos # works only on macOS
|
|
188
188
|
cibuildwheel --platform windows # works only on Windows
|
|
189
189
|
```
|
|
190
190
|
|
|
@@ -194,7 +194,7 @@ You may need root privileges for multi-architecture builds:
|
|
|
194
194
|
sudo $(which cibuildwheel) --platform linux
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
On Windows and
|
|
197
|
+
On Windows and macOS, to avoid frequent path resolution issues, you may want to use:
|
|
198
198
|
|
|
199
199
|
```sh
|
|
200
200
|
python -m cibuildwheel --platform windows
|
|
@@ -209,6 +209,13 @@ cargo bench
|
|
|
209
209
|
open target/criterion/report/index.html
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
To automatically detect the Minimum Supported Rust Version (MSRV):
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
cargo +stable install cargo-msrv
|
|
216
|
+
cargo msrv find --ignore-lockfile
|
|
217
|
+
```
|
|
218
|
+
|
|
212
219
|
## JavaScript
|
|
213
220
|
|
|
214
221
|
### NodeJS
|
|
@@ -217,7 +224,7 @@ If you don't have the environment configured, here are the [installation options
|
|
|
217
224
|
|
|
218
225
|
```sh
|
|
219
226
|
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash # Linux
|
|
220
|
-
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash #
|
|
227
|
+
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash # macOS
|
|
221
228
|
```
|
|
222
229
|
|
|
223
230
|
Install dependencies:
|
|
@@ -242,7 +249,7 @@ If you don't have the environment configured, here are [installation options](ht
|
|
|
242
249
|
|
|
243
250
|
```sh
|
|
244
251
|
wget -qO- https://deno.land/x/install/install.sh | sh # Linux
|
|
245
|
-
curl -fsSL https://deno.land/install.sh | sh #
|
|
252
|
+
curl -fsSL https://deno.land/install.sh | sh # macOS
|
|
246
253
|
irm https://deno.land/install.ps1 | iex # Windows
|
|
247
254
|
```
|
|
248
255
|
|
package/simsimd/Cargo.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "simsimd"
|
|
3
3
|
description = "Portable mixed-precision BLAS-like vector math library for x86 and ARM"
|
|
4
|
-
version = "6.5.
|
|
4
|
+
version = "6.5.5"
|
|
5
5
|
edition = "2021"
|
|
6
6
|
license = "Apache-2.0"
|
|
7
7
|
authors = ["Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>"]
|
|
@@ -16,7 +16,8 @@ categories = [
|
|
|
16
16
|
"wasm",
|
|
17
17
|
"external-ffi-bindings",
|
|
18
18
|
]
|
|
19
|
-
|
|
19
|
+
rust-version = "1.64" # Introduced Core C FFI in stable Rust
|
|
20
|
+
include = ["rust/**", "c/**", "include/**", "build.rs"]
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
[lib]
|
|
@@ -24,18 +25,13 @@ name = "simsimd"
|
|
|
24
25
|
path = "rust/lib.rs"
|
|
25
26
|
|
|
26
27
|
[build-dependencies]
|
|
27
|
-
cc = "1.
|
|
28
|
+
cc = "1.2.36"
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
[[bench]]
|
|
31
|
-
name = "
|
|
32
|
+
name = "bench"
|
|
32
33
|
harness = false
|
|
33
|
-
path = "scripts/
|
|
34
|
-
|
|
35
|
-
[[bench]]
|
|
36
|
-
name = "sqeuclidean"
|
|
37
|
-
harness = false
|
|
38
|
-
path = "scripts/bench_sqeuclidean.rs"
|
|
34
|
+
path = "scripts/bench.rs"
|
|
39
35
|
|
|
40
36
|
[profile.bench]
|
|
41
37
|
opt-level = 3 # Corresponds to -O3
|
|
@@ -48,6 +44,6 @@ default = []
|
|
|
48
44
|
std = []
|
|
49
45
|
|
|
50
46
|
[dev-dependencies]
|
|
51
|
-
criterion = { version = "0.
|
|
47
|
+
criterion = { version = "0.7.0" }
|
|
52
48
|
rand = { version = "0.9.1" }
|
|
53
49
|
half = { version = "2.6.0" }
|
package/simsimd/MANIFEST.in
CHANGED
package/simsimd/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
Computing dot-products, similarity measures, and distances between low- and high-dimensional vectors is ubiquitous in Machine Learning, Scientific Computing,
|
|
3
|
+
Computing dot-products, similarity measures, and distances between low- and high-dimensional vectors is ubiquitous in Machine Learning, Scientific Computing, Geospatial Analysis, and Information Retrieval.
|
|
4
4
|
These algorithms generally have linear complexity in time, constant or linear complexity in space, and are data-parallel.
|
|
5
5
|
In other words, it is easily parallelizable and vectorizable and often available in packages like BLAS (level 1) and LAPACK, as well as higher-level `numpy` and `scipy` Python libraries.
|
|
6
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.
|
|
@@ -32,7 +32,7 @@ SimSIMD provides an alternative.
|
|
|
32
32
|
<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">
|
|
33
33
|
</a>
|
|
34
34
|
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
35
|
-
<img alt="GitHub Actions
|
|
35
|
+
<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">
|
|
36
36
|
</a>
|
|
37
37
|
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
38
38
|
<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">
|
|
@@ -43,7 +43,7 @@ SimSIMD provides an alternative.
|
|
|
43
43
|
## Features
|
|
44
44
|
|
|
45
45
|
__SimSIMD__ (Arabic: "سيمسيم دي") is a mixed-precision math library of __over 350 SIMD-optimized kernels__ extensively used in AI, Search, and DBMS workloads.
|
|
46
|
-
Named after the iconic ["Open Sesame"](https://en.wikipedia.org/wiki/Open_sesame) command that opened doors to treasure in _Ali Baba and the Forty Thieves_,
|
|
46
|
+
Named after the iconic ["Open Sesame"](https://en.wikipedia.org/wiki/Open_sesame) command that opened doors to treasure in _Ali Baba and the Forty Thieves_, SimSIMD can help you 10x the cost-efficiency of your computational pipelines.
|
|
47
47
|
Implemented distance functions include:
|
|
48
48
|
|
|
49
49
|
- Euclidean (L2) and Cosine (Angular) spatial distances for Vector Search. _[docs][docs-spatial]_
|
|
@@ -85,7 +85,7 @@ You can learn more about the technical implementation details in the following b
|
|
|
85
85
|
|
|
86
86
|
- [Uses Horner's method for polynomial approximations, beating GCC 12 by 119x](https://ashvardanian.com/posts/gcc-12-vs-avx512fp16/).
|
|
87
87
|
- [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).
|
|
88
|
-
- [Substitutes
|
|
88
|
+
- [Substitutes libc's `sqrt` with Newton Raphson iterations](https://github.com/ashvardanian/SimSIMD/releases/tag/v5.4.0).
|
|
89
89
|
- [Uses Galloping and SVE2 histograms to intersect sparse vectors](https://ashvardanian.com/posts/simd-set-intersections-sve2-avx512/).
|
|
90
90
|
- For Python: [avoids slow PyBind11, SWIG, & `PyArg_ParseTuple`](https://ashvardanian.com/posts/pybind11-cpython-tutorial/) [using faster calling convention](https://ashvardanian.com/posts/discount-on-keyword-arguments-in-python/).
|
|
91
91
|
- For JavaScript: [uses typed arrays and NAPI for zero-copy calls](https://ashvardanian.com/posts/javascript-ai-vector-search/).
|
|
@@ -625,13 +625,13 @@ fn main() {
|
|
|
625
625
|
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
|
|
626
626
|
let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
|
|
627
627
|
|
|
628
|
-
// Compute the cosine
|
|
629
|
-
let
|
|
628
|
+
// Compute the cosine distance between vectors
|
|
629
|
+
let cosine_distance = f32::cosine(&vector_a, &vector_b)
|
|
630
630
|
.expect("Vectors must be of the same length");
|
|
631
631
|
|
|
632
|
-
println!("Cosine
|
|
632
|
+
println!("Cosine Distance: {}", cosine_distance);
|
|
633
633
|
|
|
634
|
-
// Compute the squared Euclidean distance between
|
|
634
|
+
// Compute the squared Euclidean distance between vectors
|
|
635
635
|
let sq_euclidean_distance = f32::sqeuclidean(&vector_a, &vector_b)
|
|
636
636
|
.expect("Vectors must be of the same length");
|
|
637
637
|
|
|
@@ -648,16 +648,17 @@ use simsimd::SpatialSimilarity;
|
|
|
648
648
|
use simsimd::ComplexProducts;
|
|
649
649
|
|
|
650
650
|
fn main() {
|
|
651
|
+
// Complex vectors have interleaved real & imaginary components
|
|
651
652
|
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0, 4.0];
|
|
652
653
|
let vector_b: Vec<f32> = vec![5.0, 6.0, 7.0, 8.0];
|
|
653
654
|
|
|
654
|
-
// Compute the inner product between
|
|
655
|
+
// Compute the inner product between vectors
|
|
655
656
|
let inner_product = SpatialSimilarity::dot(&vector_a, &vector_b)
|
|
656
657
|
.expect("Vectors must be of the same length");
|
|
657
658
|
|
|
658
659
|
println!("Inner Product: {}", inner_product);
|
|
659
660
|
|
|
660
|
-
// Compute the complex inner product between
|
|
661
|
+
// Compute the complex inner product between vectors
|
|
661
662
|
let complex_inner_product = ComplexProducts::dot(&vector_a, &vector_b)
|
|
662
663
|
.expect("Vectors must be of the same length");
|
|
663
664
|
|
|
@@ -705,13 +706,13 @@ fn main() {
|
|
|
705
706
|
let vector_a = &[0b11110000, 0b00001111, 0b10101010];
|
|
706
707
|
let vector_b = &[0b11110000, 0b00001111, 0b01010101];
|
|
707
708
|
|
|
708
|
-
// Compute the Hamming distance between
|
|
709
|
+
// Compute the Hamming distance between vectors
|
|
709
710
|
let hamming_distance = u8::hamming(&vector_a, &vector_b)
|
|
710
711
|
.expect("Vectors must be of the same length");
|
|
711
712
|
|
|
712
713
|
println!("Hamming Distance: {}", hamming_distance);
|
|
713
714
|
|
|
714
|
-
// Compute the Jaccard distance between
|
|
715
|
+
// Compute the Jaccard distance between vectors
|
|
715
716
|
let jaccard_distance = u8::jaccard(&vector_a, &vector_b)
|
|
716
717
|
.expect("Vectors must be of the same length");
|
|
717
718
|
|
|
@@ -734,11 +735,11 @@ fn main() {
|
|
|
734
735
|
let vector_a: Vec<f16> = vec![1.0, 2.0, 3.0].iter().map(|&x| f16::from_f32(x)).collect();
|
|
735
736
|
let vector_b: Vec<f16> = vec![4.0, 5.0, 6.0].iter().map(|&x| f16::from_f32(x)).collect();
|
|
736
737
|
|
|
737
|
-
// Compute the cosine
|
|
738
|
-
let
|
|
738
|
+
// Compute the cosine distance
|
|
739
|
+
let cosine_distance = f16::cosine(&vector_a, &vector_b)
|
|
739
740
|
.expect("Vectors must be of the same length");
|
|
740
741
|
|
|
741
|
-
println!("Cosine
|
|
742
|
+
println!("Cosine Distance: {}", cosine_distance);
|
|
742
743
|
|
|
743
744
|
// Direct bit manipulation
|
|
744
745
|
let half = f16::from_f32(3.14159);
|
|
@@ -764,10 +765,10 @@ fn main() {
|
|
|
764
765
|
let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(vector_a.as_ptr() as *const SimF16, vector_a.len()) };
|
|
765
766
|
let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(vector_b.as_ptr() as *const SimF16, vector_b.len()) };
|
|
766
767
|
|
|
767
|
-
let
|
|
768
|
+
let cosine_distance = SimF16::cosine(buffer_a, buffer_b)
|
|
768
769
|
.expect("Vectors must be of the same length");
|
|
769
770
|
|
|
770
|
-
println!("Cosine
|
|
771
|
+
println!("Cosine Distance: {}", cosine_distance);
|
|
771
772
|
}
|
|
772
773
|
```
|
|
773
774
|
|
|
@@ -787,10 +788,10 @@ fn main() {
|
|
|
787
788
|
let vector_b: Vec<bf16> = vec![4.0, 5.0, 6.0].iter().map(|&x| bf16::from_f32(x)).collect();
|
|
788
789
|
|
|
789
790
|
// Compute the cosine similarity
|
|
790
|
-
let
|
|
791
|
+
let cosine_distance = bf16::cosine(&vector_a, &vector_b)
|
|
791
792
|
.expect("Vectors must be of the same length");
|
|
792
793
|
|
|
793
|
-
println!("Cosine
|
|
794
|
+
println!("Cosine Distance: {}", cosine_distance);
|
|
794
795
|
|
|
795
796
|
// Direct bit manipulation
|
|
796
797
|
let brain_half = bf16::from_f32(3.14159);
|
|
@@ -899,8 +900,8 @@ import SimSIMD
|
|
|
899
900
|
let vectorA: [Int8] = [1, 2, 3]
|
|
900
901
|
let vectorB: [Int8] = [4, 5, 6]
|
|
901
902
|
|
|
902
|
-
let cosineSimilarity = vectorA.cosine(vectorB) // Computes the cosine similarity
|
|
903
903
|
let dotProduct = vectorA.dot(vectorB) // Computes the dot product
|
|
904
|
+
let cosineDistance = vectorA.cosine(vectorB) // Computes the cosine distance
|
|
904
905
|
let sqEuclidean = vectorA.sqeuclidean(vectorB) // Computes the squared Euclidean distance
|
|
905
906
|
```
|
|
906
907
|
|
package/simsimd/build.rs
CHANGED
|
@@ -2,14 +2,15 @@ fn main() -> Result<(), cc::Error> {
|
|
|
2
2
|
let mut build = cc::Build::new();
|
|
3
3
|
|
|
4
4
|
build
|
|
5
|
+
// Prefer portable flags to support MSVC and older toolchains
|
|
6
|
+
.std("c99") // Enforce C99 standard when supported
|
|
5
7
|
.file("c/lib.c")
|
|
6
8
|
.include("include")
|
|
7
9
|
.define("SIMSIMD_NATIVE_F16", "0")
|
|
8
10
|
.define("SIMSIMD_NATIVE_BF16", "0")
|
|
9
11
|
.define("SIMSIMD_DYNAMIC_DISPATCH", "1")
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
.flag("-pedantic") // Ensure strict compliance with the C standard
|
|
12
|
+
.opt_level(3)
|
|
13
|
+
.flag_if_supported("-pedantic") // Strict compliance when supported
|
|
13
14
|
.warnings(false);
|
|
14
15
|
|
|
15
16
|
if let Err(e) = build.try_compile("simsimd") {
|
package/simsimd/c/lib.c
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* - Linux: everything is available in GCC 12+ and Clang 16+.
|
|
20
20
|
* - Windows - MSVC: everything except Sapphire Rapids and ARM SVE.
|
|
21
|
-
* -
|
|
21
|
+
* - macOS - Apple Clang: only Arm NEON and x86 AVX2 Haswell extensions are available.
|
|
22
22
|
*/
|
|
23
23
|
#if !defined(SIMSIMD_TARGET_NEON) && (defined(__APPLE__) || defined(__linux__))
|
|
24
24
|
#define SIMSIMD_TARGET_NEON 1
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* @date July 1, 2023
|
|
6
6
|
*
|
|
7
7
|
* Contains:
|
|
8
|
-
* - Hamming distance
|
|
9
|
-
* - Jaccard
|
|
8
|
+
* - Bit-level Hamming distance
|
|
9
|
+
* - Bit-level Jaccard distance (Tanimoto coefficient)
|
|
10
|
+
* - TODO: Hamming distance for integer vectors - `u16`, `u32`
|
|
10
11
|
*
|
|
11
12
|
* For hardware architectures:
|
|
12
13
|
* - Arm: NEON, SVE
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
* - Lookup tables, mostly using nibbles (4-bit lookups)
|
|
20
21
|
* - Harley-Seal population counts: https://arxiv.org/pdf/1611.07612
|
|
21
22
|
*
|
|
22
|
-
* On binary vectors, when computing Jaccard
|
|
23
|
+
* On binary vectors, when computing Jaccard distance we can clearly see how the CPU struggles
|
|
23
24
|
* to compute that many population counts. There are several instructions we should keep in mind
|
|
24
25
|
* for future optimizations:
|
|
25
26
|
*
|
|
@@ -55,23 +56,23 @@ extern "C" {
|
|
|
55
56
|
|
|
56
57
|
// clang-format off
|
|
57
58
|
|
|
58
|
-
/* Serial backends for bitsets. */
|
|
59
|
+
/* Serial backends for bitsets and integers. */
|
|
59
60
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_serial(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
60
61
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_serial(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
61
62
|
|
|
62
|
-
/* Arm NEON backend for bitsets. */
|
|
63
|
+
/* Arm NEON backend for bitsets and integers. */
|
|
63
64
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_neon(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
64
65
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_neon(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
65
66
|
|
|
66
|
-
/* Arm SVE backend for bitsets. */
|
|
67
|
+
/* Arm SVE backend for bitsets and integers. */
|
|
67
68
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_sve(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
68
69
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_sve(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
69
70
|
|
|
70
|
-
/* x86 AVX2 backend for bitsets for Intel Haswell CPUs and newer, needs only POPCNT extensions. */
|
|
71
|
+
/* x86 AVX2 backend for bitsets and integers for Intel Haswell CPUs and newer, needs only POPCNT extensions. */
|
|
71
72
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_haswell(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
72
73
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_haswell(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
73
74
|
|
|
74
|
-
/* x86 AVX512 backend for bitsets for Intel Ice Lake CPUs and newer, using VPOPCNTDQ extensions. */
|
|
75
|
+
/* x86 AVX512 backend for bitsets and integers for Intel Ice Lake CPUs and newer, using VPOPCNTDQ extensions. */
|
|
75
76
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_ice(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
76
77
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_ice(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words, simsimd_distance_t* distance);
|
|
77
78
|
// clang-format on
|
|
@@ -91,14 +92,14 @@ SIMSIMD_PUBLIC unsigned char simsimd_popcount_b8(simsimd_b8_t x) {
|
|
|
91
92
|
|
|
92
93
|
SIMSIMD_PUBLIC void simsimd_hamming_b8_serial(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n_words,
|
|
93
94
|
simsimd_distance_t *result) {
|
|
94
|
-
|
|
95
|
+
simsimd_u32_t differences = 0;
|
|
95
96
|
for (simsimd_size_t i = 0; i != n_words; ++i) differences += simsimd_popcount_b8(a[i] ^ b[i]);
|
|
96
97
|
*result = differences;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
SIMSIMD_PUBLIC void simsimd_jaccard_b8_serial(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n_words,
|
|
100
101
|
simsimd_distance_t *result) {
|
|
101
|
-
|
|
102
|
+
simsimd_u32_t intersection = 0, union_ = 0;
|
|
102
103
|
for (simsimd_size_t i = 0; i != n_words; ++i)
|
|
103
104
|
intersection += simsimd_popcount_b8(a[i] & b[i]), union_ += simsimd_popcount_b8(a[i] | b[i]);
|
|
104
105
|
*result = (union_ != 0) ? 1 - (simsimd_f64_t)intersection / (simsimd_f64_t)union_ : 1;
|
|
@@ -545,7 +545,7 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const *a_scalars, simsi
|
|
|
545
545
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
546
546
|
|
|
547
547
|
simsimd_dot_bf16_neon_cycle:
|
|
548
|
-
if (count_scalars <
|
|
548
|
+
if (count_scalars < 8) {
|
|
549
549
|
a_vec = _simsimd_partial_load_bf16x8_neon(a_scalars, count_scalars);
|
|
550
550
|
b_vec = _simsimd_partial_load_bf16x8_neon(b_scalars, count_scalars);
|
|
551
551
|
count_scalars = 0;
|
|
@@ -553,7 +553,7 @@ simsimd_dot_bf16_neon_cycle:
|
|
|
553
553
|
else {
|
|
554
554
|
a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)a_scalars);
|
|
555
555
|
b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)b_scalars);
|
|
556
|
-
a_scalars +=
|
|
556
|
+
a_scalars += 8, b_scalars += 8, count_scalars -= 8;
|
|
557
557
|
}
|
|
558
558
|
ab_vec = vbfdotq_f32(ab_vec, a_vec, b_vec);
|
|
559
559
|
if (count_scalars) goto simsimd_dot_bf16_neon_cycle;
|
|
@@ -1163,7 +1163,7 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a_scalars, simsim
|
|
|
1163
1163
|
__m256i b_i16_low_vec = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(b_i8_vec, 0));
|
|
1164
1164
|
__m256i b_i16_high_vec = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(b_i8_vec, 1));
|
|
1165
1165
|
|
|
1166
|
-
// Multiply and accumulate at int16 level, accumulate at `int32` level
|
|
1166
|
+
// Multiply and accumulate at `int16` level, accumulate at `int32` level
|
|
1167
1167
|
ab_i32_low_vec = _mm256_add_epi32(ab_i32_low_vec, _mm256_madd_epi16(a_i16_low_vec, b_i16_low_vec));
|
|
1168
1168
|
ab_i32_high_vec = _mm256_add_epi32(ab_i32_high_vec, _mm256_madd_epi16(a_i16_high_vec, b_i16_high_vec));
|
|
1169
1169
|
}
|
|
@@ -1197,7 +1197,7 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a_scalars, simsim
|
|
|
1197
1197
|
__m256i b_i16_low_vec = _mm256_unpacklo_epi8(b_u8_vec, zeros_vec);
|
|
1198
1198
|
__m256i b_i16_high_vec = _mm256_unpackhi_epi8(b_u8_vec, zeros_vec);
|
|
1199
1199
|
|
|
1200
|
-
// Multiply and accumulate at int16 level, accumulate at int32 level
|
|
1200
|
+
// Multiply and accumulate at `int16` level, accumulate at `int32` level
|
|
1201
1201
|
ab_i32_low_vec = _mm256_add_epi32(ab_i32_low_vec, _mm256_madd_epi16(a_i16_low_vec, b_i16_low_vec));
|
|
1202
1202
|
ab_i32_high_vec = _mm256_add_epi32(ab_i32_high_vec, _mm256_madd_epi16(a_i16_high_vec, b_i16_high_vec));
|
|
1203
1203
|
}
|
|
@@ -389,11 +389,9 @@ simsimd_js_f16_haswell_cycle:
|
|
|
389
389
|
b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b));
|
|
390
390
|
n -= 8, a += 8, b += 8;
|
|
391
391
|
}
|
|
392
|
-
a_vec = _mm256_add_ps(a_vec, epsilon_vec);
|
|
393
|
-
b_vec = _mm256_add_ps(b_vec, epsilon_vec);
|
|
394
392
|
__m256 m_vec = _mm256_mul_ps(_mm256_add_ps(a_vec, b_vec), _mm256_set1_ps(0.5f)); // M = (P + Q) / 2
|
|
395
|
-
__m256 ratio_a_vec = _mm256_div_ps(a_vec, m_vec);
|
|
396
|
-
__m256 ratio_b_vec = _mm256_div_ps(b_vec, m_vec);
|
|
393
|
+
__m256 ratio_a_vec = _mm256_div_ps(_mm256_add_ps(a_vec, epsilon_vec), _mm256_add_ps(m_vec, epsilon_vec));
|
|
394
|
+
__m256 ratio_b_vec = _mm256_div_ps(_mm256_add_ps(b_vec, epsilon_vec), _mm256_add_ps(m_vec, epsilon_vec));
|
|
397
395
|
__m256 log_ratio_a_vec = _simsimd_log2_f32_haswell(ratio_a_vec);
|
|
398
396
|
__m256 log_ratio_b_vec = _simsimd_log2_f32_haswell(ratio_b_vec);
|
|
399
397
|
__m256 prod_a_vec = _mm256_mul_ps(a_vec, log_ratio_a_vec);
|
|
@@ -487,13 +485,14 @@ simsimd_js_f32_skylake_cycle:
|
|
|
487
485
|
__mmask16 nonzero_mask_a = _mm512_cmp_ps_mask(a_vec, epsilon_vec, _CMP_GE_OQ);
|
|
488
486
|
__mmask16 nonzero_mask_b = _mm512_cmp_ps_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
|
|
489
487
|
__mmask16 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
|
|
490
|
-
__m512
|
|
491
|
-
__m512
|
|
492
|
-
__m512
|
|
488
|
+
__m512 m_with_epsilon = _mm512_add_ps(m_vec, epsilon_vec);
|
|
489
|
+
__m512 m_recip_approx = _mm512_rcp14_ps(m_with_epsilon);
|
|
490
|
+
__m512 ratio_a_vec = _mm512_mul_ps(_mm512_add_ps(a_vec, epsilon_vec), m_recip_approx);
|
|
491
|
+
__m512 ratio_b_vec = _mm512_mul_ps(_mm512_add_ps(b_vec, epsilon_vec), m_recip_approx);
|
|
493
492
|
__m512 log_ratio_a_vec = _simsimd_log2_f32_skylake(ratio_a_vec);
|
|
494
493
|
__m512 log_ratio_b_vec = _simsimd_log2_f32_skylake(ratio_b_vec);
|
|
495
|
-
sum_a_vec =
|
|
496
|
-
sum_b_vec =
|
|
494
|
+
sum_a_vec = _mm512_mask3_fmadd_ps(a_vec, log_ratio_a_vec, sum_a_vec, nonzero_mask);
|
|
495
|
+
sum_b_vec = _mm512_mask3_fmadd_ps(b_vec, log_ratio_b_vec, sum_b_vec, nonzero_mask);
|
|
497
496
|
if (n) goto simsimd_js_f32_skylake_cycle;
|
|
498
497
|
|
|
499
498
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|
|
@@ -579,13 +578,14 @@ simsimd_js_f16_sapphire_cycle:
|
|
|
579
578
|
__mmask32 nonzero_mask_a = _mm512_cmp_ph_mask(a_vec, epsilon_vec, _CMP_GE_OQ);
|
|
580
579
|
__mmask32 nonzero_mask_b = _mm512_cmp_ph_mask(b_vec, epsilon_vec, _CMP_GE_OQ);
|
|
581
580
|
__mmask32 nonzero_mask = nonzero_mask_a & nonzero_mask_b;
|
|
582
|
-
__m512h
|
|
583
|
-
__m512h
|
|
584
|
-
__m512h
|
|
581
|
+
__m512h m_with_epsilon = _mm512_add_ph(m_vec, epsilon_vec);
|
|
582
|
+
__m512h m_recip_approx = _mm512_rcp_ph(m_with_epsilon);
|
|
583
|
+
__m512h ratio_a_vec = _mm512_mul_ph(_mm512_add_ph(a_vec, epsilon_vec), m_recip_approx);
|
|
584
|
+
__m512h ratio_b_vec = _mm512_mul_ph(_mm512_add_ph(b_vec, epsilon_vec), m_recip_approx);
|
|
585
585
|
__m512h log_ratio_a_vec = _simsimd_log2_f16_sapphire(ratio_a_vec);
|
|
586
586
|
__m512h log_ratio_b_vec = _simsimd_log2_f16_sapphire(ratio_b_vec);
|
|
587
|
-
sum_a_vec =
|
|
588
|
-
sum_b_vec =
|
|
587
|
+
sum_a_vec = _mm512_mask3_fmadd_ph(a_vec, log_ratio_a_vec, sum_a_vec, nonzero_mask);
|
|
588
|
+
sum_b_vec = _mm512_mask3_fmadd_ph(b_vec, log_ratio_b_vec, sum_b_vec, nonzero_mask);
|
|
589
589
|
if (n) goto simsimd_js_f16_sapphire_cycle;
|
|
590
590
|
|
|
591
591
|
simsimd_f32_t log2_normalizer = 0.693147181f;
|