usearch 0.18.8 → 0.19.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.
- package/Cargo.toml +1 -1
- package/README.md +176 -6
- package/conanfile.py +1 -1
- package/cpp/bench.cpp +2 -2
- package/cpp/test.cpp +11 -9
- package/include/usearch/index.hpp +269 -50
- package/include/usearch/index_punned_dense.hpp +123 -108
- package/include/usearch/index_punned_helpers.hpp +0 -12
- package/package.json +1 -1
- package/python/lib.cpp +116 -40
- package/python/scripts/bench.ipynb +13 -2113
- package/python/scripts/bench.py +45 -29
- package/python/scripts/index_faiss.py +19 -19
- package/python/scripts/test.py +275 -56
- package/python/usearch/eval.py +100 -49
- package/python/usearch/index.py +112 -83
- package/python/usearch/io.py +23 -23
- package/python/usearch/join.py +2 -4
- package/python/usearch/numba.py +22 -18
- package/python/usearch/server.py +38 -26
- package/simsimd/.vscode/settings.json +2 -1
- package/simsimd/CMakeLists.txt +1 -1
- package/simsimd/README.md +14 -2
- package/simsimd/bench.cxx +44 -27
- package/simsimd/include/simsimd/simsimd.h +2 -1
- package/simsimd/include/simsimd/simsimd.hpp +0 -443
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @brief Collection of Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
|
|
3
|
-
*
|
|
4
|
-
* @author Ashot Vardanian
|
|
5
|
-
* @date March 14, 2023
|
|
6
|
-
*
|
|
7
|
-
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
8
|
-
* Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
|
|
9
|
-
* Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
#pragma once
|
|
13
|
-
#include <cmath> // `std::sqrt`
|
|
14
|
-
|
|
15
|
-
#if defined(__AVX2__)
|
|
16
|
-
#include <immintrin.h>
|
|
17
|
-
#endif
|
|
18
|
-
|
|
19
|
-
#if defined(__ARM_NEON)
|
|
20
|
-
#include <arm_neon.h>
|
|
21
|
-
#endif
|
|
22
|
-
|
|
23
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
24
|
-
#include <arm_sve.h>
|
|
25
|
-
#endif
|
|
26
|
-
|
|
27
|
-
namespace av {
|
|
28
|
-
namespace simsimd {
|
|
29
|
-
|
|
30
|
-
using dim_t = unsigned int;
|
|
31
|
-
using i8_t = signed char;
|
|
32
|
-
using u8_t = unsigned char;
|
|
33
|
-
using i16_t = short;
|
|
34
|
-
using i32_t = int;
|
|
35
|
-
using u32_t = unsigned int;
|
|
36
|
-
using u64_t = unsigned long;
|
|
37
|
-
using f32_t = float;
|
|
38
|
-
using f64_t = double;
|
|
39
|
-
enum class f16_t : i16_t {};
|
|
40
|
-
|
|
41
|
-
union f32i32_t {
|
|
42
|
-
i32_t i;
|
|
43
|
-
f32_t f;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @brief Dot product for arbitrary length/type vectors.
|
|
48
|
-
* @return Dot product ∈ [-1, 1] for normalized vectors.
|
|
49
|
-
*/
|
|
50
|
-
struct dot_product_t {
|
|
51
|
-
|
|
52
|
-
f32_t operator()(f32_t const* a, f32_t const* b, dim_t const dim) const noexcept {
|
|
53
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
54
|
-
dim_t i = 0;
|
|
55
|
-
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
56
|
-
svbool_t pg_vec = svwhilelt_b32(i, dim);
|
|
57
|
-
do {
|
|
58
|
-
svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
|
|
59
|
-
svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
|
|
60
|
-
ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
61
|
-
i += svcntw();
|
|
62
|
-
pg_vec = svwhilelt_b32(i, dim);
|
|
63
|
-
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
64
|
-
return svaddv_f32(svptrue_b32(), ab_vec);
|
|
65
|
-
#else
|
|
66
|
-
return any(a, b, dim);
|
|
67
|
-
#endif
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
f16_t operator()(f16_t const* a_enum, f16_t const* b_enum, dim_t const dim) const noexcept {
|
|
71
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
72
|
-
dim_t i = 0;
|
|
73
|
-
svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
74
|
-
svbool_t pg_vec = svwhilelt_b16(i, dim);
|
|
75
|
-
auto a = reinterpret_cast<float16_t const*>(a_enum);
|
|
76
|
-
auto b = reinterpret_cast<float16_t const*>(b_enum);
|
|
77
|
-
do {
|
|
78
|
-
svfloat16_t a_vec = svld1_f16(pg_vec, a + i);
|
|
79
|
-
svfloat16_t b_vec = svld1_f16(pg_vec, b + i);
|
|
80
|
-
ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
81
|
-
i += svcnth();
|
|
82
|
-
pg_vec = svwhilelt_b16(i, dim);
|
|
83
|
-
} while (svptest_any(svptrue_b16(), pg_vec));
|
|
84
|
-
return static_cast<f16_t>(svaddv_f16(svptrue_b16(), ab_vec));
|
|
85
|
-
#else
|
|
86
|
-
return any(a_enum, b_enum, dim);
|
|
87
|
-
#endif
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
template <typename at> //
|
|
91
|
-
at operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
92
|
-
return any(a, b, dim);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private:
|
|
96
|
-
template <typename at> //
|
|
97
|
-
inline at any(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
98
|
-
at ab = 0;
|
|
99
|
-
#pragma GCC ivdep
|
|
100
|
-
#pragma clang loop vectorize(enable)
|
|
101
|
-
for (dim_t i = 0; i != dim; ++i)
|
|
102
|
-
ab += a[i] * b[i];
|
|
103
|
-
return ab;
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @brief Cosine similarity for arbitrary length/type vectors.
|
|
109
|
-
* @return Similarity ∈ [-1, 1].
|
|
110
|
-
*/
|
|
111
|
-
struct cosine_similarity_t {
|
|
112
|
-
|
|
113
|
-
f32_t operator()(f32_t const* a, f32_t const* b, dim_t const dim) const noexcept {
|
|
114
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
115
|
-
dim_t i = 0;
|
|
116
|
-
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
117
|
-
svfloat32_t a2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
118
|
-
svfloat32_t b2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
119
|
-
svbool_t pg_vec = svwhilelt_b32(i, dim);
|
|
120
|
-
do {
|
|
121
|
-
svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
|
|
122
|
-
svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
|
|
123
|
-
ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
124
|
-
a2_vec = svmla_f32_x(pg_vec, a2_vec, a_vec, a_vec);
|
|
125
|
-
b2_vec = svmla_f32_x(pg_vec, b2_vec, b_vec, b_vec);
|
|
126
|
-
i += svcntw();
|
|
127
|
-
pg_vec = svwhilelt_b32(i, dim);
|
|
128
|
-
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
129
|
-
auto ab = svaddv_f32(svptrue_b32(), ab_vec);
|
|
130
|
-
auto a2 = svaddv_f32(svptrue_b32(), a2_vec);
|
|
131
|
-
auto b2 = svaddv_f32(svptrue_b32(), b2_vec);
|
|
132
|
-
return ab / (std::sqrt(a2) * std::sqrt(b2));
|
|
133
|
-
#else
|
|
134
|
-
return any(a, b, dim);
|
|
135
|
-
#endif
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
template <typename at> //
|
|
139
|
-
at operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
140
|
-
return any(a, b, dim);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
private:
|
|
144
|
-
template <typename at> //
|
|
145
|
-
inline at any(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
146
|
-
at ab = 0, a2 = 0, b2 = 0;
|
|
147
|
-
#pragma GCC ivdep
|
|
148
|
-
#pragma clang loop vectorize(enable)
|
|
149
|
-
for (dim_t i = 0; i != dim; ++i)
|
|
150
|
-
ab += a[i] * b[i], a2 += a[i] * a[i], b2 += b[i] * b[i];
|
|
151
|
-
return ab / (std::sqrt(a2) * std::sqrt(b2));
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* @brief Euclidean distance (L2) for arbitrary length/type vectors.
|
|
157
|
-
* @return Euclidean distance (L2) ∈ [0, 2] for normalized vectors.
|
|
158
|
-
*/
|
|
159
|
-
struct euclidean_distance_t {
|
|
160
|
-
|
|
161
|
-
f32_t operator()(f32_t const* a, f32_t const* b, dim_t const dim) const noexcept {
|
|
162
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
163
|
-
dim_t i = 0;
|
|
164
|
-
svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
165
|
-
svbool_t pg_vec = svwhilelt_b32(i, dim);
|
|
166
|
-
do {
|
|
167
|
-
svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
|
|
168
|
-
svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
|
|
169
|
-
svfloat32_t a_minus_b_vec = svsub_f32_x(pg_vec, a_vec, b_vec);
|
|
170
|
-
d2_vec = svmla_f32_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
|
|
171
|
-
i += svcntw();
|
|
172
|
-
pg_vec = svwhilelt_b32(i, dim);
|
|
173
|
-
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
174
|
-
auto d2 = svaddv_f32(svptrue_b32(), d2_vec);
|
|
175
|
-
return std::sqrt(d2);
|
|
176
|
-
#else
|
|
177
|
-
return any(a, b, dim);
|
|
178
|
-
#endif
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
f16_t operator()(f16_t const* a_enum, f16_t const* b_enum, dim_t const dim) const noexcept {
|
|
182
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
183
|
-
dim_t i = 0;
|
|
184
|
-
svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
185
|
-
svbool_t pg_vec = svwhilelt_b16(i, dim);
|
|
186
|
-
auto a = reinterpret_cast<float16_t const*>(a_enum);
|
|
187
|
-
auto b = reinterpret_cast<float16_t const*>(b_enum);
|
|
188
|
-
do {
|
|
189
|
-
svfloat16_t a_vec = svld1_f16(pg_vec, a + i);
|
|
190
|
-
svfloat16_t b_vec = svld1_f16(pg_vec, b + i);
|
|
191
|
-
svfloat16_t a_minus_b_vec = svsub_f16_x(pg_vec, a_vec, b_vec);
|
|
192
|
-
d2_vec = svmla_f16_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
|
|
193
|
-
i += svcnth();
|
|
194
|
-
pg_vec = svwhilelt_b16(i, dim);
|
|
195
|
-
} while (svptest_any(svptrue_b16(), pg_vec));
|
|
196
|
-
auto d2 = svaddv_f16(svptrue_b16(), d2_vec);
|
|
197
|
-
return static_cast<f16_t>(std::sqrt(d2));
|
|
198
|
-
#else
|
|
199
|
-
return any(a_enum, b_enum, dim);
|
|
200
|
-
#endif
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
f32_t operator()(i8_t const* a, i8_t const* b, dim_t const dim) const noexcept {
|
|
204
|
-
i32_t d2 = 0;
|
|
205
|
-
#pragma GCC ivdep
|
|
206
|
-
#pragma clang loop vectorize(enable)
|
|
207
|
-
for (dim_t i = 0; i != dim; ++i)
|
|
208
|
-
d2 += i32_t(i16_t(a[i]) - i16_t(b[i])) * i32_t(i16_t(a[i]) - i16_t(b[i]));
|
|
209
|
-
return std::sqrt(d2);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
template <typename at> //
|
|
213
|
-
at operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
214
|
-
return any(a, b, dim);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private:
|
|
218
|
-
template <typename at> //
|
|
219
|
-
at any(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
220
|
-
at d2 = 0;
|
|
221
|
-
#pragma GCC ivdep
|
|
222
|
-
#pragma clang loop vectorize(enable)
|
|
223
|
-
for (dim_t i = 0; i != dim; ++i)
|
|
224
|
-
d2 += (a[i] - b[i]) * (a[i] - b[i]);
|
|
225
|
-
return std::sqrt(d2);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* @brief Bitwise Hamming distance on scalar words (8, 16, 32, or 64-bit).
|
|
231
|
-
* @return Integer number of different bits ∈ [0, dim).
|
|
232
|
-
*/
|
|
233
|
-
struct hamming_bits_distance_t {
|
|
234
|
-
|
|
235
|
-
#if defined(__GNUC__)
|
|
236
|
-
static dim_t popcount(u32_t v) noexcept { return __builtin_popcount(v); }
|
|
237
|
-
static dim_t popcount(u64_t v) noexcept { return __builtin_popcountl(v); }
|
|
238
|
-
#elif defined(__AVX2__)
|
|
239
|
-
static dim_t popcount(u32_t v) noexcept { return _mm_popcnt_u32(v); }
|
|
240
|
-
static dim_t popcount(u64_t v) noexcept { return _mm_popcnt_u64(v); }
|
|
241
|
-
#endif
|
|
242
|
-
|
|
243
|
-
template <typename at> //
|
|
244
|
-
dim_t operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
245
|
-
dim_t words = dim / (sizeof(at) * __CHAR_BIT__);
|
|
246
|
-
#if defined(__ARM_FEATURE_SVE)
|
|
247
|
-
auto a8 = reinterpret_cast<u8_t const*>(a);
|
|
248
|
-
auto b8 = reinterpret_cast<u8_t const*>(b);
|
|
249
|
-
dim_t i = 0;
|
|
250
|
-
svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
251
|
-
svbool_t pg_vec = svwhilelt_b8(i, dim);
|
|
252
|
-
do {
|
|
253
|
-
svuint8_t a_vec = svld1_u8(pg_vec, a8 + i);
|
|
254
|
-
svuint8_t b_vec = svld1_u8(pg_vec, b8 + i);
|
|
255
|
-
svuint8_t a_xor_b_vec = sveor_u8_m(pg_vec, a_vec, b_vec);
|
|
256
|
-
d_vec = svadd_u8_m(pg_vec, d_vec, svcnt_u8_x(pg_vec, a_xor_b_vec));
|
|
257
|
-
i += svcntb();
|
|
258
|
-
pg_vec = svwhilelt_b32(i, dim);
|
|
259
|
-
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
260
|
-
auto d = svaddv_u8(svptrue_b32(), d_vec);
|
|
261
|
-
return d;
|
|
262
|
-
#else
|
|
263
|
-
dim_t d = 0;
|
|
264
|
-
#pragma GCC ivdep
|
|
265
|
-
#pragma clang loop vectorize(enable)
|
|
266
|
-
for (dim_t i = 0; i != words; ++i)
|
|
267
|
-
d += hamming_bits_distance_t::popcount(a[i] ^ b[i]);
|
|
268
|
-
#endif
|
|
269
|
-
return d;
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* @brief Dot product between `float` vectors where `dim` is a multiple of 4.
|
|
275
|
-
* @return Dot product ∈ [-1, 1] for normalized vectors.
|
|
276
|
-
*/
|
|
277
|
-
struct dot_product_f32x4k_t {
|
|
278
|
-
|
|
279
|
-
f32_t operator()(f32_t const* a, f32_t const* b, dim_t const dim) const noexcept {
|
|
280
|
-
#if defined(__AVX2__)
|
|
281
|
-
__m128 ab_vec = _mm_set1_ps(0);
|
|
282
|
-
for (dim_t i = 0; i != dim; i += 4)
|
|
283
|
-
ab_vec = _mm_fmadd_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i), ab_vec);
|
|
284
|
-
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
285
|
-
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
286
|
-
f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
|
|
287
|
-
return ab_union.f;
|
|
288
|
-
#elif defined(__ARM_NEON)
|
|
289
|
-
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
290
|
-
for (dim_t i = 0; i != dim; i += 4)
|
|
291
|
-
ab_vec = vmlaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
|
|
292
|
-
return vaddvq_f32(ab_vec);
|
|
293
|
-
#else
|
|
294
|
-
return dot_product_t{}(a, b, dim);
|
|
295
|
-
#endif
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* @brief Cosine similarity between `float` vectors where `dim` is a multiple of 4.
|
|
301
|
-
* @return Similarity ∈ [-1, 1].
|
|
302
|
-
*/
|
|
303
|
-
struct cosine_similarity_f32x4k_t {
|
|
304
|
-
|
|
305
|
-
f32_t operator()(f32_t const* a, f32_t const* b, dim_t const dim) const noexcept {
|
|
306
|
-
#if defined(__AVX2__)
|
|
307
|
-
__m128 ab_vec = _mm_set1_ps(0);
|
|
308
|
-
__m128 a2_vec = _mm_set1_ps(0);
|
|
309
|
-
__m128 b2_vec = _mm_set1_ps(0);
|
|
310
|
-
for (dim_t i = 0; i != dim; i += 4) {
|
|
311
|
-
auto a_vec = _mm_loadu_ps(a + i);
|
|
312
|
-
auto b_vec = _mm_loadu_ps(b + i);
|
|
313
|
-
ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
314
|
-
a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
|
|
315
|
-
b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
316
|
-
}
|
|
317
|
-
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
318
|
-
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
319
|
-
a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
|
|
320
|
-
a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
|
|
321
|
-
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
322
|
-
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
323
|
-
f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
|
|
324
|
-
f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
|
|
325
|
-
f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
|
|
326
|
-
return ab_union.f / (std::sqrt(a2_union.f) * std::sqrt(b2_union.f));
|
|
327
|
-
#elif defined(__ARM_NEON)
|
|
328
|
-
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
329
|
-
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
330
|
-
float32x4_t b2_vec = vdupq_n_f32(0);
|
|
331
|
-
for (dim_t i = 0; i != dim; i += 4) {
|
|
332
|
-
auto a_vec = vld1q_f32(a + i);
|
|
333
|
-
auto b_vec = vld1q_f32(b + i);
|
|
334
|
-
ab_vec = vmlaq_f32(ab_vec, a_vec, b_vec);
|
|
335
|
-
a2_vec = vmlaq_f32(a2_vec, a_vec, a_vec);
|
|
336
|
-
b2_vec = vmlaq_f32(b2_vec, b_vec, b_vec);
|
|
337
|
-
}
|
|
338
|
-
auto ab = vaddvq_f32(ab_vec);
|
|
339
|
-
auto a2 = vaddvq_f32(a2_vec);
|
|
340
|
-
auto b2 = vaddvq_f32(b2_vec);
|
|
341
|
-
return ab / (std::sqrt(a2) * std::sqrt(b2));
|
|
342
|
-
#else
|
|
343
|
-
return cosine_similarity_t{}(a, b, dim);
|
|
344
|
-
#endif
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* @brief Dot product on vectors quantized into the [-100, 100] interval.
|
|
350
|
-
*/
|
|
351
|
-
struct dot_product_i8x16k_t {
|
|
352
|
-
|
|
353
|
-
i32_t operator()(i8_t const* a, i8_t const* b, dim_t const dim) const noexcept {
|
|
354
|
-
#if defined(__AVX2__)
|
|
355
|
-
__m256i ab_vec = _mm256_set1_epi16(0);
|
|
356
|
-
for (dim_t i = 0; i != dim; i += 4)
|
|
357
|
-
ab_vec = _mm256_add_epi16( //
|
|
358
|
-
ab_vec, //
|
|
359
|
-
_mm256_mullo_epi16( //
|
|
360
|
-
_mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(a + i))), //
|
|
361
|
-
_mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(b + i)))));
|
|
362
|
-
ab_vec = _mm256_hadd_epi16(ab_vec, ab_vec);
|
|
363
|
-
ab_vec = _mm256_hadd_epi16(ab_vec, ab_vec);
|
|
364
|
-
ab_vec = _mm256_hadd_epi16(ab_vec, ab_vec);
|
|
365
|
-
return (_mm256_cvtsi256_si32(ab_vec) & 0xFF);
|
|
366
|
-
#else
|
|
367
|
-
return 0;
|
|
368
|
-
#endif
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* @brief Bitwise Hamming distance on 128-bit long words.
|
|
374
|
-
* @return Integer number of different bits ∈ [0, dim).
|
|
375
|
-
*/
|
|
376
|
-
struct hamming_bits_distance_u1x128k_t {
|
|
377
|
-
|
|
378
|
-
template <typename at> //
|
|
379
|
-
dim_t operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
380
|
-
auto words = dim / 128;
|
|
381
|
-
|
|
382
|
-
#if defined(__AVX512VPOPCNTDQ__)
|
|
383
|
-
auto a64 = reinterpret_cast<u64_t const*>(a);
|
|
384
|
-
auto b64 = reinterpret_cast<u64_t const*>(b);
|
|
385
|
-
/// Contains 2x 64-bit integers with running population count sums.
|
|
386
|
-
__m128i d_vec = _mm_set_epi64x(0, 0);
|
|
387
|
-
for (dim_t i = 0; i != words; i += 2)
|
|
388
|
-
d_vec = _mm_add_epi64( //
|
|
389
|
-
d_vec, //
|
|
390
|
-
_mm_popcnt_epi64( //
|
|
391
|
-
_mm_xor_si128( //
|
|
392
|
-
_mm_load_si128(reinterpret_cast<__m128i const*>(a64 + i)),
|
|
393
|
-
_mm_load_si128(reinterpret_cast<__m128i const*>(b64 + i)))));
|
|
394
|
-
dim_t d = _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
|
|
395
|
-
return d;
|
|
396
|
-
#elif defined(__ARM_NEON)
|
|
397
|
-
auto a8 = reinterpret_cast<u8_t const*>(a);
|
|
398
|
-
auto b8 = reinterpret_cast<u8_t const*>(b);
|
|
399
|
-
/// Contains 16x 8-bit integers with running population count sums.
|
|
400
|
-
uint8x16_t d_vec = vdupq_n_u8(0);
|
|
401
|
-
for (dim_t i = 0; i != dim; i += 16) {
|
|
402
|
-
auto a_vec = vld1q_u8(a8 + i);
|
|
403
|
-
auto b_vec = vld1q_u8(b8 + i);
|
|
404
|
-
auto a_xor_b_vec = veorq_u8(a_vec, b_vec);
|
|
405
|
-
d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
|
|
406
|
-
}
|
|
407
|
-
return vaddvq_u8(d_vec);
|
|
408
|
-
#else
|
|
409
|
-
auto a64 = reinterpret_cast<u64_t const*>(a);
|
|
410
|
-
auto b64 = reinterpret_cast<u64_t const*>(b);
|
|
411
|
-
dim_t d_odd = 0, d_even = 0;
|
|
412
|
-
for (dim_t i = 0; i != words; i += 2)
|
|
413
|
-
d_even += hamming_bits_distance_t::popcount(a64[i] ^ b64[i]),
|
|
414
|
-
d_odd += hamming_bits_distance_t::popcount(a64[i + 1] ^ b64[i + 1]);
|
|
415
|
-
return d_odd + d_even;
|
|
416
|
-
#endif
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
template <typename similarity_measure_at> //
|
|
421
|
-
struct cosine_distance_from_similarity_gt {
|
|
422
|
-
template <typename at> //
|
|
423
|
-
inline at operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
424
|
-
return 1 - similarity_measure_at{}(a, b, dim);
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
using cosine_distance_t = cosine_distance_from_similarity_gt<cosine_similarity_t>;
|
|
429
|
-
using cosine_distance_f32x4k_t = cosine_distance_from_similarity_gt<cosine_similarity_f32x4k_t>;
|
|
430
|
-
|
|
431
|
-
template <typename distance_measure_at> //
|
|
432
|
-
struct hamming_bits_similarity_from_distance_gt {
|
|
433
|
-
template <typename at> //
|
|
434
|
-
inline f32_t operator()(at const* a, at const* b, dim_t const dim) const noexcept {
|
|
435
|
-
return 1.f - distance_measure_at{}(a, b, dim) * 1.f / dim;
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
using hamming_bits_similarity_t = hamming_bits_similarity_from_distance_gt<hamming_bits_distance_t>;
|
|
440
|
-
using hamming_bits_similarity_u1x128k_t = hamming_bits_similarity_from_distance_gt<hamming_bits_distance_u1x128k_t>;
|
|
441
|
-
|
|
442
|
-
} // namespace simsimd
|
|
443
|
-
} // namespace av
|