usearch 2.9.0 → 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 +36 -2
- package/Cargo.toml +1 -1
- package/README.md +12 -15
- package/VERSION +1 -1
- package/c/lib.cpp +19 -17
- package/c/test.c +80 -38
- package/c/usearch.h +15 -15
- package/conanfile.py +1 -1
- package/include/usearch/index.hpp +5 -5
- package/include/usearch/index_dense.hpp +8 -4
- package/include/usearch/index_plugins.hpp +52 -32
- package/javascript/types/usearch.d.ts.map +1 -1
- package/javascript/usearch.js +4 -1
- package/package.json +3 -2
- 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
|
@@ -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) {
|
|
@@ -194,11 +194,25 @@ simsimd_neon_f16_l2sq(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_si
|
|
|
194
194
|
float32x4_t diff_vec = vsubq_f32(a_vec, b_vec);
|
|
195
195
|
sum_vec = vfmaq_f32(sum_vec, diff_vec, diff_vec);
|
|
196
196
|
}
|
|
197
|
-
|
|
198
|
-
for
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
|
|
198
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
199
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
200
|
+
// loads and stores with vectorized math.
|
|
201
|
+
if (i < n) {
|
|
202
|
+
union {
|
|
203
|
+
float16x4_t f16_vec;
|
|
204
|
+
simsimd_f16_t f16[4];
|
|
205
|
+
} a_padded_tail, b_padded_tail;
|
|
206
|
+
simsimd_size_t j = 0;
|
|
207
|
+
for (; i < n; ++i, ++j)
|
|
208
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
209
|
+
for (; j < 4; ++j)
|
|
210
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
211
|
+
float32x4_t diff_vec = vsubq_f32(vcvt_f32_f16(a_padded_tail.f16_vec), vcvt_f32_f16(b_padded_tail.f16_vec));
|
|
212
|
+
sum_vec = vfmaq_f32(sum_vec, diff_vec, diff_vec);
|
|
201
213
|
}
|
|
214
|
+
|
|
215
|
+
simsimd_f32_t sum = vaddvq_f32(sum_vec);
|
|
202
216
|
return sum;
|
|
203
217
|
}
|
|
204
218
|
|
|
@@ -212,9 +226,23 @@ simsimd_neon_f16_ip(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size
|
|
|
212
226
|
float32x4_t b_vec = vcvt_f32_f16(vld1_f16((float16_t const*)b + i));
|
|
213
227
|
ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
|
|
214
228
|
}
|
|
229
|
+
|
|
230
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
231
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
232
|
+
// loads and stores with vectorized math.
|
|
233
|
+
if (i < n) {
|
|
234
|
+
union {
|
|
235
|
+
float16x4_t f16_vec;
|
|
236
|
+
simsimd_f16_t f16[4];
|
|
237
|
+
} a_padded_tail, b_padded_tail;
|
|
238
|
+
simsimd_size_t j = 0;
|
|
239
|
+
for (; i < n; ++i, ++j)
|
|
240
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
241
|
+
for (; j < 4; ++j)
|
|
242
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
243
|
+
ab_vec = vfmaq_f32(ab_vec, vcvt_f32_f16(a_padded_tail.f16_vec), vcvt_f32_f16(b_padded_tail.f16_vec));
|
|
244
|
+
}
|
|
215
245
|
simsimd_f32_t ab = vaddvq_f32(ab_vec);
|
|
216
|
-
for (; i < n; ++i)
|
|
217
|
-
ab += SIMSIMD_UNCOMPRESS_F16(a[i]) * SIMSIMD_UNCOMPRESS_F16(b[i]);
|
|
218
246
|
return 1 - ab;
|
|
219
247
|
}
|
|
220
248
|
|
|
@@ -230,13 +258,29 @@ simsimd_neon_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
|
|
|
230
258
|
a2_vec = vfmaq_f32(a2_vec, a_vec, a_vec);
|
|
231
259
|
b2_vec = vfmaq_f32(b2_vec, b_vec, b_vec);
|
|
232
260
|
}
|
|
233
|
-
|
|
234
|
-
for
|
|
235
|
-
|
|
236
|
-
|
|
261
|
+
|
|
262
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
263
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
264
|
+
// loads and stores with vectorized math.
|
|
265
|
+
if (i < n) {
|
|
266
|
+
union {
|
|
267
|
+
float16x4_t f16_vec;
|
|
268
|
+
simsimd_f16_t f16[4];
|
|
269
|
+
} a_padded_tail, b_padded_tail;
|
|
270
|
+
simsimd_size_t j = 0;
|
|
271
|
+
for (; i < n; ++i, ++j)
|
|
272
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
273
|
+
for (; j < 4; ++j)
|
|
274
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
275
|
+
float32x4_t a_vec = vcvt_f32_f16(a_padded_tail.f16_vec);
|
|
276
|
+
float32x4_t b_vec = vcvt_f32_f16(b_padded_tail.f16_vec);
|
|
277
|
+
ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
|
|
278
|
+
a2_vec = vfmaq_f32(a2_vec, a_vec, a_vec);
|
|
279
|
+
b2_vec = vfmaq_f32(b2_vec, b_vec, b_vec);
|
|
237
280
|
}
|
|
238
281
|
|
|
239
282
|
// Avoid `simsimd_approximate_inverse_square_root` on Arm NEON
|
|
283
|
+
simsimd_f32_t ab = vaddvq_f32(ab_vec), a2 = vaddvq_f32(a2_vec), b2 = vaddvq_f32(b2_vec);
|
|
240
284
|
simsimd_f32_t a2_b2_arr[2] = {a2, b2};
|
|
241
285
|
float32x2_t a2_b2 = vld1_f32(a2_b2_arr);
|
|
242
286
|
a2_b2 = vrsqrte_f32(a2_b2);
|
|
@@ -582,18 +626,31 @@ simsimd_avx2_f16_l2sq(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_si
|
|
|
582
626
|
d2_vec = _mm256_fmadd_ps(d_vec, d_vec, d2_vec);
|
|
583
627
|
}
|
|
584
628
|
|
|
629
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
630
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
631
|
+
// loads and stores with vectorized math.
|
|
632
|
+
if (i < n) {
|
|
633
|
+
union {
|
|
634
|
+
__m128i f16_vec;
|
|
635
|
+
simsimd_f16_t f16[8];
|
|
636
|
+
} a_padded_tail, b_padded_tail;
|
|
637
|
+
simsimd_size_t j = 0;
|
|
638
|
+
for (; i < n; ++i, ++j)
|
|
639
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
640
|
+
for (; j < 8; ++j)
|
|
641
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
642
|
+
__m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
|
|
643
|
+
__m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
|
|
644
|
+
__m256 d_vec = _mm256_sub_ps(a_vec, b_vec);
|
|
645
|
+
d2_vec = _mm256_fmadd_ps(d_vec, d_vec, d2_vec);
|
|
646
|
+
}
|
|
647
|
+
|
|
585
648
|
d2_vec = _mm256_add_ps(_mm256_permute2f128_ps(d2_vec, d2_vec, 1), d2_vec);
|
|
586
649
|
d2_vec = _mm256_hadd_ps(d2_vec, d2_vec);
|
|
587
650
|
d2_vec = _mm256_hadd_ps(d2_vec, d2_vec);
|
|
588
651
|
|
|
589
652
|
simsimd_f32_t result;
|
|
590
653
|
_mm_store_ss(&result, _mm256_castps256_ps128(d2_vec));
|
|
591
|
-
|
|
592
|
-
// Accumulate the tail:
|
|
593
|
-
for (; i < n; ++i) {
|
|
594
|
-
simsimd_f32_t n = SIMSIMD_UNCOMPRESS_F16(a[i]) - SIMSIMD_UNCOMPRESS_F16(b[i]);
|
|
595
|
-
result += n * n;
|
|
596
|
-
}
|
|
597
654
|
return result;
|
|
598
655
|
}
|
|
599
656
|
|
|
@@ -608,18 +665,33 @@ simsimd_avx2_f16_ip(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size
|
|
|
608
665
|
ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
609
666
|
}
|
|
610
667
|
|
|
668
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
669
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
670
|
+
// loads and stores with vectorized math.
|
|
671
|
+
if (i < n) {
|
|
672
|
+
union {
|
|
673
|
+
__m128i f16_vec;
|
|
674
|
+
simsimd_f16_t f16[8];
|
|
675
|
+
} a_padded_tail, b_padded_tail;
|
|
676
|
+
simsimd_size_t j = 0;
|
|
677
|
+
for (; i < n; ++i, ++j)
|
|
678
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
679
|
+
for (; j < 8; ++j)
|
|
680
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
681
|
+
__m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
|
|
682
|
+
__m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
|
|
683
|
+
ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
684
|
+
}
|
|
685
|
+
|
|
611
686
|
ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 1), ab_vec);
|
|
612
687
|
ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
|
|
613
688
|
ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
|
|
614
689
|
|
|
615
690
|
simsimd_f32_t result;
|
|
616
691
|
_mm_store_ss(&result, _mm256_castps256_ps128(ab_vec));
|
|
617
|
-
|
|
618
|
-
// Accumulate the tail:
|
|
619
|
-
for (; i < n; ++i)
|
|
620
|
-
result += SIMSIMD_UNCOMPRESS_F16(a[i]) * SIMSIMD_UNCOMPRESS_F16(b[i]);
|
|
621
692
|
return 1 - result;
|
|
622
693
|
}
|
|
694
|
+
|
|
623
695
|
__attribute__((target("avx2,f16c,fma"))) //
|
|
624
696
|
inline static simsimd_f32_t
|
|
625
697
|
simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n) {
|
|
@@ -634,6 +706,27 @@ simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
|
|
|
634
706
|
b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
635
707
|
}
|
|
636
708
|
|
|
709
|
+
// In case the software emulation for `f16` scalars is enabled, the `simsimd_uncompress_f16`
|
|
710
|
+
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
711
|
+
// loads and stores with vectorized math.
|
|
712
|
+
if (i < n) {
|
|
713
|
+
union {
|
|
714
|
+
__m128i f16_vec;
|
|
715
|
+
simsimd_f16_t f16[8];
|
|
716
|
+
} a_padded_tail, b_padded_tail;
|
|
717
|
+
simsimd_size_t j = 0;
|
|
718
|
+
for (; i < n; ++i, ++j)
|
|
719
|
+
a_padded_tail.f16[j] = a[i], b_padded_tail.f16[j] = b[i];
|
|
720
|
+
for (; j < 8; ++j)
|
|
721
|
+
a_padded_tail.f16[j] = 0, b_padded_tail.f16[j] = 0;
|
|
722
|
+
__m256 a_vec = _mm256_cvtph_ps(a_padded_tail.f16_vec);
|
|
723
|
+
__m256 b_vec = _mm256_cvtph_ps(b_padded_tail.f16_vec);
|
|
724
|
+
ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
725
|
+
a2_vec = _mm256_fmadd_ps(a_vec, a_vec, a2_vec);
|
|
726
|
+
b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Horizontal reductions:
|
|
637
730
|
ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 1), ab_vec);
|
|
638
731
|
ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
|
|
639
732
|
ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
|
|
@@ -651,12 +744,6 @@ simsimd_avx2_f16_cos(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_siz
|
|
|
651
744
|
_mm_store_ss(&a2, _mm256_castps256_ps128(a2_vec));
|
|
652
745
|
_mm_store_ss(&b2, _mm256_castps256_ps128(b2_vec));
|
|
653
746
|
|
|
654
|
-
// Accumulate the tail:
|
|
655
|
-
for (; i < n; ++i) {
|
|
656
|
-
simsimd_f32_t ai = SIMSIMD_UNCOMPRESS_F16(a[i]), bi = SIMSIMD_UNCOMPRESS_F16(b[i]);
|
|
657
|
-
ab += ai * bi, a2 += ai * ai, b2 += bi * bi;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
747
|
// Replace simsimd_approximate_inverse_square_root with `rsqrtss`
|
|
661
748
|
__m128 a2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)a2));
|
|
662
749
|
__m128 b2_sqrt_recip = _mm_rsqrt_ss(_mm_set_ss((float)b2));
|
|
@@ -1037,19 +1124,24 @@ simsimd_avx512_i8_cos(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size
|
|
|
1037
1124
|
__m512i a_vec, b_vec;
|
|
1038
1125
|
|
|
1039
1126
|
simsimd_avx512_i8_cos_cycle:
|
|
1040
|
-
if (n <
|
|
1041
|
-
|
|
1042
|
-
a_vec =
|
|
1043
|
-
b_vec =
|
|
1127
|
+
if (n < 32) {
|
|
1128
|
+
__mmask32 mask = _bzhi_u32(0xFFFFFFFF, n);
|
|
1129
|
+
a_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a));
|
|
1130
|
+
b_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b));
|
|
1044
1131
|
n = 0;
|
|
1045
1132
|
} else {
|
|
1046
|
-
a_vec =
|
|
1047
|
-
b_vec =
|
|
1048
|
-
a +=
|
|
1133
|
+
a_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(a));
|
|
1134
|
+
b_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(b));
|
|
1135
|
+
a += 32, b += 32, n -= 32;
|
|
1049
1136
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1137
|
+
// Unfortunately we can't use the `_mm512_dpbusd_epi32` intrinsics here either,
|
|
1138
|
+
// as it's assymetric with respect to the sign of the input arguments:
|
|
1139
|
+
// Signed(ZeroExtend16(a.byte[4*j]) * SignExtend16(b.byte[4*j]))
|
|
1140
|
+
// So we have to use the `_mm512_dpwssd_epi32` intrinsics instead, upcasting
|
|
1141
|
+
// to 16-bit beforehand.
|
|
1142
|
+
ab_i32s_vec = _mm512_dpwssd_epi32(ab_i32s_vec, a_vec, b_vec);
|
|
1143
|
+
a2_i32s_vec = _mm512_dpwssd_epi32(a2_i32s_vec, a_vec, a_vec);
|
|
1144
|
+
b2_i32s_vec = _mm512_dpwssd_epi32(b2_i32s_vec, b_vec, b_vec);
|
|
1053
1145
|
if (n)
|
|
1054
1146
|
goto simsimd_avx512_i8_cos_cycle;
|
|
1055
1147
|
|
|
@@ -84,11 +84,11 @@
|
|
|
84
84
|
#endif
|
|
85
85
|
|
|
86
86
|
#ifndef SIMSIMD_F32_DIVISION_EPSILON
|
|
87
|
-
#define SIMSIMD_F32_DIVISION_EPSILON 1e-7
|
|
87
|
+
#define SIMSIMD_F32_DIVISION_EPSILON (1e-7)
|
|
88
88
|
#endif
|
|
89
89
|
|
|
90
90
|
#ifndef SIMSIMD_F16_DIVISION_EPSILON
|
|
91
|
-
#define SIMSIMD_F16_DIVISION_EPSILON 1e-3
|
|
91
|
+
#define SIMSIMD_F16_DIVISION_EPSILON (1e-3)
|
|
92
92
|
#endif
|
|
93
93
|
|
|
94
94
|
#ifdef __cplusplus
|
|
@@ -100,6 +100,7 @@ typedef float simsimd_f32_t;
|
|
|
100
100
|
typedef double simsimd_f64_t;
|
|
101
101
|
typedef signed char simsimd_i8_t;
|
|
102
102
|
typedef unsigned char simsimd_b8_t;
|
|
103
|
+
typedef unsigned long long simsimd_u64_t;
|
|
103
104
|
typedef unsigned long long simsimd_size_t;
|
|
104
105
|
|
|
105
106
|
#if !defined(SIMSIMD_NATIVE_F16) || SIMSIMD_NATIVE_F16
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simsimd",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "simsimd",
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.8.1",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "Apache 2.0",
|
|
12
12
|
"dependencies": {
|
|
@@ -627,9 +627,9 @@
|
|
|
627
627
|
"dev": true
|
|
628
628
|
},
|
|
629
629
|
"node_modules/ip": {
|
|
630
|
-
"version": "2.0.
|
|
631
|
-
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.
|
|
632
|
-
"integrity": "sha512-
|
|
630
|
+
"version": "2.0.1",
|
|
631
|
+
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
|
|
632
|
+
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==",
|
|
633
633
|
"dev": true
|
|
634
634
|
},
|
|
635
635
|
"node_modules/is-fullwidth-code-point": {
|
package/simsimd/package.json
CHANGED
|
@@ -10,18 +10,21 @@ pub(crate) fn cosine_similarity_cpu(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
|
10
10
|
return None;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
let dot_product
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
let (dot_product, norm_a, norm_b) = a
|
|
14
|
+
.iter()
|
|
15
|
+
.zip(b)
|
|
16
|
+
.map(|(a, b)| (a * b, a * a, b * b))
|
|
17
|
+
.fold((0.0, 0.0, 0.0), |acc, x| {
|
|
18
|
+
(acc.0 + x.0, acc.1 + x.1, acc.2 + x.2)
|
|
19
|
+
});
|
|
16
20
|
|
|
17
21
|
Some(1.0 - (dot_product / (norm_a.sqrt() * norm_b.sqrt())))
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
pub(crate) fn squared_euclidean_cpu(a: &[f32], b: &[f32]) -> f32 {
|
|
21
|
-
|
|
24
|
+
pub(crate) fn squared_euclidean_cpu(a: &[f32], b: &[f32]) -> Option<f32> {
|
|
25
|
+
if a.len() != b.len() {
|
|
26
|
+
return None;
|
|
27
|
+
}
|
|
22
28
|
|
|
23
|
-
a.iter()
|
|
24
|
-
.zip(b.iter())
|
|
25
|
-
.map(|(x, y)| ((*x) - (*y)) * ((*x) - (*y)))
|
|
26
|
-
.fold(0.0, ::std::ops::Add::add)
|
|
29
|
+
Some(a.iter().zip(b).map(|(x, y)| (x - y).powi(2)).sum())
|
|
27
30
|
}
|
package/simsimd/rust/lib.c
CHANGED
|
@@ -7,62 +7,39 @@ simsimd_capability_t cached_capabilities(void) {
|
|
|
7
7
|
return static_capabilities;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
#define SIMSIMD_METRIC_DECLARATION(name, type) \
|
|
11
|
+
simsimd_f32_t name##_##type(simsimd_##type##_t const* a, simsimd_##type##_t const* b, simsimd_size_t d) { \
|
|
12
|
+
static simsimd_metric_punned_t metric = 0; \
|
|
13
|
+
if (metric == 0) { \
|
|
14
|
+
simsimd_capability_t used_capability; \
|
|
15
|
+
simsimd_find_metric_punned(simsimd_metric_##name##_k, simsimd_datatype_##type##_k, cached_capabilities(), \
|
|
16
|
+
simsimd_cap_any_k, &metric, &used_capability); \
|
|
17
|
+
} \
|
|
18
|
+
return metric(a, b, d, d); \
|
|
16
19
|
}
|
|
17
|
-
return metric(a, b, d, d);
|
|
18
|
-
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
// Spatial distances
|
|
22
|
+
SIMSIMD_METRIC_DECLARATION(cosine, i8)
|
|
23
|
+
SIMSIMD_METRIC_DECLARATION(cosine, f16)
|
|
24
|
+
SIMSIMD_METRIC_DECLARATION(cosine, f32)
|
|
25
|
+
SIMSIMD_METRIC_DECLARATION(cosine, f64)
|
|
26
|
+
SIMSIMD_METRIC_DECLARATION(inner, i8)
|
|
27
|
+
SIMSIMD_METRIC_DECLARATION(inner, f16)
|
|
28
|
+
SIMSIMD_METRIC_DECLARATION(inner, f32)
|
|
29
|
+
SIMSIMD_METRIC_DECLARATION(inner, f64)
|
|
30
|
+
SIMSIMD_METRIC_DECLARATION(sqeuclidean, i8)
|
|
31
|
+
SIMSIMD_METRIC_DECLARATION(sqeuclidean, f16)
|
|
32
|
+
SIMSIMD_METRIC_DECLARATION(sqeuclidean, f32)
|
|
33
|
+
SIMSIMD_METRIC_DECLARATION(sqeuclidean, f64)
|
|
29
34
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
}
|
|
35
|
+
// Binary distances
|
|
36
|
+
SIMSIMD_METRIC_DECLARATION(hamming, b8)
|
|
37
|
+
SIMSIMD_METRIC_DECLARATION(jaccard, b8)
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
}
|
|
39
|
+
// Probability distributions
|
|
40
|
+
SIMSIMD_METRIC_DECLARATION(kl, f16)
|
|
41
|
+
SIMSIMD_METRIC_DECLARATION(kl, f32)
|
|
42
|
+
SIMSIMD_METRIC_DECLARATION(kl, f64)
|
|
43
|
+
SIMSIMD_METRIC_DECLARATION(js, f16)
|
|
44
|
+
SIMSIMD_METRIC_DECLARATION(js, f32)
|
|
45
|
+
SIMSIMD_METRIC_DECLARATION(js, f64)
|