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.
- package/CITATION.cff +1 -1
- package/CONTRIBUTING.md +10 -2
- package/Cargo.toml +1 -1
- package/README.md +12 -15
- package/VERSION +1 -1
- package/c/test.c +58 -50
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +20 -10
- package/javascript/types/usearch.d.ts.map +1 -1
- package/javascript/usearch.js +4 -1
- package/package.json +1 -1
- package/simsimd/CONTRIBUTING.md +88 -0
- package/simsimd/Cargo.lock +4 -3
- package/simsimd/Cargo.toml +10 -2
- package/simsimd/README.md +102 -60
- package/simsimd/include/simsimd/binary.h +44 -0
- package/simsimd/include/simsimd/complex.h +174 -0
- package/simsimd/include/simsimd/simsimd.h +18 -9
- package/simsimd/include/simsimd/spatial.h +128 -36
- package/simsimd/include/simsimd/types.h +3 -2
- package/simsimd/package-lock.json +5 -5
- package/simsimd/package.json +1 -1
- package/simsimd/rust/benches/native.rs +12 -9
- package/simsimd/rust/lib.c +32 -55
- package/simsimd/rust/lib.rs +374 -56
- package/stringzilla/CONTRIBUTING.md +17 -0
- package/stringzilla/Cargo.lock +1 -1
- package/stringzilla/Cargo.toml +1 -1
- package/stringzilla/README.md +175 -34
- package/stringzilla/VERSION +1 -1
- package/stringzilla/build.rs +77 -4
- package/stringzilla/c/lib.c +33 -0
- package/stringzilla/cli/README.md +30 -13
- package/stringzilla/cli/__init__.py +0 -0
- package/stringzilla/cli/split.py +66 -33
- package/stringzilla/cli/wc.py +101 -21
- package/stringzilla/include/stringzilla/experimental.h +122 -1
- package/stringzilla/include/stringzilla/stringzilla.h +262 -132
- package/stringzilla/include/stringzilla/stringzilla.hpp +13 -5
- package/stringzilla/package.json +1 -1
- package/stringzilla/rust/lib.rs +673 -162
- package/wasmer.toml +1 -1
package/simsimd/README.md
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
# SimSIMD 📏
|
|
2
2
|
|
|
3
3
|
<div>
|
|
4
|
-
<a href="https://pepy.tech/project/simsimd">
|
|
5
|
-
<
|
|
6
|
-
|
|
4
|
+
<a href="https://pepy.tech/project/simsimd">
|
|
5
|
+
<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">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/simsimd">
|
|
8
|
+
<img alt="npm" src="https://img.shields.io/npm/dy/simsimd?label=npm%20dowloads">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://crates.io/crates/simsimd">
|
|
11
|
+
<img alt="rust" src="https://img.shields.io/crates/d/simsimd?logo=rust" />
|
|
12
|
+
</a>
|
|
7
13
|
<img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/ashvardanian/simsimd">
|
|
14
|
+
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
15
|
+
<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">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
18
|
+
<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">
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
21
|
+
<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">
|
|
22
|
+
</a>
|
|
23
|
+
<a href="https://github.com/ashvardanian/SimSIMD/actions/workflows/release.yml">
|
|
24
|
+
<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">
|
|
25
|
+
</a>
|
|
26
|
+
|
|
8
27
|
</div>
|
|
9
28
|
|
|
29
|
+
|
|
10
30
|
## Hardware-Accelerated Similarity Metrics and Distance Functions
|
|
11
31
|
|
|
12
32
|
- Zero-dependency [header-only C 99](#using-simsimd-in-c) library.
|
|
13
|
-
- Bindings for [Python](#using-simsimd-in-python), [Rust](#using-simsimd-in-rust) and [JavaScript](#using-simsimd-in-javascript).
|
|
14
|
-
- Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
|
|
15
|
-
- Zero-copy compatible with NumPy, PyTorch, TensorFlow, and other tensors.
|
|
16
33
|
- Handles `f64` double-, `f32` single-, and `f16` half-precision, `i8` integral, and binary vectors.
|
|
34
|
+
- Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
|
|
35
|
+
- Bindings for [Python](#using-simsimd-in-python), [Rust](#using-simsimd-in-rust) and [JavaScript](#using-simsimd-in-javascript).
|
|
17
36
|
- __Up to 200x faster__ than [`scipy.spatial.distance`][scipy] and [`numpy.inner`][numpy].
|
|
37
|
+
- [Supports more Python versions][compatibility] than SciPy and NumPy.
|
|
38
|
+
- Zero-copy compatible with NumPy, PyTorch, TensorFlow, and other tensors.
|
|
18
39
|
- Used in [USearch](https://github.com/unum-cloud/usearch) and several DBMS products.
|
|
19
40
|
|
|
20
41
|
__Implemented distance functions__ include:
|
|
@@ -25,6 +46,7 @@ __Implemented distance functions__ include:
|
|
|
25
46
|
|
|
26
47
|
[scipy]: https://docs.scipy.org/doc/scipy/reference/spatial.distance.html#module-scipy.spatial.distance
|
|
27
48
|
[numpy]: https://numpy.org/doc/stable/reference/generated/numpy.inner.html
|
|
49
|
+
[compatibility]: https://pypi.org/project/simsimd/#files
|
|
28
50
|
|
|
29
51
|
__Technical Insights__ and related articles:
|
|
30
52
|
|
|
@@ -67,6 +89,11 @@ __Broader Benchmarking Results__:
|
|
|
67
89
|
|
|
68
90
|
## Using SimSIMD in Python
|
|
69
91
|
|
|
92
|
+
The package is intended to replace the usage of `numpy.inner`, `numpy.dot`, and `scipy.spatial.distance`.
|
|
93
|
+
Aside from drastic performance improvements, SimSIMD significantly improves accuracy in mixed precision setups.
|
|
94
|
+
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 entirely avoid overflows.
|
|
95
|
+
The same applies to processing `f16` values with `f32` precision.
|
|
96
|
+
|
|
70
97
|
### Installation
|
|
71
98
|
|
|
72
99
|
```sh
|
|
@@ -85,6 +112,14 @@ dist = simsimd.cosine(vec1, vec2)
|
|
|
85
112
|
```
|
|
86
113
|
|
|
87
114
|
Supported functions include `cosine`, `inner`, `sqeuclidean`, `hamming`, and `jaccard`.
|
|
115
|
+
Unlike SciPy, SimSIMD allows explicitly stating the precision of the input vectors, which is especially useful for mixed-precision setups.
|
|
116
|
+
|
|
117
|
+
```py
|
|
118
|
+
dist = simsimd.cosine(vec1, vec2, "i8")
|
|
119
|
+
dist = simsimd.cosine(vec1, vec2, "f16")
|
|
120
|
+
dist = simsimd.cosine(vec1, vec2, "f32")
|
|
121
|
+
dist = simsimd.cosine(vec1, vec2, "f64")
|
|
122
|
+
```
|
|
88
123
|
|
|
89
124
|
### Distance Between 2 Batches
|
|
90
125
|
|
|
@@ -150,20 +185,73 @@ To install, add the following to your `Cargo.toml`:
|
|
|
150
185
|
simsimd = "..."
|
|
151
186
|
```
|
|
152
187
|
|
|
153
|
-
|
|
188
|
+
Before using the SimSIMD library, ensure you have imported the necessary traits and types into your Rust source file.
|
|
189
|
+
The library provides several traits for different distance/similarity kinds - `SpatialSimilarity`, `BinarySimilarity`, and `ProbabilitySimilarity`.
|
|
154
190
|
|
|
155
191
|
```rust
|
|
156
|
-
use simsimd::
|
|
192
|
+
use simsimd::SpatialSimilarity;
|
|
157
193
|
|
|
158
194
|
fn main() {
|
|
159
|
-
let vector_a = vec![1.0, 2.0, 3.0];
|
|
160
|
-
let vector_b = vec![4.0, 5.0, 6.0];
|
|
195
|
+
let vector_a: Vec<f32> = vec![1.0, 2.0, 3.0];
|
|
196
|
+
let vector_b: Vec<f32> = vec![4.0, 5.0, 6.0];
|
|
161
197
|
|
|
162
|
-
|
|
163
|
-
|
|
198
|
+
// Compute the cosine similarity between vector_a and vector_b
|
|
199
|
+
let cosine_similarity = f32::cosine(&vector_a, &vector_b)
|
|
200
|
+
.expect("Vectors must be of the same length");
|
|
164
201
|
|
|
165
|
-
|
|
166
|
-
|
|
202
|
+
println!("Cosine Similarity: {}", cosine_similarity);
|
|
203
|
+
|
|
204
|
+
// Compute the squared Euclidean distance between vector_a and vector_b
|
|
205
|
+
let sq_euclidean_distance = f32::sqeuclidean(&vector_a, &vector_b)
|
|
206
|
+
.expect("Vectors must be of the same length");
|
|
207
|
+
|
|
208
|
+
println!("Squared Euclidean Distance: {}", sq_euclidean_distance);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Similarly, one can compute bit-level distance functions between slices of unsigned integers:
|
|
213
|
+
|
|
214
|
+
```rust
|
|
215
|
+
use simsimd::BinarySimilarity;
|
|
216
|
+
|
|
217
|
+
fn main() {
|
|
218
|
+
let vector_a = &[0b11110000, 0b00001111, 0b10101010];
|
|
219
|
+
let vector_b = &[0b11110000, 0b00001111, 0b01010101];
|
|
220
|
+
|
|
221
|
+
// Compute the Hamming distance between vector_a and vector_b
|
|
222
|
+
let hamming_distance = u8::hamming(&vector_a, &vector_b)
|
|
223
|
+
.expect("Vectors must be of the same length");
|
|
224
|
+
|
|
225
|
+
println!("Hamming Distance: {}", hamming_distance);
|
|
226
|
+
|
|
227
|
+
// Compute the Jaccard distance between vector_a and vector_b
|
|
228
|
+
let jaccard_distance = u8::jaccard(&vector_a, &vector_b)
|
|
229
|
+
.expect("Vectors must be of the same length");
|
|
230
|
+
|
|
231
|
+
println!("Jaccard Distance: {}", jaccard_distance);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Rust has no native support for half-precision floating-point numbers, but SimSIMD provides a `f16` type for this purpose.
|
|
236
|
+
It doesn't have any functionality and is a `transparent` wrapper around `u16`, so it can be used with `half`, or any other half-precision library.
|
|
237
|
+
|
|
238
|
+
```rust
|
|
239
|
+
use simsimd::SpatialSimilarity;
|
|
240
|
+
use simsimd::f16 as SimF16;
|
|
241
|
+
use half::f16 as HalfF16;
|
|
242
|
+
|
|
243
|
+
fn main() {
|
|
244
|
+
let vector_a: Vec<HalfF16> = ...
|
|
245
|
+
let vector_b: Vec<HalfF16> = ...
|
|
246
|
+
|
|
247
|
+
let buffer_a: &[SimF16] = unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const SimF16, a_half.len()) };
|
|
248
|
+
let buffer_b: &[SimF16] = unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const SimF16, b_half.len()) };
|
|
249
|
+
|
|
250
|
+
// Compute the cosine similarity between vector_a and vector_b
|
|
251
|
+
let cosine_similarity = SimF16::cosine(&vector_a, &vector_b)
|
|
252
|
+
.expect("Vectors must be of the same length");
|
|
253
|
+
|
|
254
|
+
println!("Cosine Similarity: {}", cosine_similarity);
|
|
167
255
|
}
|
|
168
256
|
```
|
|
169
257
|
|
|
@@ -227,49 +315,3 @@ All of the functions names follow the same pattern: `simsimd_{backend}_{type}_{m
|
|
|
227
315
|
- The metric can be `cos`, `ip`, `l2sq`, `hamming`, `jaccard`, `kl`, or `js`.
|
|
228
316
|
|
|
229
317
|
In case you want to avoid hard-coding the backend, you can use the `simsimd_metric_punned_t` to pun the function pointer, and `simsimd_capabilities` function to get the available backends at runtime.
|
|
230
|
-
|
|
231
|
-
## Benchmarking and Contributing
|
|
232
|
-
|
|
233
|
-
__To rerun experiments__ utilize the following command:
|
|
234
|
-
|
|
235
|
-
```sh
|
|
236
|
-
cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build_release
|
|
237
|
-
cmake --build build_release --config Release
|
|
238
|
-
./build_release/simsimd_bench
|
|
239
|
-
./build_release/simsimd_bench --benchmark_filter=js
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
__To test and benchmark with Python bindings__:
|
|
243
|
-
|
|
244
|
-
```sh
|
|
245
|
-
pip install -e .
|
|
246
|
-
pytest python/test.py -s -x
|
|
247
|
-
|
|
248
|
-
pip install numpy scipy scikit-learn # for comparison baselines
|
|
249
|
-
python python/bench.py # to run default benchmarks
|
|
250
|
-
python python/bench.py --n 1000 --ndim 1000000 # batch size and dimensions
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
__To test and benchmark JavaScript bindings__:
|
|
254
|
-
|
|
255
|
-
```sh
|
|
256
|
-
npm install --dev
|
|
257
|
-
npm test
|
|
258
|
-
npm run bench
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
__To test and benchmark GoLang bindings__:
|
|
262
|
-
|
|
263
|
-
```sh
|
|
264
|
-
cd golang
|
|
265
|
-
go test # To test
|
|
266
|
-
go test -run=^$ -bench=. -benchmem # To benchmark
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
__To test and benchmark Rust bindings__:
|
|
270
|
-
|
|
271
|
-
```sh
|
|
272
|
-
cargo test
|
|
273
|
-
cargo bench
|
|
274
|
-
open ./target/criterion/report/index.html
|
|
275
|
-
```
|
|
@@ -190,6 +190,50 @@ simsimd_avx512_b8_jaccard_cycle:
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
#endif // SIMSIMD_TARGET_X86_AVX512
|
|
193
|
+
|
|
194
|
+
/*
|
|
195
|
+
* @file x86_avx2_b8.h
|
|
196
|
+
* @brief x86 AVX2 implementation of the most common similarity metrics for bitsets.
|
|
197
|
+
* @author Ash Vardanian
|
|
198
|
+
*
|
|
199
|
+
* - Implements: Hamming and Jaccard similarity.
|
|
200
|
+
* - Requires compiler capabilities: avx2.
|
|
201
|
+
*
|
|
202
|
+
* AVX2 provides no built-in functionality for popcount, so we use the BMI2 instruction set.
|
|
203
|
+
* Wojciech Mu la, Nathan Kurz and Daniel Lemire propose an alternative solution based on Harley-Seal
|
|
204
|
+
* transforms and lookup tables. Those are very fast on large vectors, but on short vectors they lose
|
|
205
|
+
* to built-in scalar population counts. https://arxiv.org/pdf/1611.07612.pdf
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
#if SIMSIMD_TARGET_X86_AVX2
|
|
209
|
+
|
|
210
|
+
__attribute__((target("popcnt"))) //
|
|
211
|
+
inline static simsimd_f32_t
|
|
212
|
+
simsimd_avx2_b8_hamming(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words) {
|
|
213
|
+
// x86 supports unaligned loads and works just fine with the scalar version for small vectors.
|
|
214
|
+
simsimd_size_t differences = 0;
|
|
215
|
+
for (; n_words >= 8; n_words -= 8, a += 8, b += 8)
|
|
216
|
+
differences += _mm_popcnt_u64(*(simsimd_u64_t const*)a ^ *(simsimd_u64_t const*)b);
|
|
217
|
+
for (; n_words; --n_words, ++a, ++b)
|
|
218
|
+
differences += _mm_popcnt_u32(*a ^ *b);
|
|
219
|
+
return (simsimd_f32_t)differences;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
__attribute__((target("popcnt"))) //
|
|
223
|
+
inline static simsimd_f32_t
|
|
224
|
+
simsimd_avx2_b8_jaccard(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n_words) {
|
|
225
|
+
// x86 supports unaligned loads and works just fine with the scalar version for small vectors.
|
|
226
|
+
simsimd_size_t intersection = 0, union_ = 0;
|
|
227
|
+
for (; n_words >= 8; n_words -= 8, a += 8, b += 8)
|
|
228
|
+
intersection += _mm_popcnt_u64(*(simsimd_u64_t const*)a & *(simsimd_u64_t const*)b),
|
|
229
|
+
union_ += _mm_popcnt_u64(*(simsimd_u64_t const*)a | *(simsimd_u64_t const*)b);
|
|
230
|
+
for (; n_words; --n_words, ++a, ++b)
|
|
231
|
+
intersection += _mm_popcnt_u32(*a & *b), union_ += _mm_popcnt_u32(*a | *b);
|
|
232
|
+
return (union_ != 0) ? 1 - (simsimd_f32_t)intersection / (simsimd_f32_t)union_ : 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
#endif // SIMSIMD_TARGET_X86_AVX2
|
|
236
|
+
|
|
193
237
|
#endif // SIMSIMD_TARGET_X86
|
|
194
238
|
|
|
195
239
|
#undef popcount64
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @brief SIMD-accelerated Similarity Measures for Complex Vectors.
|
|
3
|
+
* @author Ash Vardanian
|
|
4
|
+
* @date February 24, 2024
|
|
5
|
+
*
|
|
6
|
+
* Contains:
|
|
7
|
+
* - Inner Product
|
|
8
|
+
* - Cosine Distance
|
|
9
|
+
*
|
|
10
|
+
* For datatypes:
|
|
11
|
+
* - 32-bit floating point numbers
|
|
12
|
+
* - 16-bit floating point numbers
|
|
13
|
+
*
|
|
14
|
+
* For hardware architectures:
|
|
15
|
+
* - Arm (NEON, SVE?)
|
|
16
|
+
* - x86 (AVX2?, AVX512?)
|
|
17
|
+
*
|
|
18
|
+
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
19
|
+
* Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
|
|
20
|
+
*/
|
|
21
|
+
#ifndef SIMSIMD_COMPLEX_H
|
|
22
|
+
#define SIMSIMD_COMPLEX_H
|
|
23
|
+
|
|
24
|
+
#include "types.h"
|
|
25
|
+
|
|
26
|
+
#define SIMSIMD_MAKE_COMPLEX_IP(name, input_type, accumulator_type, converter, epsilon) \
|
|
27
|
+
inline static simsimd_f32_t simsimd_##name##_##input_type##_complex_ip( \
|
|
28
|
+
simsimd_##input_type##_t const* a, simsimd_##input_type##_t const* b, simsimd_size_t n, \
|
|
29
|
+
simsimd_f32_t* real_out, simsimd_f32_t* imag_out) { \
|
|
30
|
+
simsimd_##accumulator_type##_t ab_real, ab_imag; \
|
|
31
|
+
for (simsimd_size_t i = 0; i + 2 <= n; i += 2) { \
|
|
32
|
+
simsimd_##accumulator_type##_t ar = converter(a[i]); \
|
|
33
|
+
simsimd_##accumulator_type##_t br = converter(b[i]); \
|
|
34
|
+
simsimd_##accumulator_type##_t ai = converter(a[i + 1]); \
|
|
35
|
+
simsimd_##accumulator_type##_t bi = converter(b[i + 1]); \
|
|
36
|
+
ab_real += ar * br + ai * bi; \
|
|
37
|
+
ab_imag += ai * br - ar * bi; \
|
|
38
|
+
} \
|
|
39
|
+
*real_out = ab_real; \
|
|
40
|
+
*imag_out = ab_imag; \
|
|
41
|
+
return 1 - ab_real; \
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#define SIMSIMD_MAKE_COMPLEX_COS(name, input_type, accumulator_type, converter, epsilon) \
|
|
45
|
+
inline static simsimd_f32_t simsimd_##name##_##input_type##_complex_cos( \
|
|
46
|
+
simsimd_##input_type##_t const* a, simsimd_##input_type##_t const* b, simsimd_size_t n, \
|
|
47
|
+
simsimd_f32_t* real_out, simsimd_f32_t* imag_out) { \
|
|
48
|
+
simsimd_##accumulator_type##_t ab_real, ab_imag, a2_sum, b2_sum; \
|
|
49
|
+
for (simsimd_size_t i = 0; i + 2 <= n; i += 2) { \
|
|
50
|
+
simsimd_##accumulator_type##_t ar = converter(a[i]); \
|
|
51
|
+
simsimd_##accumulator_type##_t br = converter(b[i]); \
|
|
52
|
+
simsimd_##accumulator_type##_t ai = converter(a[i + 1]); \
|
|
53
|
+
simsimd_##accumulator_type##_t bi = converter(b[i + 1]); \
|
|
54
|
+
ab_real += ar * br + ai * bi; \
|
|
55
|
+
ab_imag += ai * br - ar * bi; \
|
|
56
|
+
a2_sum += ar * ar + ai * ai; \
|
|
57
|
+
b2_sum += br * br + bi * bi; \
|
|
58
|
+
} \
|
|
59
|
+
*real_out = ab_real; \
|
|
60
|
+
*imag_out = ab_imag; \
|
|
61
|
+
simsimd_##accumulator_type##_t ab_angle = ab_real * SIMSIMD_RSQRT(a2_sum) * SIMSIMD_RSQRT(b2_sum); \
|
|
62
|
+
return 1 - ab_angle; \
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#ifdef __cplusplus
|
|
66
|
+
extern "C" {
|
|
67
|
+
#endif
|
|
68
|
+
|
|
69
|
+
SIMSIMD_MAKE_COMPLEX_IP(serial, f64, f64, SIMSIMD_IDENTIFY,
|
|
70
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f64_complex_ip
|
|
71
|
+
SIMSIMD_MAKE_COMPLEX_COS(serial, f64, f64, SIMSIMD_IDENTIFY,
|
|
72
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f64_complex_cos
|
|
73
|
+
|
|
74
|
+
SIMSIMD_MAKE_COMPLEX_IP(serial, f32, f32, SIMSIMD_IDENTIFY,
|
|
75
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f32_complex_ip
|
|
76
|
+
SIMSIMD_MAKE_COMPLEX_COS(serial, f32, f32, SIMSIMD_IDENTIFY,
|
|
77
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f32_complex_cos
|
|
78
|
+
|
|
79
|
+
SIMSIMD_MAKE_COMPLEX_IP(serial, f16, f32, SIMSIMD_UNCOMPRESS_F16,
|
|
80
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f16_complex_ip
|
|
81
|
+
SIMSIMD_MAKE_COMPLEX_COS(serial, f16, f32, SIMSIMD_UNCOMPRESS_F16,
|
|
82
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_serial_f16_complex_cos
|
|
83
|
+
|
|
84
|
+
SIMSIMD_MAKE_COMPLEX_IP(accurate, f32, f64, SIMSIMD_IDENTIFY,
|
|
85
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_accurate_f32_complex_ip
|
|
86
|
+
SIMSIMD_MAKE_COMPLEX_COS(accurate, f32, f64, SIMSIMD_IDENTIFY,
|
|
87
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_accurate_f32_complex_cos
|
|
88
|
+
|
|
89
|
+
SIMSIMD_MAKE_COMPLEX_IP(accurate, f16, f64, SIMSIMD_UNCOMPRESS_F16,
|
|
90
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_accurate_f16_complex_ip
|
|
91
|
+
SIMSIMD_MAKE_COMPLEX_COS(accurate, f16, f64, SIMSIMD_UNCOMPRESS_F16,
|
|
92
|
+
SIMSIMD_F32_DIVISION_EPSILON) // simsimd_accurate_f16_complex_cos
|
|
93
|
+
|
|
94
|
+
#if SIMSIMD_TARGET_ARM
|
|
95
|
+
#if SIMSIMD_TARGET_ARM_NEON
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* @file arm_neon_f32.h
|
|
99
|
+
* @brief Arm NEON implementation of the most common similarity metrics for 32-bit floating point numbers.
|
|
100
|
+
* @author Ash Vardanian
|
|
101
|
+
*
|
|
102
|
+
* - Implements: Kullback-Leibler and Jensen–Shannon divergence.
|
|
103
|
+
* - Uses `f32` for storage and `f32` for accumulation.
|
|
104
|
+
* - Requires compiler capabilities: +simd.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
__attribute__((target("+simd"))) //
|
|
108
|
+
inline static simsimd_f32_t
|
|
109
|
+
simsimd_neon_f32_complex_cos(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, //
|
|
110
|
+
simsimd_f32_t* real_out, simsimd_f32_t* imag_out) {
|
|
111
|
+
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
112
|
+
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
113
|
+
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
114
|
+
float32x4_t b2_vec = vdupq_n_f32(0);
|
|
115
|
+
simsimd_size_t i = 0;
|
|
116
|
+
for (; i + 8 <= n; i += 8) {
|
|
117
|
+
// Unpack the input arrays into real and imaginary parts:
|
|
118
|
+
// a_real = a[::2]
|
|
119
|
+
// a_imag = a[1::2]
|
|
120
|
+
// b_real = b[::2]
|
|
121
|
+
// b_imag = b[1::2]
|
|
122
|
+
float32x4x2_t a_vec = vld2q_f32(a + i);
|
|
123
|
+
float32x4x2_t b_vec = vld2q_f32(b + i);
|
|
124
|
+
float32x4_t a_real_vec = a_vec.val[0];
|
|
125
|
+
float32x4_t a_imag_vec = a_vec.val[1];
|
|
126
|
+
float32x4_t b_real_vec = b_vec.val[0];
|
|
127
|
+
float32x4_t b_imag_vec = b_vec.val[1];
|
|
128
|
+
|
|
129
|
+
// Compute the dot product:
|
|
130
|
+
// ab_real = sum(ar * br + ai * bi for ar, ai, br, bi in zip(a_real, a_imag, b_real, b_imag))
|
|
131
|
+
// ab_imag = sum(ai * br - ar * bi for ar, ai, br, bi in zip(a_real, a_imag, b_real, b_imag))
|
|
132
|
+
ab_real_vec = vfmaq_f32(ab_real_vec, a_real_vec, b_real_vec);
|
|
133
|
+
ab_real_vec = vfmaq_f32(ab_real_vec, a_imag_vec, b_imag_vec);
|
|
134
|
+
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
|
|
135
|
+
ab_imag_vec = vfmsq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
|
|
136
|
+
|
|
137
|
+
// Compute the vector norms:
|
|
138
|
+
// a2 = sum(ar * ar + ai * ai for ar, ai in zip(a_real, a_imag))
|
|
139
|
+
// b2 = sum(br * br + bi * bi for br, bi in zip(b_real, b_imag))
|
|
140
|
+
a2_vec = vfmaq_f32(a2_vec, a_real_vec, a_real_vec);
|
|
141
|
+
a2_vec = vfmaq_f32(a2_vec, a_imag_vec, a_imag_vec);
|
|
142
|
+
b2_vec = vfmaq_f32(b2_vec, b_real_vec, b_real_vec);
|
|
143
|
+
b2_vec = vfmaq_f32(b2_vec, b_imag_vec, b_imag_vec);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Reduce horizontal sums:
|
|
147
|
+
simsimd_f32_t ab_real = vaddvq_f32(ab_real_vec);
|
|
148
|
+
simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
|
|
149
|
+
simsimd_f32_t a2 = vaddvq_f32(a2_vec);
|
|
150
|
+
simsimd_f32_t b2 = vaddvq_f32(b2_vec);
|
|
151
|
+
|
|
152
|
+
// Handle the tail:
|
|
153
|
+
for (; i < n; i += 2) {
|
|
154
|
+
simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
|
|
155
|
+
ab_real += ar * br + ai * bi, ab_imag += ai * br - ar * bi;
|
|
156
|
+
a2 += ar * ar + ai * ai, b2 += br * br + bi * bi;
|
|
157
|
+
}
|
|
158
|
+
*real_out = ab_real;
|
|
159
|
+
*imag_out = ab_imag;
|
|
160
|
+
|
|
161
|
+
// Avoid `simsimd_approximate_inverse_square_root` on Arm NEON
|
|
162
|
+
simsimd_f32_t a2_b2_arr[2] = {a2, b2};
|
|
163
|
+
vst1_f32(a2_b2_arr, vrsqrte_f32(vld1_f32(a2_b2_arr)));
|
|
164
|
+
return ab_real != 0 ? 1 - ab_real * a2_b2_arr[0] * a2_b2_arr[1] : 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#endif // SIMSIMD_TARGET_ARM_NEON
|
|
168
|
+
#endif // SIMSIMD_TARGET_ARM
|
|
169
|
+
|
|
170
|
+
#ifdef __cplusplus
|
|
171
|
+
}
|
|
172
|
+
#endif
|
|
173
|
+
|
|
174
|
+
#endif
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
#define SIMSIMD_H
|
|
16
16
|
|
|
17
17
|
#define SIMSIMD_VERSION_MAJOR 3
|
|
18
|
-
#define SIMSIMD_VERSION_MINOR
|
|
18
|
+
#define SIMSIMD_VERSION_MINOR 9
|
|
19
19
|
#define SIMSIMD_VERSION_PATCH 0
|
|
20
20
|
|
|
21
21
|
#include "binary.h" // Hamming, Jaccard
|
|
22
|
+
#include "complex.h" // Complex numbers
|
|
22
23
|
#include "probability.h" // Kullback-Leibler, Jensen–Shannon
|
|
23
24
|
#include "spatial.h" // L2, Inner Product, Cosine
|
|
24
25
|
|
|
@@ -388,14 +389,6 @@ inline static void simsimd_find_metric_punned( //
|
|
|
388
389
|
// Binary vectors
|
|
389
390
|
case simsimd_datatype_b8_k:
|
|
390
391
|
|
|
391
|
-
#if SIMSIMD_TARGET_ARM_NEON
|
|
392
|
-
if (viable & simsimd_cap_arm_neon_k)
|
|
393
|
-
switch (kind) {
|
|
394
|
-
case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_hamming, *c = simsimd_cap_arm_neon_k; return;
|
|
395
|
-
case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_jaccard, *c = simsimd_cap_arm_neon_k; return;
|
|
396
|
-
default: break;
|
|
397
|
-
}
|
|
398
|
-
#endif
|
|
399
392
|
#if SIMSIMD_TARGET_ARM_SVE
|
|
400
393
|
if (viable & simsimd_cap_arm_sve_k)
|
|
401
394
|
switch (kind) {
|
|
@@ -404,6 +397,14 @@ inline static void simsimd_find_metric_punned( //
|
|
|
404
397
|
default: break;
|
|
405
398
|
}
|
|
406
399
|
#endif
|
|
400
|
+
#if SIMSIMD_TARGET_ARM_NEON
|
|
401
|
+
if (viable & simsimd_cap_arm_neon_k)
|
|
402
|
+
switch (kind) {
|
|
403
|
+
case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_hamming, *c = simsimd_cap_arm_neon_k; return;
|
|
404
|
+
case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_jaccard, *c = simsimd_cap_arm_neon_k; return;
|
|
405
|
+
default: break;
|
|
406
|
+
}
|
|
407
|
+
#endif
|
|
407
408
|
#if SIMSIMD_TARGET_X86_AVX512
|
|
408
409
|
if (viable & simsimd_cap_x86_avx512vpopcntdq_k)
|
|
409
410
|
switch (kind) {
|
|
@@ -412,6 +413,14 @@ inline static void simsimd_find_metric_punned( //
|
|
|
412
413
|
default: break;
|
|
413
414
|
}
|
|
414
415
|
#endif
|
|
416
|
+
#if SIMSIMD_TARGET_X86_AVX2
|
|
417
|
+
if (viable & simsimd_cap_x86_avx2_k)
|
|
418
|
+
switch (kind) {
|
|
419
|
+
case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_b8_hamming, *c = simsimd_cap_x86_avx2_k; return;
|
|
420
|
+
case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_b8_jaccard, *c = simsimd_cap_x86_avx2_k; return;
|
|
421
|
+
default: break;
|
|
422
|
+
}
|
|
423
|
+
#endif
|
|
415
424
|
|
|
416
425
|
if (viable & simsimd_cap_serial_k)
|
|
417
426
|
switch (kind) {
|