usearch 2.8.16 → 2.9.0

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 (62) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
  2. package/.github/workflows/prerelease.yml +33 -7
  3. package/.github/workflows/release.yml +25 -26
  4. package/.gitmodules +4 -1
  5. package/CITATION.cff +1 -1
  6. package/CMakeLists.txt +6 -8
  7. package/CONTRIBUTING.md +20 -1
  8. package/Cargo.toml +1 -1
  9. package/README.md +7 -6
  10. package/VERSION +1 -1
  11. package/conanfile.py +1 -1
  12. package/cpp/test.cpp +9 -2
  13. package/include/usearch/index.hpp +11 -11
  14. package/include/usearch/index_plugins.hpp +2 -1
  15. package/javascript/usearch.js +1 -1
  16. package/package.json +1 -1
  17. package/pyproject.toml +2 -2
  18. package/setup.py +10 -4
  19. package/simsimd/.releaserc +3 -2
  20. package/simsimd/Cargo.lock +708 -0
  21. package/simsimd/Cargo.toml +36 -0
  22. package/simsimd/README.md +37 -1
  23. package/simsimd/build.rs +20 -0
  24. package/simsimd/include/simsimd/simsimd.h +4 -4
  25. package/simsimd/include/simsimd/spatial.h +1 -1
  26. package/simsimd/include/simsimd/types.h +5 -1
  27. package/simsimd/package.json +1 -1
  28. package/simsimd/rust/benches/cosine.rs +27 -0
  29. package/simsimd/rust/benches/native.rs +27 -0
  30. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  31. package/simsimd/rust/lib.c +68 -0
  32. package/simsimd/rust/lib.rs +232 -0
  33. package/sqlite/README.md +178 -0
  34. package/sqlite/lib.cpp +325 -0
  35. package/stringzilla/CONTRIBUTING.md +429 -0
  36. package/stringzilla/Cargo.lock +25 -0
  37. package/stringzilla/Cargo.toml +26 -0
  38. package/stringzilla/LICENSE +201 -0
  39. package/stringzilla/README.md +1281 -0
  40. package/stringzilla/VERSION +1 -0
  41. package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
  42. package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
  43. package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
  44. package/stringzilla/binding.gyp +16 -0
  45. package/stringzilla/build.rs +14 -0
  46. package/stringzilla/c/lib.c +304 -0
  47. package/stringzilla/cli/README.md +48 -0
  48. package/stringzilla/cli/split.py +72 -0
  49. package/stringzilla/cli/wc.py +37 -0
  50. package/stringzilla/include/stringzilla/experimental.h +724 -0
  51. package/stringzilla/include/stringzilla/module.modulemap +9 -0
  52. package/stringzilla/include/stringzilla/stringzilla.h +5240 -0
  53. package/stringzilla/include/stringzilla/stringzilla.hpp +3830 -0
  54. package/stringzilla/javascript/lib.c +125 -0
  55. package/stringzilla/javascript/stringzilla.js +22 -0
  56. package/stringzilla/package-ci.json +10 -0
  57. package/stringzilla/package.json +32 -0
  58. package/stringzilla/rust/lib.rs +362 -0
  59. package/stringzilla/swift/StringProtocol+StringZilla.swift +280 -0
  60. package/stringzilla/swift/Test.swift +60 -0
  61. package/wasmer.toml +1 -1
  62. package/simsimd/bench.js +0 -51
@@ -0,0 +1,36 @@
1
+ [package]
2
+ name = "simsimd"
3
+ description = "Fastest SIMD-Accelerated Vector Similarity Functions for x86 and Arm"
4
+ version = "3.8.0"
5
+ edition = "2021"
6
+ license = "Apache-2.0"
7
+ authors = [
8
+ "Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>",
9
+ "Pedro Planel <pplanel@gmail.com>",
10
+ ]
11
+ repository = "https://github.com/ashvardanian/SimSIMD"
12
+ documentation = "https://docs.rs/simsimd"
13
+ homepage = "https://ashvardanian.com/posts/simsimd-faster-scipy"
14
+ keywords = ["simd", "search", "linear-algebra", "vector"]
15
+ categories = ["mathematics", "hardware-support", "no-std", "wasm", "external-ffi-bindings"]
16
+
17
+ [lib]
18
+ name = "simsimd"
19
+ path = "rust/lib.rs"
20
+
21
+ [build-dependencies]
22
+ cc = "1.0.83"
23
+
24
+ [[bench]]
25
+ name = "cosine"
26
+ harness = false
27
+ path = "rust/benches/cosine.rs"
28
+
29
+ [[bench]]
30
+ name = "sqeuclidean"
31
+ harness = false
32
+ path = "rust/benches/sqeuclidean.rs"
33
+
34
+ [dev-dependencies]
35
+ criterion = { version = "0.5.1" }
36
+ rand = { version = "0.8.5" }
package/simsimd/README.md CHANGED
@@ -3,12 +3,14 @@
3
3
  <div>
4
4
  <a href="https://pepy.tech/project/simsimd"> <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"> </a>
5
5
  <a href="https://www.npmjs.com/package/simsimd"> <img alt="npm" src="https://img.shields.io/npm/dy/simsimd?label=npm%20dowloads"> </a>
6
+ <a href="https://crates.io/crates/simsimd"> <img alt="rust" src="https://img.shields.io/crates/d/simsimd?logo=rust" /> </a>
6
7
  <img alt="GitHub code size in bytes" src="https://img.shields.io/github/languages/code-size/ashvardanian/simsimd">
7
8
  </div>
8
9
 
9
10
  ## Hardware-Accelerated Similarity Metrics and Distance Functions
10
11
 
11
- - Zero-dependency [header-only C 99](#using-simsimd-in-c) library with bindings for [Python](#using-simsimd-in-python) and [JavaScript](#using-simsimd-in-javascript).
12
+ - 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).
12
14
  - Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
13
15
  - Zero-copy compatible with NumPy, PyTorch, TensorFlow, and other tensors.
14
16
  - Handles `f64` double-, `f32` single-, and `f16` half-precision, `i8` integral, and binary vectors.
@@ -139,6 +141,32 @@ metric = CompiledMetric(
139
141
  index = Index(256, metric=metric)
140
142
  ```
141
143
 
144
+ ## Using SimSIMD in Rust
145
+
146
+ To install, add the following to your `Cargo.toml`:
147
+
148
+ ```toml
149
+ [dependencies]
150
+ simsimd = "..."
151
+ ```
152
+
153
+ To use it:
154
+
155
+ ```rust
156
+ use simsimd::{cosine, sqeuclidean};
157
+
158
+ fn main() {
159
+ let vector_a = vec![1.0, 2.0, 3.0];
160
+ let vector_b = vec![4.0, 5.0, 6.0];
161
+
162
+ let distance = cosine(&vector_a, &vector_b);
163
+ println!("Cosine Distance: {}", distance);
164
+
165
+ let distance = sqeuclidean(&vector_a, &vector_b);
166
+ println!("Squared Euclidean Distance: {}", distance);
167
+ }
168
+ ```
169
+
142
170
  ## Using SimSIMD in JavaScript
143
171
 
144
172
  To install, choose one of the following options depending on your environment:
@@ -237,3 +265,11 @@ cd golang
237
265
  go test # To test
238
266
  go test -run=^$ -bench=. -benchmem # To benchmark
239
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
+ ```
@@ -0,0 +1,20 @@
1
+ fn main() {
2
+ cc::Build::new()
3
+ .file("rust/lib.c")
4
+ .include("include")
5
+ .flag("-O3")
6
+ .flag("-DSIMSIMD_NATIVE_F16=0")
7
+ .flag("-std=c99") // Enforce C99 standard
8
+ .flag("-pedantic") // Ensure strict compliance with the C standard
9
+ .warnings(false)
10
+ .compile("simsimd");
11
+
12
+ println!("cargo:rerun-if-changed=rust/lib.c");
13
+ println!("cargo:rerun-if-changed=rust/lib.rs");
14
+ println!("cargo:rerun-if-changed=include/simsimd/simsimd.h");
15
+
16
+ println!("cargo:rerun-if-changed=include/simsimd/binary.h");
17
+ println!("cargo:rerun-if-changed=include/simsimd/probability.h");
18
+ println!("cargo:rerun-if-changed=include/simsimd/spatial.h");
19
+ println!("cargo:rerun-if-changed=include/simsimd/types.h");
20
+ }
@@ -15,7 +15,7 @@
15
15
  #define SIMSIMD_H
16
16
 
17
17
  #define SIMSIMD_VERSION_MAJOR 3
18
- #define SIMSIMD_VERSION_MINOR 6
18
+ #define SIMSIMD_VERSION_MINOR 8
19
19
  #define SIMSIMD_VERSION_PATCH 0
20
20
 
21
21
  #include "binary.h" // Hamming, Jaccard
@@ -69,7 +69,7 @@ typedef enum {
69
69
  */
70
70
  typedef enum {
71
71
  simsimd_cap_serial_k = 1, ///< Serial (non-SIMD) capability
72
- simsimd_cap_any_k = 0xFFFFFFFF, ///< Mask representing any capability
72
+ simsimd_cap_any_k = 0x7FFFFFFF, ///< Mask representing any capability with `INT_MAX`
73
73
 
74
74
  simsimd_cap_arm_neon_k = 1 << 10, ///< ARM NEON capability
75
75
  simsimd_cap_arm_sve_k = 1 << 11, ///< ARM SVE capability
@@ -112,11 +112,11 @@ typedef simsimd_f32_t (*simsimd_metric_punned_t)(void const* a, void const* b, s
112
112
  * @brief Function to determine the SIMD capabilities of the current machine at @b runtime.
113
113
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
114
114
  */
115
- inline static simsimd_capability_t simsimd_capabilities() {
115
+ inline static simsimd_capability_t simsimd_capabilities(void) {
116
116
 
117
117
  #if SIMSIMD_TARGET_X86
118
118
 
119
- /// The states of 4 registers populated for a specific "cpuid" assmebly call
119
+ /// The states of 4 registers populated for a specific "cpuid" assembly call
120
120
  union four_registers_t {
121
121
  int array[4];
122
122
  struct separate_t {
@@ -1172,4 +1172,4 @@ simsimd_avx512_f64_cos_cycle:
1172
1172
  }
1173
1173
  #endif
1174
1174
 
1175
- #endif
1175
+ #endif
@@ -102,7 +102,7 @@ typedef signed char simsimd_i8_t;
102
102
  typedef unsigned char simsimd_b8_t;
103
103
  typedef unsigned long long simsimd_size_t;
104
104
 
105
- #if !defined(SIMSIMD_NATIVE_F16)
105
+ #if !defined(SIMSIMD_NATIVE_F16) || SIMSIMD_NATIVE_F16
106
106
  /**
107
107
  * @brief Half-precision floating-point type.
108
108
  *
@@ -112,12 +112,16 @@ typedef unsigned long long simsimd_size_t;
112
112
  */
113
113
  #if (defined(__GNUC__) || defined(__clang__)) && (defined(__ARM_ARCH) || defined(__aarch64__)) && \
114
114
  (defined(__ARM_FP16_FORMAT_IEEE))
115
+ #if !defined(SIMSIMD_NATIVE_F16)
115
116
  #define SIMSIMD_NATIVE_F16 1
117
+ #endif
116
118
  typedef __fp16 simsimd_f16_t;
117
119
  #elif ((defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__)) && \
118
120
  (defined(__SSE2__) || defined(__AVX512F__)))
119
121
  typedef _Float16 simsimd_f16_t;
122
+ #if !defined(SIMSIMD_NATIVE_F16)
120
123
  #define SIMSIMD_NATIVE_F16 1
124
+ #endif
121
125
  #else // Unknown compiler or architecture
122
126
  #define SIMSIMD_NATIVE_F16 0
123
127
  #endif // Unknown compiler or architecture
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simsimd",
3
- "version": "3.6.7",
3
+ "version": "3.8.0",
4
4
  "description": "Fastest SIMD-Accelerated Vector Similarity Functions for x86 and Arm",
5
5
  "homepage": "https://github.com/ashvardanian/simsimd",
6
6
  "author": "Ash Vardanian",
@@ -0,0 +1,27 @@
1
+ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
2
+ use simsimd::SimSIMD;
3
+
4
+ mod native;
5
+
6
+ const DIMENSIONS: usize = 1536;
7
+
8
+ pub fn cosine_benchmark(c: &mut Criterion) {
9
+ let inputs: (Vec<f32>, Vec<f32>) = (
10
+ native::generate_random_vector(DIMENSIONS),
11
+ native::generate_random_vector(DIMENSIONS),
12
+ );
13
+
14
+ let mut group = c.benchmark_group("SIMD Cosine");
15
+
16
+ for i in 0..=5 {
17
+ group.bench_with_input(BenchmarkId::new("SimSIMD", i), &i, |b, _| {
18
+ b.iter(|| SimSIMD::cosine(&inputs.0, &inputs.1))
19
+ });
20
+ group.bench_with_input(BenchmarkId::new("Rust Native", i), &i, |b, _| {
21
+ b.iter(|| native::cosine_similarity_cpu(&inputs.0, &inputs.1))
22
+ });
23
+ }
24
+ }
25
+
26
+ criterion_group!(benches, cosine_benchmark);
27
+ criterion_main!(benches);
@@ -0,0 +1,27 @@
1
+ #![allow(unused)]
2
+ use rand::Rng;
3
+
4
+ pub(crate) fn generate_random_vector(dim: usize) -> Vec<f32> {
5
+ (0..dim).map(|_| rand::thread_rng().gen()).collect()
6
+ }
7
+
8
+ pub(crate) fn cosine_similarity_cpu(a: &[f32], b: &[f32]) -> Option<f32> {
9
+ if a.len() != b.len() {
10
+ return None;
11
+ }
12
+
13
+ let dot_product: f32 = a.iter().zip(b.iter()).map(|(a, b)| a * b).sum();
14
+ let norm_a: f32 = a.iter().map(|a| a.powf(2.0)).sum();
15
+ let norm_b: f32 = b.iter().map(|b| b.powf(2.0)).sum();
16
+
17
+ Some(1.0 - (dot_product / (norm_a.sqrt() * norm_b.sqrt())))
18
+ }
19
+
20
+ pub(crate) fn squared_euclidean_cpu(a: &[f32], b: &[f32]) -> f32 {
21
+ assert_eq!(a.len(), b.len());
22
+
23
+ a.iter()
24
+ .zip(b.iter())
25
+ .map(|(x, y)| ((*x) - (*y)) * ((*x) - (*y)))
26
+ .fold(0.0, ::std::ops::Add::add)
27
+ }
@@ -0,0 +1,27 @@
1
+ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
2
+ use simsimd::SimSIMD;
3
+
4
+ mod native;
5
+
6
+ const DIMENSIONS: usize = 1536;
7
+
8
+ pub fn sqeuclidean_benchmark(c: &mut Criterion) {
9
+ let inputs: (Vec<f32>, Vec<f32>) = (
10
+ native::generate_random_vector(DIMENSIONS),
11
+ native::generate_random_vector(DIMENSIONS),
12
+ );
13
+
14
+ let mut group = c.benchmark_group("SIMD SqEuclidean");
15
+
16
+ for i in 0..=5 {
17
+ group.bench_with_input(BenchmarkId::new("SimSIMD", i), &i, |b, _| {
18
+ b.iter(|| SimSIMD::sqeuclidean(&inputs.0, &inputs.1))
19
+ });
20
+ group.bench_with_input(BenchmarkId::new("Rust Native", i), &i, |b, _| {
21
+ b.iter(|| native::squared_euclidean_cpu(&inputs.0, &inputs.1))
22
+ });
23
+ }
24
+ }
25
+
26
+ criterion_group!(benches, sqeuclidean_benchmark);
27
+ criterion_main!(benches);
@@ -0,0 +1,68 @@
1
+ #include <simsimd/simsimd.h>
2
+
3
+ simsimd_capability_t cached_capabilities(void) {
4
+ static simsimd_capability_t static_capabilities = 0;
5
+ if (static_capabilities == 0)
6
+ static_capabilities = simsimd_capabilities();
7
+ return static_capabilities;
8
+ }
9
+
10
+ simsimd_f32_t cosine_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
11
+ static simsimd_metric_punned_t metric = 0;
12
+ if (metric == 0) {
13
+ simsimd_capability_t used_capability;
14
+ simsimd_find_metric_punned(simsimd_metric_cosine_k, simsimd_datatype_i8_k, cached_capabilities(),
15
+ simsimd_cap_any_k, &metric, &used_capability);
16
+ }
17
+ return metric(a, b, d, d);
18
+ }
19
+
20
+ simsimd_f32_t cosine_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
21
+ static simsimd_metric_punned_t metric = 0;
22
+ if (metric == 0) {
23
+ simsimd_capability_t used_capability;
24
+ simsimd_find_metric_punned(simsimd_metric_cosine_k, simsimd_datatype_f32_k, cached_capabilities(),
25
+ simsimd_cap_any_k, &metric, &used_capability);
26
+ }
27
+ return metric(a, b, d, d);
28
+ }
29
+
30
+ simsimd_f32_t inner_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
31
+ static simsimd_metric_punned_t metric = 0;
32
+ if (metric == 0) {
33
+ simsimd_capability_t used_capability;
34
+ simsimd_find_metric_punned(simsimd_metric_inner_k, simsimd_datatype_i8_k, cached_capabilities(),
35
+ simsimd_cap_any_k, &metric, &used_capability);
36
+ }
37
+ return metric(a, b, d, d);
38
+ }
39
+
40
+ simsimd_f32_t inner_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
41
+ static simsimd_metric_punned_t metric = 0;
42
+ if (metric == 0) {
43
+ simsimd_capability_t used_capability;
44
+ simsimd_find_metric_punned(simsimd_metric_inner_k, simsimd_datatype_f32_k, cached_capabilities(),
45
+ simsimd_cap_any_k, &metric, &used_capability);
46
+ }
47
+ return metric(a, b, d, d);
48
+ }
49
+
50
+ simsimd_f32_t sqeuclidean_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t d) {
51
+ static simsimd_metric_punned_t metric = 0;
52
+ if (metric == 0) {
53
+ simsimd_capability_t used_capability;
54
+ simsimd_find_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_i8_k, cached_capabilities(),
55
+ simsimd_cap_any_k, &metric, &used_capability);
56
+ }
57
+ return metric(a, b, d, d);
58
+ }
59
+
60
+ simsimd_f32_t sqeuclidean_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t d) {
61
+ static simsimd_metric_punned_t metric = 0;
62
+ if (metric == 0) {
63
+ simsimd_capability_t used_capability;
64
+ simsimd_find_metric_punned(simsimd_metric_sqeuclidean_k, simsimd_datatype_f32_k, cached_capabilities(),
65
+ simsimd_cap_any_k, &metric, &used_capability);
66
+ }
67
+ return metric(a, b, d, d);
68
+ }
@@ -0,0 +1,232 @@
1
+ //! # SimSIMD - Hardware-Accelerated Similarity Metrics and Distance Functions
2
+ //!
3
+ //! * Zero-dependency header-only C 99 library with bindings for Python, JavaScript and Rust.
4
+ //! * Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
5
+ //! * Zero-copy compatible with NumPy, PyTorch, TensorFlow, and other tensors.
6
+ //! * Handles f64 double-, f32 single-, and f16 half-precision, i8 integral, and binary vectors.
7
+ //! * Up to 200x faster than scipy.spatial.distance and numpy.inner.
8
+ //! * Used in USearch and several DBMS products.
9
+ //!
10
+ //! ## Implemented distance functions include:
11
+ //!
12
+ //! * Euclidean (L2), Inner Distance, and Cosine (Angular) spatial distances.
13
+ //! * Hamming (~ Manhattan) and Jaccard (~ Tanimoto) binary distances.
14
+ //! * Kullback-Leibler and Jensen–Shannon divergences for probability distributions.
15
+ //!
16
+ //! The functions in this module are exposed through a trait `SimSIMD`, which is
17
+ //! implemented for types with SIMD support (e.g., i8 and f32). The trait provides
18
+ //! methods for computing cosine similarity, inner product, and squared Euclidean
19
+ //! distance between slices of these types.
20
+ //!
21
+ //! # Example
22
+ //!
23
+ //! ```rust
24
+ //! use simsimd::SimSIMD;
25
+ //!
26
+ //! let a = &[1, 2, 3];
27
+ //! let b = &[4, 5, 6];
28
+ //!
29
+ //! // Compute cosine similarity
30
+ //! let cosine_sim = i8::cosine(a, b);
31
+ //!
32
+ //! // Compute inner product
33
+ //! let inner_product = i8::inner(a, b);
34
+ //!
35
+ //! // Compute squared Euclidean distance
36
+ //! let sqeuclidean_dist = i8::sqeuclidean(a, b);
37
+ //! ```
38
+ //!
39
+ //! # Safety
40
+ //!
41
+ //! The functions declared in the `extern "C"` block are low-level bindings to
42
+ //! SimSIMD implementation. It is crucial to ensure that the input slices have the
43
+ //! same length (`c` parameter) to avoid undefined behavior.
44
+ //!
45
+ //!
46
+ //! # Trait Implementation
47
+ //!
48
+ //! The `SimSIMD` trait is implemented for types with SIMD support and provides
49
+ //! three associated methods:
50
+ //!
51
+ //! - `cosine(a: &[Self], b: &[Self]) -> Option<f32>`: Computes cosine similarity between two slices.
52
+ //! - `inner(a: &[Self], b: &[Self]) -> Option<f32>`: Computes inner product between two slices.
53
+ //! - `sqeuclidean(a: &[Self], b: &[Self]) -> Option<f32>`: Computes squared Euclidean distance between two slices.
54
+ //!
55
+ #![allow(non_camel_case_types)]
56
+
57
+ extern "C" {
58
+ /// Computes cosine similarity for i8 types.
59
+ fn cosine_i8(a: *const i8, b: *const i8, c: usize) -> f32;
60
+
61
+ /// Computes cosine similarity for f32 types.
62
+ fn cosine_f32(a: *const f32, b: *const f32, c: usize) -> f32;
63
+
64
+ /// Computes inner product for i8 types.
65
+ fn inner_i8(a: *const i8, b: *const i8, c: usize) -> f32;
66
+
67
+ /// Computes inner product for f32 types.
68
+ fn inner_f32(a: *const f32, b: *const f32, c: usize) -> f32;
69
+
70
+ /// Computes squared Euclidean distance for i8 types.
71
+ fn sqeuclidean_i8(a: *const i8, b: *const i8, c: usize) -> f32;
72
+
73
+ /// Computes squared Euclidean distance for f32 types.
74
+ fn sqeuclidean_f32(a: *const f32, b: *const f32, c: usize) -> f32;
75
+ }
76
+
77
+ /// A trait for SIMD similarity functions.
78
+ pub trait SimSIMD
79
+ where
80
+ Self: Sized,
81
+ {
82
+ /// Computes cosine similarity between two slices.
83
+ fn cosine(a: &[Self], b: &[Self]) -> Option<f32>;
84
+
85
+ /// Computes inner product between two slices.
86
+ fn inner(a: &[Self], b: &[Self]) -> Option<f32>;
87
+
88
+ /// Computes squared Euclidean distance between two slices.
89
+ fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<f32>;
90
+ }
91
+
92
+ impl SimSIMD for i8 {
93
+ fn cosine(a: &[Self], b: &[Self]) -> Option<f32> {
94
+ if a.len() != b.len() {
95
+ return None;
96
+ }
97
+
98
+ let operation = unsafe { cosine_i8(a.as_ptr(), b.as_ptr(), a.len()) };
99
+
100
+ Some(operation)
101
+ }
102
+
103
+ fn inner(a: &[Self], b: &[Self]) -> Option<f32> {
104
+ if a.len() != b.len() {
105
+ return None;
106
+ }
107
+
108
+ let operation = unsafe { inner_i8(a.as_ptr(), b.as_ptr(), a.len()) };
109
+
110
+ Some(operation)
111
+ }
112
+
113
+ fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<f32> {
114
+ if a.len() != b.len() {
115
+ return None;
116
+ }
117
+
118
+ let operation = unsafe { sqeuclidean_i8(a.as_ptr(), b.as_ptr(), a.len()) };
119
+
120
+ Some(operation)
121
+ }
122
+ }
123
+
124
+ impl SimSIMD for f32 {
125
+ fn cosine(a: &[Self], b: &[Self]) -> Option<f32> {
126
+ if a.len() != b.len() {
127
+ return None;
128
+ }
129
+
130
+ let operation = unsafe { cosine_f32(a.as_ptr(), b.as_ptr(), a.len()) };
131
+
132
+ Some(operation)
133
+ }
134
+
135
+ fn inner(a: &[Self], b: &[Self]) -> Option<f32> {
136
+ if a.len() != b.len() {
137
+ return None;
138
+ }
139
+
140
+ let operation = unsafe { inner_f32(a.as_ptr(), b.as_ptr(), a.len()) };
141
+
142
+ Some(operation)
143
+ }
144
+
145
+ fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<f32> {
146
+ if a.len() != b.len() {
147
+ return None;
148
+ }
149
+
150
+ let operation = unsafe { sqeuclidean_f32(a.as_ptr(), b.as_ptr(), a.len()) };
151
+
152
+ Some(operation)
153
+ }
154
+ }
155
+
156
+ #[cfg(test)]
157
+ mod tests {
158
+ use super::*;
159
+ //
160
+ fn assert_almost_equal(left: f32, right: f32, tolerance: f32) {
161
+ let lower = right - tolerance;
162
+ let upper = right + tolerance;
163
+
164
+ assert!(left >= lower && left <= upper);
165
+ }
166
+
167
+ #[test]
168
+ fn test_cosine_i8() {
169
+ let a = &[3, 97, 127];
170
+ let b = &[3, 97, 127];
171
+
172
+ if let Some(result) = SimSIMD::cosine(a, b) {
173
+ assert_almost_equal(0.00012027938, result, 0.01);
174
+ println!("The result of cosine_i8 is {:.8}", result);
175
+ }
176
+ }
177
+
178
+ #[test]
179
+ fn test_cosine_f32() {
180
+ let a = &[1.0, 2.0, 3.0];
181
+ let b = &[1.0, 2.0, 3.0];
182
+
183
+ if let Some(result) = SimSIMD::cosine(a, b) {
184
+ assert_almost_equal(0.004930496, result, 0.01);
185
+ println!("The result of cosine_f32 is {:.8}", result);
186
+ }
187
+ }
188
+
189
+ #[test]
190
+ fn test_inner_i8() {
191
+ let a = &[1, 2, 3];
192
+ let b = &[4, 5, 6];
193
+
194
+ if let Some(result) = SimSIMD::inner(a, b) {
195
+ assert_almost_equal(0.029403687, result, 0.01);
196
+ println!("The result of inner_i8 is {:.8}", result);
197
+ }
198
+ }
199
+
200
+ #[test]
201
+ fn test_inner_f32() {
202
+ let a = &[1.0, 2.0, 3.0];
203
+ let b = &[4.0, 5.0, 6.0];
204
+
205
+ if let Some(result) = SimSIMD::inner(a, b) {
206
+ assert_almost_equal(-31.0, result, 0.01);
207
+ println!("The result of inner_f32 is {:.8}", result);
208
+ }
209
+ }
210
+
211
+ #[test]
212
+ fn test_sqeuclidean_i8() {
213
+ let a = &[1, 2, 3];
214
+ let b = &[4, 5, 6];
215
+
216
+ if let Some(result) = SimSIMD::sqeuclidean(a, b) {
217
+ assert_almost_equal(27.0, result, 0.01);
218
+ println!("The result of sqeuclidean_i8 is {:.8}", result);
219
+ }
220
+ }
221
+
222
+ #[test]
223
+ fn test_sqeuclidean_f32() {
224
+ let a = &[1.0, 2.0, 3.0];
225
+ let b = &[4.0, 5.0, 6.0];
226
+
227
+ if let Some(result) = SimSIMD::sqeuclidean(a, b) {
228
+ assert_almost_equal(27.0, result, 0.01);
229
+ println!("The result of sqeuclidean_f32 is {:.8}", result);
230
+ }
231
+ }
232
+ }