usearch 2.10.3 → 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.
- package/README.md +1 -1
- package/binding.gyp +101 -0
- package/c/CMakeLists.txt +27 -0
- package/c/README.md +34 -0
- package/c/lib.cpp +362 -0
- package/c/test.c +382 -0
- package/c/usearch.h +356 -0
- package/cmake/clipp.cmake +7 -0
- package/cmake/config.cmake.in +15 -0
- package/cmake/emscripten.cmake +8 -0
- package/cmake/jemalloc.cmake +62 -0
- package/cmake/pkg-config.pc.in +4 -0
- package/cmake/usearchConfigVersion.cmake.in +20 -0
- package/cpp/CMakeLists.txt +14 -0
- package/cpp/README.md +161 -0
- package/cpp/bench.cpp +616 -0
- package/cpp/test.cpp +324 -0
- package/fp16/CMakeLists.txt +176 -0
- package/fp16/LICENSE +11 -0
- package/fp16/README.md +20 -0
- package/fp16/bench/alt-element.cc +89 -0
- package/fp16/bench/from-alt-array.cc +175 -0
- package/fp16/bench/from-ieee-array.cc +399 -0
- package/fp16/bench/ieee-element.cc +272 -0
- package/fp16/bench/to-alt-array.cc +91 -0
- package/fp16/bench/to-ieee-array.cc +303 -0
- package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
- package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
- package/fp16/cmake/DownloadPSimd.cmake +15 -0
- package/fp16/configure.py +65 -0
- package/fp16/confu.yaml +8 -0
- package/fp16/include/fp16/__init__.py +0 -0
- package/fp16/include/fp16/avx.py +50 -0
- package/fp16/include/fp16/avx2.py +53 -0
- package/fp16/include/fp16/bitcasts.h +92 -0
- package/fp16/include/fp16/fp16.h +451 -0
- package/fp16/include/fp16/psimd.h +131 -0
- package/fp16/include/fp16.h +11 -0
- package/fp16/jni/Android.mk +6 -0
- package/fp16/jni/Application.mk +4 -0
- package/fp16/test/alt-from-fp32-value.cc +498 -0
- package/fp16/test/alt-to-fp32-bits.cc +263 -0
- package/fp16/test/alt-to-fp32-psimd.cc +145 -0
- package/fp16/test/alt-to-fp32-value.cc +348 -0
- package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
- package/fp16/test/bitcasts.cc +56 -0
- package/fp16/test/ieee-from-fp32-value.cc +523 -0
- package/fp16/test/ieee-to-fp32-bits.cc +333 -0
- package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
- package/fp16/test/ieee-to-fp32-value.cc +420 -0
- package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
- package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
- package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
- package/fp16/test/peachpy/stubs.py +38 -0
- package/fp16/test/tables.cc +530 -0
- package/fp16/test/tables.h +13 -0
- package/fp16/third-party/THHalf.h +92 -0
- package/fp16/third-party/eigen-half.h +170 -0
- package/fp16/third-party/float16-compressor.h +80 -0
- package/fp16/third-party/half.hpp +2916 -0
- package/fp16/third-party/npy-halffloat.h +328 -0
- package/include/usearch/index.hpp +3838 -0
- package/include/usearch/index_dense.hpp +2004 -0
- package/include/usearch/index_plugins.hpp +2255 -0
- package/javascript/dist-package-cjs.json +3 -0
- package/javascript/dist-package-esm.json +3 -0
- package/javascript/lib.cpp +350 -0
- package/javascript/node-gyp-build.d.ts +1 -0
- package/javascript/tsconfig-base.json +25 -0
- package/javascript/tsconfig-cjs.json +8 -0
- package/javascript/tsconfig-esm.json +8 -0
- package/javascript/usearch.test.js +84 -0
- package/javascript/usearch.ts +640 -0
- package/package.json +10 -3
- 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/prebuilds/win32-ia32/usearch.node +0 -0
- package/prebuilds/win32-x64/usearch.node +0 -0
- package/simsimd/.releaserc +87 -0
- package/simsimd/CONTRIBUTING.md +114 -0
- package/simsimd/Cargo.lock +709 -0
- package/simsimd/Cargo.toml +44 -0
- package/simsimd/LICENSE +201 -0
- package/simsimd/README.md +708 -0
- package/simsimd/binding.gyp +36 -0
- package/simsimd/build.rs +51 -0
- package/simsimd/c/lib.c +110 -0
- package/simsimd/include/simsimd/binary.h +258 -0
- package/simsimd/include/simsimd/dot.h +1237 -0
- package/simsimd/include/simsimd/geospatial.h +37 -0
- package/simsimd/include/simsimd/probability.h +563 -0
- package/simsimd/include/simsimd/simsimd.h +1129 -0
- package/simsimd/include/simsimd/spatial.h +1019 -0
- package/simsimd/include/simsimd/types.h +309 -0
- package/simsimd/javascript/bench.js +127 -0
- package/simsimd/javascript/dist-package-cjs.json +3 -0
- package/simsimd/javascript/dist-package-esm.json +3 -0
- package/simsimd/javascript/fallback.ts +203 -0
- package/simsimd/javascript/lib.c +107 -0
- package/simsimd/javascript/node-gyp-build.d.ts +1 -0
- package/simsimd/javascript/simsimd.ts +135 -0
- package/simsimd/javascript/test.mjs +179 -0
- package/simsimd/javascript/tsconfig-base.json +37 -0
- package/simsimd/javascript/tsconfig-cjs.json +8 -0
- package/simsimd/javascript/tsconfig-esm.json +8 -0
- package/simsimd/package-lock.json +1709 -0
- package/simsimd/package.json +61 -0
- package/simsimd/rust/benches/cosine.rs +27 -0
- package/simsimd/rust/benches/native.rs +30 -0
- package/simsimd/rust/benches/sqeuclidean.rs +27 -0
- package/simsimd/rust/lib.rs +778 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
//! # SpatialSimilarity - Hardware-Accelerated Similarity Metrics and Distance Functions
|
|
2
|
+
//!
|
|
3
|
+
//! * Targets ARM NEON, SVE, x86 AVX2, AVX-512 (VNNI, FP16) hardware backends.
|
|
4
|
+
//! * Handles `f64` double-, `f32` single-, and `f16` half-precision, `i8` integral, and binary vectors.
|
|
5
|
+
//! * Zero-dependency header-only C 99 library with bindings for Rust and other langauges.
|
|
6
|
+
//!
|
|
7
|
+
//! ## Implemented distance functions include:
|
|
8
|
+
//!
|
|
9
|
+
//! * Euclidean (L2), Inner Distance, and Cosine (Angular) spatial distances.
|
|
10
|
+
//! * Hamming (~ Manhattan) and Jaccard (~ Tanimoto) binary distances.
|
|
11
|
+
//! * Kullback-Leibler and Jensen-Shannon divergences for probability distributions.
|
|
12
|
+
//!
|
|
13
|
+
//! ## Example
|
|
14
|
+
//!
|
|
15
|
+
//! ```rust
|
|
16
|
+
//! use simsimd::SpatialSimilarity;
|
|
17
|
+
//!
|
|
18
|
+
//! let a = &[1, 2, 3];
|
|
19
|
+
//! let b = &[4, 5, 6];
|
|
20
|
+
//!
|
|
21
|
+
//! // Compute cosine similarity
|
|
22
|
+
//! let cos_sim = i8::cos(a, b);
|
|
23
|
+
//!
|
|
24
|
+
//! // Compute dot product distance
|
|
25
|
+
//! let dot_product = i8::dot(a, b);
|
|
26
|
+
//!
|
|
27
|
+
//! // Compute squared Euclidean distance
|
|
28
|
+
//! let l2sq_dist = i8::l2sq(a, b);
|
|
29
|
+
//! ```
|
|
30
|
+
//!
|
|
31
|
+
//! ## Traits
|
|
32
|
+
//!
|
|
33
|
+
//! The `SpatialSimilarity` trait covers following methods:
|
|
34
|
+
//!
|
|
35
|
+
//! - `cosine(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes cosine similarity between two slices.
|
|
36
|
+
//! - `dot(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes dot product distance between two slices.
|
|
37
|
+
//! - `sqeuclidean(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes squared Euclidean distance between two slices.
|
|
38
|
+
//!
|
|
39
|
+
//! The `BinarySimilarity` trait covers following methods:
|
|
40
|
+
//!
|
|
41
|
+
//! - `hamming(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes Hamming distance between two slices.
|
|
42
|
+
//! - `jaccard(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes Jaccard index between two slices.
|
|
43
|
+
//!
|
|
44
|
+
//! The `ProbabilitySimilarity` trait covers following methods:
|
|
45
|
+
//!
|
|
46
|
+
//! - `jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes Jensen-Shannon divergence between two slices.
|
|
47
|
+
//! - `kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance>`: Computes Kullback-Leibler divergence between two slices.
|
|
48
|
+
//!
|
|
49
|
+
#![allow(non_camel_case_types)]
|
|
50
|
+
|
|
51
|
+
type Distance = f64;
|
|
52
|
+
type ComplexProduct = (f64, f64);
|
|
53
|
+
|
|
54
|
+
extern "C" {
|
|
55
|
+
|
|
56
|
+
fn simsimd_dot_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
57
|
+
fn simsimd_dot_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
58
|
+
fn simsimd_dot_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
59
|
+
|
|
60
|
+
fn simsimd_dot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
61
|
+
fn simsimd_dot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
62
|
+
fn simsimd_dot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
63
|
+
|
|
64
|
+
fn simsimd_vdot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
65
|
+
fn simsimd_vdot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
66
|
+
fn simsimd_vdot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
67
|
+
|
|
68
|
+
fn simsimd_cos_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
|
|
69
|
+
fn simsimd_cos_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
70
|
+
fn simsimd_cos_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
71
|
+
fn simsimd_cos_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
72
|
+
|
|
73
|
+
fn simsimd_l2sq_i8(a: *const i8, b: *const i8, c: usize, d: *mut Distance);
|
|
74
|
+
fn simsimd_l2sq_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
75
|
+
fn simsimd_l2sq_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
76
|
+
fn simsimd_l2sq_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
77
|
+
|
|
78
|
+
fn simsimd_hamming_b8(a: *const u8, b: *const u8, c: usize, d: *mut Distance);
|
|
79
|
+
fn simsimd_jaccard_b8(a: *const u8, b: *const u8, c: usize, d: *mut Distance);
|
|
80
|
+
|
|
81
|
+
fn simsimd_js_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
82
|
+
fn simsimd_js_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
83
|
+
fn simsimd_js_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
84
|
+
|
|
85
|
+
fn simsimd_kl_f16(a: *const u16, b: *const u16, c: usize, d: *mut Distance);
|
|
86
|
+
fn simsimd_kl_f32(a: *const f32, b: *const f32, c: usize, d: *mut Distance);
|
|
87
|
+
fn simsimd_kl_f64(a: *const f64, b: *const f64, c: usize, d: *mut Distance);
|
|
88
|
+
|
|
89
|
+
fn simsimd_uses_neon() -> i32;
|
|
90
|
+
fn simsimd_uses_sve() -> i32;
|
|
91
|
+
fn simsimd_uses_haswell() -> i32;
|
|
92
|
+
fn simsimd_uses_skylake() -> i32;
|
|
93
|
+
fn simsimd_uses_ice() -> i32;
|
|
94
|
+
fn simsimd_uses_sapphire() -> i32;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// A half-precision floating point number.
|
|
98
|
+
#[repr(transparent)]
|
|
99
|
+
pub struct f16(u16);
|
|
100
|
+
|
|
101
|
+
impl f16 {}
|
|
102
|
+
|
|
103
|
+
/// The `capabilties` module provides functions for detecting the hardware features
|
|
104
|
+
/// available on the current system.
|
|
105
|
+
pub mod capabilties {
|
|
106
|
+
|
|
107
|
+
pub fn uses_neon() -> bool {
|
|
108
|
+
unsafe { crate::simsimd_uses_neon() != 0 }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub fn uses_sve() -> bool {
|
|
112
|
+
unsafe { crate::simsimd_uses_sve() != 0 }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pub fn uses_haswell() -> bool {
|
|
116
|
+
unsafe { crate::simsimd_uses_haswell() != 0 }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pub fn uses_skylake() -> bool {
|
|
120
|
+
unsafe { crate::simsimd_uses_skylake() != 0 }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub fn uses_ice() -> bool {
|
|
124
|
+
unsafe { crate::simsimd_uses_ice() != 0 }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
pub fn uses_sapphire() -> bool {
|
|
128
|
+
unsafe { crate::simsimd_uses_sapphire() != 0 }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// `SpatialSimilarity` provides a set of trait methods for computing similarity
|
|
133
|
+
/// or distance between spatial data vectors in SIMD (Single Instruction, Multiple Data) context.
|
|
134
|
+
/// These methods can be used to calculate metrics like cosine similarity, dot product,
|
|
135
|
+
/// and squared Euclidean distance between two slices of data.
|
|
136
|
+
///
|
|
137
|
+
/// Each method takes two slices of data (a and b) and returns an Option<Distance>.
|
|
138
|
+
/// The result is `None` if the slices are not of the same length, as these operations
|
|
139
|
+
/// require one-to-one correspondence between the elements of the slices.
|
|
140
|
+
/// Otherwise, it returns the computed similarity or distance as `Some(f32)`.
|
|
141
|
+
pub trait SpatialSimilarity
|
|
142
|
+
where
|
|
143
|
+
Self: Sized,
|
|
144
|
+
{
|
|
145
|
+
/// Computes the cosine similarity between two slices.
|
|
146
|
+
/// The cosine similarity is a measure of similarity between two non-zero vectors
|
|
147
|
+
/// of an dot product space that measures the cosine of the angle between them.
|
|
148
|
+
fn cos(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
149
|
+
|
|
150
|
+
/// Computes the inner product (also known as dot product) between two slices.
|
|
151
|
+
/// The dot product is the sum of the products of the corresponding entries
|
|
152
|
+
/// of the two sequences of numbers.
|
|
153
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
154
|
+
|
|
155
|
+
/// Computes the squared Euclidean distance between two slices.
|
|
156
|
+
/// The squared Euclidean distance is the sum of the squared differences
|
|
157
|
+
/// between corresponding elements of the two slices.
|
|
158
|
+
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
159
|
+
|
|
160
|
+
/// Computes the squared Euclidean distance between two slices.
|
|
161
|
+
/// The squared Euclidean distance is the sum of the squared differences
|
|
162
|
+
/// between corresponding elements of the two slices.
|
|
163
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
164
|
+
SpatialSimilarity::l2sq(a, b)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Computes the squared Euclidean distance between two slices.
|
|
168
|
+
/// The squared Euclidean distance is the sum of the squared differences
|
|
169
|
+
/// between corresponding elements of the two slices.
|
|
170
|
+
fn inner(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
171
|
+
SpatialSimilarity::dot(a, b)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Computes the cosine similarity between two slices.
|
|
175
|
+
/// The cosine similarity is a measure of similarity between two non-zero vectors
|
|
176
|
+
/// of an dot product space that measures the cosine of the angle between them.
|
|
177
|
+
fn cosine(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
178
|
+
SpatialSimilarity::cos(a, b)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// `BinarySimilarity` provides trait methods for computing similarity metrics
|
|
183
|
+
/// that are commonly used with binary data vectors, such as Hamming distance
|
|
184
|
+
/// and Jaccard index.
|
|
185
|
+
///
|
|
186
|
+
/// The methods accept two slices of binary data and return an Option<Distance>
|
|
187
|
+
/// indicating the computed similarity or distance, with `None` returned if the
|
|
188
|
+
/// slices differ in length.
|
|
189
|
+
pub trait BinarySimilarity
|
|
190
|
+
where
|
|
191
|
+
Self: Sized,
|
|
192
|
+
{
|
|
193
|
+
/// Computes the Hamming distance between two binary data slices.
|
|
194
|
+
/// The Hamming distance between two strings of equal length is the number of
|
|
195
|
+
/// bits at which the corresponding values are different.
|
|
196
|
+
fn hamming(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
197
|
+
|
|
198
|
+
/// Computes the Jaccard index between two bitsets represented by binary data slices.
|
|
199
|
+
/// The Jaccard index, also known as the Jaccard similarity coefficient, is a statistic
|
|
200
|
+
/// used for gauging the similarity and diversity of sample sets.
|
|
201
|
+
fn jaccard(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// `ProbabilitySimilarity` provides trait methods for computing similarity or divergence
|
|
205
|
+
/// measures between probability distributions, such as the Jensen-Shannon divergence
|
|
206
|
+
/// and the Kullback-Leibler divergence.
|
|
207
|
+
///
|
|
208
|
+
/// These methods are particularly useful in contexts such as information theory and
|
|
209
|
+
/// machine learning, where one often needs to measure how one probability distribution
|
|
210
|
+
/// differs from a second, reference probability distribution.
|
|
211
|
+
pub trait ProbabilitySimilarity
|
|
212
|
+
where
|
|
213
|
+
Self: Sized,
|
|
214
|
+
{
|
|
215
|
+
/// Computes the Jensen-Shannon divergence between two probability distributions.
|
|
216
|
+
/// The Jensen-Shannon divergence is a method of measuring the similarity between
|
|
217
|
+
/// two probability distributions. It is based on the Kullback-Leibler divergence,
|
|
218
|
+
/// but is symmetric and always has a finite value.
|
|
219
|
+
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
220
|
+
|
|
221
|
+
/// Computes the Kullback-Leibler divergence between two probability distributions.
|
|
222
|
+
/// The Kullback-Leibler divergence is a measure of how one probability distribution
|
|
223
|
+
/// diverges from a second, expected probability distribution.
|
|
224
|
+
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// `ComplexProducts` provides trait methods for computing products between
|
|
228
|
+
/// complex number vectors. This includes standard and Hermitian dot products.
|
|
229
|
+
pub trait ComplexProducts
|
|
230
|
+
where
|
|
231
|
+
Self: Sized,
|
|
232
|
+
{
|
|
233
|
+
/// Computes the dot product between two complex number vectors.
|
|
234
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct>;
|
|
235
|
+
|
|
236
|
+
/// Computes the Hermitian dot product (conjugate dot product) between two complex number vectors.
|
|
237
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
impl BinarySimilarity for u8 {
|
|
241
|
+
fn hamming(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
242
|
+
if a.len() != b.len() {
|
|
243
|
+
return None;
|
|
244
|
+
}
|
|
245
|
+
let mut distance_value: Distance = 0.0;
|
|
246
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
247
|
+
unsafe { simsimd_hamming_b8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
248
|
+
Some(distance_value)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn jaccard(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
252
|
+
if a.len() != b.len() {
|
|
253
|
+
return None;
|
|
254
|
+
}
|
|
255
|
+
let mut distance_value: Distance = 0.0;
|
|
256
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
257
|
+
unsafe { simsimd_jaccard_b8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
258
|
+
Some(distance_value)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
impl SpatialSimilarity for i8 {
|
|
263
|
+
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
264
|
+
if a.len() != b.len() {
|
|
265
|
+
return None;
|
|
266
|
+
}
|
|
267
|
+
let mut distance_value: Distance = 0.0;
|
|
268
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
269
|
+
unsafe { simsimd_cos_i8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
270
|
+
Some(distance_value)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
274
|
+
if a.len() != b.len() {
|
|
275
|
+
return None;
|
|
276
|
+
}
|
|
277
|
+
let mut distance_value: Distance = 0.0;
|
|
278
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
279
|
+
unsafe { simsimd_cos_i8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
280
|
+
Some(distance_value)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
284
|
+
if a.len() != b.len() {
|
|
285
|
+
return None;
|
|
286
|
+
}
|
|
287
|
+
let mut distance_value: Distance = 0.0;
|
|
288
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
289
|
+
unsafe { simsimd_l2sq_i8(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
290
|
+
Some(distance_value)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
impl SpatialSimilarity for f16 {
|
|
295
|
+
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
296
|
+
if a.len() != b.len() {
|
|
297
|
+
return None;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
301
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
302
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
303
|
+
let mut distance_value: Distance = 0.0;
|
|
304
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
305
|
+
unsafe { simsimd_cos_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
|
|
306
|
+
Some(distance_value)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
310
|
+
if a.len() != b.len() {
|
|
311
|
+
return None;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
315
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
316
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
317
|
+
let mut distance_value: Distance = 0.0;
|
|
318
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
319
|
+
unsafe { simsimd_dot_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
|
|
320
|
+
Some(distance_value)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
324
|
+
if a.len() != b.len() {
|
|
325
|
+
return None;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
329
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
330
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
331
|
+
let mut distance_value: Distance = 0.0;
|
|
332
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
333
|
+
unsafe { simsimd_l2sq_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
|
|
334
|
+
Some(distance_value)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
impl SpatialSimilarity for f32 {
|
|
339
|
+
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
340
|
+
if a.len() != b.len() {
|
|
341
|
+
return None;
|
|
342
|
+
}
|
|
343
|
+
let mut distance_value: Distance = 0.0;
|
|
344
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
345
|
+
unsafe { simsimd_cos_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
346
|
+
Some(distance_value)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
350
|
+
if a.len() != b.len() {
|
|
351
|
+
return None;
|
|
352
|
+
}
|
|
353
|
+
let mut distance_value: Distance = 0.0;
|
|
354
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
355
|
+
unsafe { simsimd_dot_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
356
|
+
Some(distance_value)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
360
|
+
if a.len() != b.len() {
|
|
361
|
+
return None;
|
|
362
|
+
}
|
|
363
|
+
let mut distance_value: Distance = 0.0;
|
|
364
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
365
|
+
unsafe { simsimd_l2sq_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
366
|
+
Some(distance_value)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
impl SpatialSimilarity for f64 {
|
|
371
|
+
fn cos(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
372
|
+
if a.len() != b.len() {
|
|
373
|
+
return None;
|
|
374
|
+
}
|
|
375
|
+
let mut distance_value: Distance = 0.0;
|
|
376
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
377
|
+
unsafe { simsimd_cos_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
378
|
+
Some(distance_value)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
382
|
+
if a.len() != b.len() {
|
|
383
|
+
return None;
|
|
384
|
+
}
|
|
385
|
+
let mut distance_value: Distance = 0.0;
|
|
386
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
387
|
+
unsafe { simsimd_dot_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
388
|
+
Some(distance_value)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
fn l2sq(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
392
|
+
if a.len() != b.len() {
|
|
393
|
+
return None;
|
|
394
|
+
}
|
|
395
|
+
let mut distance_value: Distance = 0.0;
|
|
396
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
397
|
+
unsafe { simsimd_l2sq_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
398
|
+
Some(distance_value)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
impl ProbabilitySimilarity for f16 {
|
|
403
|
+
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
404
|
+
if a.len() != b.len() {
|
|
405
|
+
return None;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
409
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
410
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
411
|
+
let mut distance_value: Distance = 0.0;
|
|
412
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
413
|
+
unsafe { simsimd_js_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
|
|
414
|
+
Some(distance_value)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
418
|
+
if a.len() != b.len() {
|
|
419
|
+
return None;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
423
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
424
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
425
|
+
let mut distance_value: Distance = 0.0;
|
|
426
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
427
|
+
unsafe { simsimd_kl_f16(a_ptr, b_ptr, a.len(), distance_ptr) };
|
|
428
|
+
Some(distance_value)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
impl ProbabilitySimilarity for f32 {
|
|
433
|
+
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
434
|
+
if a.len() != b.len() {
|
|
435
|
+
return None;
|
|
436
|
+
}
|
|
437
|
+
let mut distance_value: Distance = 0.0;
|
|
438
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
439
|
+
unsafe { simsimd_js_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
440
|
+
Some(distance_value)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
444
|
+
if a.len() != b.len() {
|
|
445
|
+
return None;
|
|
446
|
+
}
|
|
447
|
+
let mut distance_value: Distance = 0.0;
|
|
448
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
449
|
+
unsafe { simsimd_kl_f32(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
450
|
+
Some(distance_value)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
impl ProbabilitySimilarity for f64 {
|
|
455
|
+
fn jensenshannon(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
456
|
+
if a.len() != b.len() {
|
|
457
|
+
return None;
|
|
458
|
+
}
|
|
459
|
+
let mut distance_value: Distance = 0.0;
|
|
460
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
461
|
+
unsafe { simsimd_js_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
462
|
+
Some(distance_value)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
fn kullbackleibler(a: &[Self], b: &[Self]) -> Option<Distance> {
|
|
466
|
+
if a.len() != b.len() {
|
|
467
|
+
return None;
|
|
468
|
+
}
|
|
469
|
+
let mut distance_value: Distance = 0.0;
|
|
470
|
+
let distance_ptr: *mut Distance = &mut distance_value as *mut Distance;
|
|
471
|
+
unsafe { simsimd_kl_f64(a.as_ptr(), b.as_ptr(), a.len(), distance_ptr) };
|
|
472
|
+
Some(distance_value)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
impl ComplexProducts for f16 {
|
|
477
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
478
|
+
if a.len() != b.len() {
|
|
479
|
+
return None;
|
|
480
|
+
}
|
|
481
|
+
// Prepare the output array where the real and imaginary parts will be stored
|
|
482
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
483
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
484
|
+
// Explicitly cast `*const f16` to `*const u16`
|
|
485
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
486
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
487
|
+
unsafe { simsimd_dot_f16c(a_ptr, b_ptr, a.len(), product_ptr) };
|
|
488
|
+
Some((product[0], product[1]))
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
492
|
+
if a.len() != b.len() {
|
|
493
|
+
return None;
|
|
494
|
+
}
|
|
495
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
496
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
497
|
+
let a_ptr = a.as_ptr() as *const u16;
|
|
498
|
+
let b_ptr = b.as_ptr() as *const u16;
|
|
499
|
+
unsafe { simsimd_vdot_f16c(a_ptr, b_ptr, a.len(), product_ptr) };
|
|
500
|
+
Some((product[0], product[1]))
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
impl ComplexProducts for f32 {
|
|
505
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
506
|
+
if a.len() != b.len() {
|
|
507
|
+
return None;
|
|
508
|
+
}
|
|
509
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
510
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
511
|
+
unsafe { simsimd_dot_f32c(a.as_ptr(), b.as_ptr(), a.len(), product_ptr) };
|
|
512
|
+
Some((product[0], product[1]))
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
516
|
+
if a.len() != b.len() {
|
|
517
|
+
return None;
|
|
518
|
+
}
|
|
519
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
520
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
521
|
+
unsafe { simsimd_vdot_f32c(a.as_ptr(), b.as_ptr(), a.len(), product_ptr) };
|
|
522
|
+
Some((product[0], product[1]))
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
impl ComplexProducts for f64 {
|
|
527
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
528
|
+
if a.len() != b.len() {
|
|
529
|
+
return None;
|
|
530
|
+
}
|
|
531
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
532
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
533
|
+
unsafe { simsimd_dot_f64c(a.as_ptr(), b.as_ptr(), a.len(), product_ptr) };
|
|
534
|
+
Some((product[0], product[1]))
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<ComplexProduct> {
|
|
538
|
+
if a.len() != b.len() {
|
|
539
|
+
return None;
|
|
540
|
+
}
|
|
541
|
+
let mut product: [Distance; 2] = [0.0, 0.0];
|
|
542
|
+
let product_ptr: *mut Distance = &mut product[0] as *mut _;
|
|
543
|
+
unsafe { simsimd_vdot_f64c(a.as_ptr(), b.as_ptr(), a.len(), product_ptr) };
|
|
544
|
+
Some((product[0], product[1]))
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#[cfg(test)]
|
|
549
|
+
mod tests {
|
|
550
|
+
use super::*;
|
|
551
|
+
use half::f16 as HalfF16;
|
|
552
|
+
|
|
553
|
+
#[test]
|
|
554
|
+
fn test_hardware_features_detection() {
|
|
555
|
+
let uses_arm = capabilties::uses_neon() || capabilties::uses_sve();
|
|
556
|
+
let uses_x86 = capabilties::uses_haswell()
|
|
557
|
+
|| capabilties::uses_skylake()
|
|
558
|
+
|| capabilties::uses_ice()
|
|
559
|
+
|| capabilties::uses_sapphire();
|
|
560
|
+
|
|
561
|
+
// The CPU can't simultaneously support ARM and x86 SIMD extensions
|
|
562
|
+
if uses_arm {
|
|
563
|
+
assert!(!uses_x86);
|
|
564
|
+
}
|
|
565
|
+
if uses_x86 {
|
|
566
|
+
assert!(!uses_arm);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
println!("- uses_neon: {}", capabilties::uses_neon());
|
|
570
|
+
println!("- uses_sve: {}", capabilties::uses_sve());
|
|
571
|
+
println!("- uses_haswell: {}", capabilties::uses_haswell());
|
|
572
|
+
println!("- uses_skylake: {}", capabilties::uses_skylake());
|
|
573
|
+
println!("- uses_ice: {}", capabilties::uses_ice());
|
|
574
|
+
println!("- uses_sapphire: {}", capabilties::uses_sapphire());
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
//
|
|
578
|
+
fn assert_almost_equal(left: Distance, right: Distance, tolerance: Distance) {
|
|
579
|
+
let lower = right - tolerance;
|
|
580
|
+
let upper = right + tolerance;
|
|
581
|
+
|
|
582
|
+
assert!(left >= lower && left <= upper);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
#[test]
|
|
586
|
+
fn test_cos_i8() {
|
|
587
|
+
let a = &[3, 97, 127];
|
|
588
|
+
let b = &[3, 97, 127];
|
|
589
|
+
|
|
590
|
+
if let Some(result) = SpatialSimilarity::cosine(a, b) {
|
|
591
|
+
println!("The result of cos_i8 is {:.8}", result);
|
|
592
|
+
assert_almost_equal(0.00012027938, result, 0.01);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
#[test]
|
|
597
|
+
fn test_cos_f32() {
|
|
598
|
+
let a = &[1.0, 2.0, 3.0];
|
|
599
|
+
let b = &[4.0, 5.0, 6.0];
|
|
600
|
+
|
|
601
|
+
if let Some(result) = SpatialSimilarity::cosine(a, b) {
|
|
602
|
+
println!("The result of cos_f32 is {:.8}", result);
|
|
603
|
+
assert_almost_equal(0.025, result, 0.01);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#[test]
|
|
608
|
+
fn test_dot_i8() {
|
|
609
|
+
let a = &[1, 2, 3];
|
|
610
|
+
let b = &[4, 5, 6];
|
|
611
|
+
|
|
612
|
+
if let Some(result) = SpatialSimilarity::dot(a, b) {
|
|
613
|
+
println!("The result of dot_i8 is {:.8}", result);
|
|
614
|
+
assert_almost_equal(0.029403687, result, 0.01);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
#[test]
|
|
619
|
+
fn test_dot_f32() {
|
|
620
|
+
let a = &[1.0, 2.0, 3.0];
|
|
621
|
+
let b = &[4.0, 5.0, 6.0];
|
|
622
|
+
|
|
623
|
+
if let Some(result) = SpatialSimilarity::dot(a, b) {
|
|
624
|
+
println!("The result of dot_f32 is {:.8}", result);
|
|
625
|
+
assert_almost_equal(32.0, result, 0.01);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
#[test]
|
|
630
|
+
fn test_dot_f32_complex() {
|
|
631
|
+
// Let's consider these as complex numbers where every pair is (real, imaginary)
|
|
632
|
+
let a: &[f32; 4] = &[1.0, 2.0, 3.0, 4.0]; // Represents two complex numbers: 1+2i, 3+4i
|
|
633
|
+
let b: &[f32; 4] = &[5.0, 6.0, 7.0, 8.0]; // Represents two complex numbers: 5+6i, 7+8i
|
|
634
|
+
|
|
635
|
+
if let Some((real, imag)) = ComplexProducts::dot(a, b) {
|
|
636
|
+
println!(
|
|
637
|
+
"The result of dot_f32_complex is real: {:.8}, imag: {:.8}",
|
|
638
|
+
real, imag
|
|
639
|
+
);
|
|
640
|
+
// These values should be replaced with the expected real and imaginary parts of the result
|
|
641
|
+
assert_almost_equal(-18.0, real, 0.01); // Corrected expected real part
|
|
642
|
+
assert_almost_equal(68.0, imag, 0.01); // Corrected expected imaginary part
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
#[test]
|
|
647
|
+
fn test_vdot_f32_complex() {
|
|
648
|
+
// Here we're assuming a similar setup to the previous test, but for the Hermitian (conjugate) dot product
|
|
649
|
+
let a: &[f32; 4] = &[1.0, 2.0, 3.0, 4.0]; // Represents two complex numbers: 1+2i, 3+4i
|
|
650
|
+
let b: &[f32; 4] = &[5.0, 6.0, 7.0, 8.0]; // Represents two complex numbers: 5+6i, 7+8i
|
|
651
|
+
|
|
652
|
+
if let Some((real, imag)) = ComplexProducts::vdot(a, b) {
|
|
653
|
+
println!(
|
|
654
|
+
"The result of vdot_f32_complex is real: {:.8}, imag: {:.8}",
|
|
655
|
+
real, imag
|
|
656
|
+
);
|
|
657
|
+
// Replace these with the actual expected values
|
|
658
|
+
assert_almost_equal(70.0, real, 0.01); // Example expected real part
|
|
659
|
+
assert_almost_equal(-8.0, imag, 0.01); // Example expected imaginary part
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
#[test]
|
|
664
|
+
fn test_l2sq_i8() {
|
|
665
|
+
let a = &[1, 2, 3];
|
|
666
|
+
let b = &[4, 5, 6];
|
|
667
|
+
|
|
668
|
+
if let Some(result) = SpatialSimilarity::sqeuclidean(a, b) {
|
|
669
|
+
println!("The result of l2sq_i8 is {:.8}", result);
|
|
670
|
+
assert_almost_equal(27.0, result, 0.01);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
#[test]
|
|
675
|
+
fn test_l2sq_f32() {
|
|
676
|
+
let a = &[1.0, 2.0, 3.0];
|
|
677
|
+
let b = &[4.0, 5.0, 6.0];
|
|
678
|
+
|
|
679
|
+
if let Some(result) = SpatialSimilarity::sqeuclidean(a, b) {
|
|
680
|
+
println!("The result of l2sq_f32 is {:.8}", result);
|
|
681
|
+
assert_almost_equal(27.0, result, 0.01);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Adding new tests for bit-level distances
|
|
686
|
+
#[test]
|
|
687
|
+
fn test_hamming_u8() {
|
|
688
|
+
let a = &[0b01010101, 0b11110000, 0b10101010]; // Binary representations for clarity
|
|
689
|
+
let b = &[0b01010101, 0b11110000, 0b10101010];
|
|
690
|
+
|
|
691
|
+
if let Some(result) = BinarySimilarity::hamming(a, b) {
|
|
692
|
+
println!("The result of hamming_u8 is {:.8}", result);
|
|
693
|
+
assert_almost_equal(0.0, result, 0.01); // Perfect match
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
#[test]
|
|
698
|
+
fn test_jaccard_u8() {
|
|
699
|
+
// For binary data, treat each byte as a set of bits
|
|
700
|
+
let a = &[0b11110000, 0b00001111, 0b10101010];
|
|
701
|
+
let b = &[0b11110000, 0b00001111, 0b01010101];
|
|
702
|
+
|
|
703
|
+
if let Some(result) = BinarySimilarity::jaccard(a, b) {
|
|
704
|
+
println!("The result of jaccard_u8 is {:.8}", result);
|
|
705
|
+
assert_almost_equal(0.5, result, 0.01); // Example value
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Adding new tests for probability similarities
|
|
710
|
+
#[test]
|
|
711
|
+
fn test_js_f32() {
|
|
712
|
+
let a = &[0.1, 0.9, 0.0];
|
|
713
|
+
let b = &[0.2, 0.8, 0.0];
|
|
714
|
+
|
|
715
|
+
if let Some(result) = ProbabilitySimilarity::jensenshannon(a, b) {
|
|
716
|
+
println!("The result of js_f32 is {:.8}", result);
|
|
717
|
+
assert_almost_equal(0.01, result, 0.01); // Example value
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
#[test]
|
|
722
|
+
fn test_kl_f32() {
|
|
723
|
+
let a = &[0.1, 0.9, 0.0];
|
|
724
|
+
let b = &[0.2, 0.8, 0.0];
|
|
725
|
+
|
|
726
|
+
if let Some(result) = ProbabilitySimilarity::kullbackleibler(a, b) {
|
|
727
|
+
println!("The result of kl_f32 is {:.8}", result);
|
|
728
|
+
assert_almost_equal(0.036, result, 0.01); // Example value
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
#[test]
|
|
733
|
+
fn test_cos_f16_same() {
|
|
734
|
+
// Assuming these u16 values represent f16 bit patterns, and they are identical
|
|
735
|
+
let a_u16: &[u16] = &[15360, 16384, 17408]; // Corresponding to some f16 values
|
|
736
|
+
let b_u16: &[u16] = &[15360, 16384, 17408]; // Same as above for simplicity
|
|
737
|
+
|
|
738
|
+
// Reinterpret cast from &[u16] to &[f16]
|
|
739
|
+
// SAFETY: This is safe as long as the representations are guaranteed to be identical,
|
|
740
|
+
// which they are for transparent structs wrapping the same type.
|
|
741
|
+
let a_f16: &[f16] =
|
|
742
|
+
unsafe { std::slice::from_raw_parts(a_u16.as_ptr() as *const f16, a_u16.len()) };
|
|
743
|
+
let b_f16: &[f16] =
|
|
744
|
+
unsafe { std::slice::from_raw_parts(b_u16.as_ptr() as *const f16, b_u16.len()) };
|
|
745
|
+
|
|
746
|
+
if let Some(result) = SpatialSimilarity::cosine(a_f16, b_f16) {
|
|
747
|
+
println!("The result of cos_f16 is {:.8}", result);
|
|
748
|
+
assert_almost_equal(0.0, result, 0.01); // Example value, adjust according to actual expected value
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
#[test]
|
|
753
|
+
fn test_cos_f16_interop() {
|
|
754
|
+
let a_half: Vec<HalfF16> = vec![1.0, 2.0, 3.0]
|
|
755
|
+
.iter()
|
|
756
|
+
.map(|&x| HalfF16::from_f32(x))
|
|
757
|
+
.collect();
|
|
758
|
+
let b_half: Vec<HalfF16> = vec![4.0, 5.0, 6.0]
|
|
759
|
+
.iter()
|
|
760
|
+
.map(|&x| HalfF16::from_f32(x))
|
|
761
|
+
.collect();
|
|
762
|
+
|
|
763
|
+
// SAFETY: This is safe as long as the memory representations are guaranteed to be identical,
|
|
764
|
+
// which they are due to both being #[repr(transparent)] wrappers around u16.
|
|
765
|
+
let a_simsimd: &[f16] =
|
|
766
|
+
unsafe { std::slice::from_raw_parts(a_half.as_ptr() as *const f16, a_half.len()) };
|
|
767
|
+
let b_simsimd: &[f16] =
|
|
768
|
+
unsafe { std::slice::from_raw_parts(b_half.as_ptr() as *const f16, b_half.len()) };
|
|
769
|
+
|
|
770
|
+
// Use the reinterpret-casted slices with your SpatialSimilarity implementation
|
|
771
|
+
if let Some(result) = SpatialSimilarity::cosine(a_simsimd, b_simsimd) {
|
|
772
|
+
// Expected value might need adjustment depending on actual cosine functionality
|
|
773
|
+
// Assuming identical vectors yield cosine similarity of 1.0
|
|
774
|
+
println!("The result of cos_f16 (interop) is {:.8}", result);
|
|
775
|
+
assert_almost_equal(0.025, result, 0.01);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|