usearch 2.1.2 → 2.2.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/.github/workflows/prerelease.yml +18 -3
- package/CMakeLists.txt +15 -0
- package/Cargo.toml +1 -1
- package/README.md +2 -2
- package/conanfile.py +1 -1
- package/include/usearch/index_plugins.hpp +50 -6
- package/package.json +1 -1
- package/python/lib.cpp +11 -0
- package/python/usearch/index.py +17 -4
- package/setup.py +3 -4
- package/simsimd/.github/workflows/prerelease.yml +42 -9
- package/simsimd/.github/workflows/release.yml +15 -11
- package/simsimd/.vscode/settings.json +5 -1
- package/simsimd/CMakeLists.txt +5 -2
- package/simsimd/README.md +69 -48
- package/simsimd/bench.cxx +5 -6
- package/simsimd/include/simsimd/simsimd.h +300 -140
- package/simsimd/include/simsimd/simsimd_bio.h +54 -0
- package/simsimd/include/simsimd/simsimd_chem.h +65 -24
- package/simsimd/include/simsimd/simsimd_text.h +92 -0
- package/simsimd/package.json +1 -1
- package/simsimd/pyproject.toml +2 -1
- package/simsimd/python/{python.c → lib.c} +44 -15
- package/simsimd/python/test.py +35 -20
- package/simsimd/setup.py +2 -6
- package/wasmer.toml +1 -1
- /package/{cluster.ipynb → python/scripts/cluster.ipynb} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @brief Collection of Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
|
|
3
3
|
*
|
|
4
|
-
* @author
|
|
4
|
+
* @author Ash Vardanian
|
|
5
5
|
* @date March 14, 2023
|
|
6
6
|
*
|
|
7
7
|
* x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
|
|
@@ -14,23 +14,82 @@
|
|
|
14
14
|
#include <stddef.h> // `size_t`
|
|
15
15
|
#include <stdint.h>
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
#include <
|
|
17
|
+
#ifdef _MSC_VER
|
|
18
|
+
#include <intrin.h>
|
|
19
|
+
#define popcount32 __popcnt
|
|
20
|
+
#define popcount64 __popcnt64
|
|
21
|
+
#else
|
|
22
|
+
#define popcount32 __builtin_popcount
|
|
23
|
+
#define popcount64 __builtin_popcountll
|
|
19
24
|
#endif
|
|
20
25
|
|
|
26
|
+
// Compiling for Arm: SIMSIMD_TARGET_ARM
|
|
27
|
+
#if defined(__aarch64__) || defined(_M_ARM64)
|
|
28
|
+
#define SIMSIMD_TARGET_ARM 1
|
|
29
|
+
|
|
30
|
+
// Compiling for Arm: SIMSIMD_TARGET_ARM_NEON
|
|
31
|
+
#if !defined(SIMSIMD_TARGET_ARM_NEON)
|
|
21
32
|
#if defined(__ARM_NEON)
|
|
22
|
-
#
|
|
33
|
+
#define SIMSIMD_TARGET_ARM_NEON 1
|
|
34
|
+
#else
|
|
35
|
+
#define SIMSIMD_TARGET_ARM_NEON 0
|
|
36
|
+
#endif
|
|
23
37
|
#endif
|
|
24
38
|
|
|
39
|
+
// Compiling for Arm: SIMSIMD_TARGET_ARM_SVE
|
|
40
|
+
#if !defined(SIMSIMD_TARGET_ARM_SVE)
|
|
25
41
|
#if defined(__ARM_FEATURE_SVE)
|
|
26
|
-
#
|
|
42
|
+
#define SIMSIMD_TARGET_ARM_SVE 1
|
|
43
|
+
#else
|
|
44
|
+
#define SIMSIMD_TARGET_ARM_SVE 0
|
|
45
|
+
#endif
|
|
27
46
|
#endif
|
|
28
47
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#define
|
|
48
|
+
#define SIMSIMD_TARGET_X86 0
|
|
49
|
+
#define SIMSIMD_TARGET_X86_AVX2 0
|
|
50
|
+
#define SIMSIMD_TARGET_X86_AVX512 0
|
|
51
|
+
|
|
52
|
+
// Compiling for x86: SIMSIMD_TARGET_X86
|
|
53
|
+
#elif defined(__x86_64__) || defined(_M_X64)
|
|
54
|
+
#define SIMSIMD_TARGET_X86 1
|
|
55
|
+
|
|
56
|
+
// Compiling for x86: SIMSIMD_TARGET_X86_AVX2
|
|
57
|
+
#if !defined(SIMSIMD_TARGET_X86_AVX2)
|
|
58
|
+
#if defined(__AVX2__)
|
|
59
|
+
#define SIMSIMD_TARGET_X86_AVX2 1
|
|
32
60
|
#else
|
|
33
|
-
#define
|
|
61
|
+
#define SIMSIMD_TARGET_X86_AVX2 0
|
|
62
|
+
#endif
|
|
63
|
+
#endif
|
|
64
|
+
|
|
65
|
+
// Compiling for x86: SIMSIMD_TARGET_X86_AVX512
|
|
66
|
+
#if !defined(SIMSIMD_TARGET_X86_AVX512)
|
|
67
|
+
#if defined(__AVX512F__) || defined(__AVX512VPOPCNTDQ__)
|
|
68
|
+
#define SIMSIMD_TARGET_X86_AVX512 1
|
|
69
|
+
#else
|
|
70
|
+
#define SIMSIMD_TARGET_X86_AVX512 0
|
|
71
|
+
#endif
|
|
72
|
+
#endif
|
|
73
|
+
|
|
74
|
+
#define SIMSIMD_TARGET_ARM 0
|
|
75
|
+
#define SIMSIMD_TARGET_ARM_NEON 0
|
|
76
|
+
#define SIMSIMD_TARGET_ARM_SVE 0
|
|
77
|
+
|
|
78
|
+
// Compiling for unknown hardware architecture
|
|
79
|
+
#else
|
|
80
|
+
#error Unknown hardware architecture!
|
|
81
|
+
#endif
|
|
82
|
+
|
|
83
|
+
#if SIMSIMD_TARGET_ARM_NEON
|
|
84
|
+
#include <arm_neon.h>
|
|
85
|
+
#endif
|
|
86
|
+
|
|
87
|
+
#if SIMSIMD_TARGET_ARM_SVE
|
|
88
|
+
#include <arm_sve.h>
|
|
89
|
+
#endif
|
|
90
|
+
|
|
91
|
+
#if SIMSIMD_TARGET_X86_AVX2 || SIMSIMD_TARGET_X86_AVX512
|
|
92
|
+
#include <immintrin.h>
|
|
34
93
|
#endif
|
|
35
94
|
|
|
36
95
|
#ifdef __cplusplus
|
|
@@ -46,8 +105,12 @@ union simsimd_f32i32_t {
|
|
|
46
105
|
float f;
|
|
47
106
|
};
|
|
48
107
|
|
|
49
|
-
|
|
50
|
-
#if
|
|
108
|
+
#if SIMSIMD_TARGET_ARM
|
|
109
|
+
#if SIMSIMD_TARGET_ARM_SVE
|
|
110
|
+
|
|
111
|
+
__attribute__((target("+sve"))) inline static simsimd_f32_t //
|
|
112
|
+
simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
113
|
+
|
|
51
114
|
size_t i = 0;
|
|
52
115
|
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
53
116
|
svbool_t pg_vec = svwhilelt_b32(i, d);
|
|
@@ -59,14 +122,10 @@ inline static simsimd_f32_t simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_
|
|
|
59
122
|
pg_vec = svwhilelt_b32(i, d);
|
|
60
123
|
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
61
124
|
return 1 - svaddv_f32(svptrue_b32(), ab_vec);
|
|
62
|
-
#else
|
|
63
|
-
(void)a, (void)b, (void)d;
|
|
64
|
-
return -1;
|
|
65
|
-
#endif
|
|
66
125
|
}
|
|
67
126
|
|
|
68
|
-
inline static simsimd_f32_t
|
|
69
|
-
|
|
127
|
+
__attribute__((target("+sve"))) inline static simsimd_f32_t //
|
|
128
|
+
simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
70
129
|
size_t i = 0;
|
|
71
130
|
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
72
131
|
svfloat32_t a2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
@@ -85,14 +144,10 @@ inline static simsimd_f32_t simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_
|
|
|
85
144
|
simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
|
|
86
145
|
simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
|
|
87
146
|
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
88
|
-
#else
|
|
89
|
-
(void)a, (void)b, (void)d;
|
|
90
|
-
return -1;
|
|
91
|
-
#endif
|
|
92
147
|
}
|
|
93
148
|
|
|
94
|
-
inline static simsimd_f32_t
|
|
95
|
-
|
|
149
|
+
__attribute__((target("+sve"))) inline static simsimd_f32_t //
|
|
150
|
+
simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
96
151
|
size_t i = 0;
|
|
97
152
|
svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
98
153
|
svbool_t pg_vec = svwhilelt_b32(i, d);
|
|
@@ -105,15 +160,11 @@ inline static simsimd_f32_t simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd
|
|
|
105
160
|
pg_vec = svwhilelt_b32(i, d);
|
|
106
161
|
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
107
162
|
simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_vec);
|
|
108
|
-
return
|
|
109
|
-
#else
|
|
110
|
-
(void)a, (void)b, (void)d;
|
|
111
|
-
return -1;
|
|
112
|
-
#endif
|
|
163
|
+
return d2;
|
|
113
164
|
}
|
|
114
165
|
|
|
115
|
-
inline static simsimd_f32_t
|
|
116
|
-
|
|
166
|
+
__attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
|
|
167
|
+
simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
|
|
117
168
|
size_t i = 0;
|
|
118
169
|
svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
119
170
|
svfloat16_t a2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
@@ -134,14 +185,10 @@ inline static simsimd_f32_t simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, sim
|
|
|
134
185
|
simsimd_f16_t a2 = svaddv_f16(svptrue_b16(), a2_vec);
|
|
135
186
|
simsimd_f16_t b2 = svaddv_f16(svptrue_b16(), b2_vec);
|
|
136
187
|
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
137
|
-
#else
|
|
138
|
-
(void)a_enum, (void)b_enum, (void)d;
|
|
139
|
-
return -1;
|
|
140
|
-
#endif
|
|
141
188
|
}
|
|
142
189
|
|
|
143
|
-
inline static simsimd_f32_t
|
|
144
|
-
|
|
190
|
+
__attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
|
|
191
|
+
simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
|
|
145
192
|
size_t i = 0;
|
|
146
193
|
svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
147
194
|
svbool_t pg_vec = svwhilelt_b16(i, d);
|
|
@@ -155,16 +202,12 @@ inline static simsimd_f32_t simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, si
|
|
|
155
202
|
i += svcnth();
|
|
156
203
|
pg_vec = svwhilelt_b16(i, d);
|
|
157
204
|
} while (svptest_any(svptrue_b16(), pg_vec));
|
|
158
|
-
float16_t
|
|
159
|
-
return 1 -
|
|
160
|
-
#else
|
|
161
|
-
(void)a_enum, (void)b_enum, (void)d;
|
|
162
|
-
return -1;
|
|
163
|
-
#endif
|
|
205
|
+
float16_t d2_f16 = svaddv_f16(svptrue_b16(), d2_vec);
|
|
206
|
+
return 1 - d2_f16;
|
|
164
207
|
}
|
|
165
208
|
|
|
166
|
-
inline static simsimd_f32_t
|
|
167
|
-
|
|
209
|
+
__attribute__((target("+sve"))) inline static simsimd_f32_t //
|
|
210
|
+
simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
168
211
|
size_t i = 0;
|
|
169
212
|
svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
170
213
|
svbool_t pg_vec = svwhilelt_b8(i, d);
|
|
@@ -177,26 +220,24 @@ inline static simsimd_f32_t simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t c
|
|
|
177
220
|
pg_vec = svwhilelt_b32(i, d);
|
|
178
221
|
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
179
222
|
return 1 - svaddv_u8(svptrue_b32(), d_vec);
|
|
180
|
-
#else
|
|
181
|
-
(void)a, (void)b, (void)d;
|
|
182
|
-
return -1;
|
|
183
|
-
#endif
|
|
184
223
|
}
|
|
185
224
|
|
|
186
|
-
|
|
187
|
-
|
|
225
|
+
#endif // SIMSIMD_TARGET_ARM_SVE
|
|
226
|
+
|
|
227
|
+
#if SIMSIMD_TARGET_ARM_NEON
|
|
228
|
+
|
|
229
|
+
__attribute__((target("+simd"))) inline static simsimd_f32_t //
|
|
230
|
+
simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
231
|
+
|
|
188
232
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
189
233
|
for (size_t i = 0; i != d; i += 4)
|
|
190
234
|
ab_vec = vfmaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
|
|
191
235
|
return 1 - vaddvq_f32(ab_vec);
|
|
192
|
-
#else
|
|
193
|
-
(void)a, (void)b, (void)d;
|
|
194
|
-
return -1;
|
|
195
|
-
#endif
|
|
196
236
|
}
|
|
197
237
|
|
|
198
|
-
inline static simsimd_f32_t
|
|
199
|
-
|
|
238
|
+
__attribute__((target("+simd+fp16"))) inline static simsimd_f32_t //
|
|
239
|
+
simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
240
|
+
|
|
200
241
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
201
242
|
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
202
243
|
float32x4_t b2_vec = vdupq_n_f32(0);
|
|
@@ -212,41 +253,36 @@ inline static simsimd_f32_t simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsi
|
|
|
212
253
|
simsimd_f32_t a2 = vaddvq_f32(a2_vec);
|
|
213
254
|
simsimd_f32_t b2 = vaddvq_f32(b2_vec);
|
|
214
255
|
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
215
|
-
#else
|
|
216
|
-
(void)a, (void)b, (void)d;
|
|
217
|
-
return -1;
|
|
218
|
-
#endif
|
|
219
256
|
}
|
|
220
257
|
|
|
221
|
-
inline static simsimd_f32_t
|
|
222
|
-
|
|
258
|
+
__attribute__((target("+simd"))) inline static simsimd_f32_t //
|
|
259
|
+
simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
|
|
260
|
+
|
|
223
261
|
int32x4_t ab_vec = vdupq_n_s32(0);
|
|
224
262
|
int32x4_t a2_vec = vdupq_n_s32(0);
|
|
225
263
|
int32x4_t b2_vec = vdupq_n_s32(0);
|
|
226
264
|
|
|
227
|
-
|
|
228
|
-
for (size_t i = 0; i != d; i += 16) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
#else
|
|
265
|
+
// This 128-bit `vdot_s32` intrinsic is often unavailable, so we use the 64-bit `vdot_s32`.
|
|
266
|
+
// for (size_t i = 0; i != d; i += 16) {
|
|
267
|
+
// int8x16_t a_vec = vld1q_s8(a + i);
|
|
268
|
+
// int8x16_t b_vec = vld1q_s8(b + i);
|
|
269
|
+
// ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
|
|
270
|
+
// a2_vec = vdotq_s32(a2_vec, a_vec, a_vec);
|
|
271
|
+
// b2_vec = vdotq_s32(b2_vec, b_vec, b_vec);
|
|
272
|
+
// }
|
|
236
273
|
for (size_t i = 0; i != d; i += 8) {
|
|
237
274
|
int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
|
|
238
275
|
int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
|
|
239
276
|
int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
|
|
240
277
|
int16x8_t a2_part_vec = vmulq_s16(a_vec, a_vec);
|
|
241
278
|
int16x8_t b2_part_vec = vmulq_s16(b_vec, b_vec);
|
|
242
|
-
ab_vec = //
|
|
243
|
-
|
|
244
|
-
a2_vec = //
|
|
245
|
-
|
|
246
|
-
b2_vec = //
|
|
247
|
-
|
|
279
|
+
ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
|
|
280
|
+
vmovl_s16(vget_low_s16(ab_part_vec))));
|
|
281
|
+
a2_vec = vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), //
|
|
282
|
+
vmovl_s16(vget_low_s16(a2_part_vec))));
|
|
283
|
+
b2_vec = vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), //
|
|
284
|
+
vmovl_s16(vget_low_s16(b2_part_vec))));
|
|
248
285
|
}
|
|
249
|
-
#endif
|
|
250
286
|
|
|
251
287
|
int32x2_t ab_part = vadd_s32(vget_high_s32(ab_vec), vget_low_s32(ab_vec));
|
|
252
288
|
int32_t ab = vget_lane_s32(vpadd_s32(ab_part, ab_part), 0);
|
|
@@ -255,14 +291,29 @@ inline static simsimd_f32_t simsimd_cos_i8x16_neon(int8_t const* a, int8_t const
|
|
|
255
291
|
int32x2_t b2_part = vadd_s32(vget_high_s32(b2_vec), vget_low_s32(b2_vec));
|
|
256
292
|
int32_t b2 = vget_lane_s32(vpadd_s32(b2_part, b2_part), 0);
|
|
257
293
|
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
258
|
-
#else
|
|
259
|
-
(void)a, (void)b, (void)d;
|
|
260
|
-
return -1;
|
|
261
|
-
#endif
|
|
262
294
|
}
|
|
263
295
|
|
|
264
|
-
inline static simsimd_f32_t
|
|
265
|
-
|
|
296
|
+
__attribute__((target("+simd"))) inline static simsimd_f32_t //
|
|
297
|
+
simsimd_l2sq_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
|
|
298
|
+
|
|
299
|
+
int32x4_t d2_vec = vdupq_n_s32(0);
|
|
300
|
+
for (size_t i = 0; i != d; i += 8) {
|
|
301
|
+
int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
|
|
302
|
+
int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
|
|
303
|
+
int16x8_t d2_part_vec = vsubq_s16(a_vec, b_vec);
|
|
304
|
+
d2_part_vec = vmulq_s16(d2_part_vec, d2_part_vec);
|
|
305
|
+
d2_vec = vaddq_s32(d2_vec, vaddq_s32(vmovl_s16(vget_high_s16(d2_part_vec)), //
|
|
306
|
+
vmovl_s16(vget_low_s16(d2_part_vec))));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
int32x2_t d2_part = vadd_s32(vget_high_s32(d2_vec), vget_low_s32(d2_vec));
|
|
310
|
+
int32_t d2 = vget_lane_s32(vpadd_s32(d2_part, d2_part), 0);
|
|
311
|
+
return d2;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
__attribute__((target("+simd"))) inline static simsimd_f32_t //
|
|
315
|
+
simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
316
|
+
|
|
266
317
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
267
318
|
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
268
319
|
float32x4_t b2_vec = vdupq_n_f32(0);
|
|
@@ -277,14 +328,11 @@ inline static simsimd_f32_t simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsi
|
|
|
277
328
|
simsimd_f32_t a2 = vaddvq_f32(a2_vec);
|
|
278
329
|
simsimd_f32_t b2 = vaddvq_f32(b2_vec);
|
|
279
330
|
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
280
|
-
#else
|
|
281
|
-
(void)a, (void)b, (void)d;
|
|
282
|
-
return -1;
|
|
283
|
-
#endif
|
|
284
331
|
}
|
|
285
332
|
|
|
286
|
-
inline static simsimd_f32_t
|
|
287
|
-
|
|
333
|
+
__attribute__((target("+sve"))) inline static simsimd_f32_t //
|
|
334
|
+
simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
335
|
+
|
|
288
336
|
/// Contains 16x 8-bit integers with running population count sums.
|
|
289
337
|
uint8x16_t d_vec = vdupq_n_u8(0);
|
|
290
338
|
for (size_t i = 0; i != d; i += 16) {
|
|
@@ -294,52 +342,30 @@ inline static simsimd_f32_t simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t
|
|
|
294
342
|
d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
|
|
295
343
|
}
|
|
296
344
|
return 1 - vaddvq_u8(d_vec);
|
|
297
|
-
#else
|
|
298
|
-
(void)a, (void)b, (void)d;
|
|
299
|
-
return -1;
|
|
300
|
-
#endif
|
|
301
345
|
}
|
|
346
|
+
#endif // SIMSIMD_TARGET_ARM_NEON
|
|
347
|
+
#endif // SIMSIMD_TARGET_ARM
|
|
302
348
|
|
|
303
|
-
|
|
304
|
-
#if
|
|
349
|
+
#if SIMSIMD_TARGET_X86
|
|
350
|
+
#if SIMSIMD_TARGET_X86_AVX2
|
|
351
|
+
|
|
352
|
+
__attribute__((target("avx2"))) //
|
|
353
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
354
|
+
simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
305
355
|
__m128 ab_vec = _mm_set1_ps(0);
|
|
306
356
|
for (size_t i = 0; i != d; i += 4)
|
|
307
357
|
ab_vec = _mm_fmadd_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i), ab_vec);
|
|
308
358
|
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
309
359
|
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
310
|
-
simsimd_f32i32_t ab_union;
|
|
360
|
+
union simsimd_f32i32_t ab_union;
|
|
311
361
|
ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
|
|
312
362
|
return 1 - ab_union.f;
|
|
313
|
-
#else
|
|
314
|
-
(void)a, (void)b, (void)d;
|
|
315
|
-
return -1;
|
|
316
|
-
#endif
|
|
317
363
|
}
|
|
318
364
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
__m512 a2_vec = _mm512_set1_ps(0);
|
|
323
|
-
__m512 b2_vec = _mm512_set1_ps(0);
|
|
324
|
-
for (size_t i = 0; i != d; i += 16) {
|
|
325
|
-
__m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
|
|
326
|
-
__m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
|
|
327
|
-
ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
328
|
-
a2_vec = _mm512_fmadd_ps(a_vec, a_vec, a2_vec);
|
|
329
|
-
b2_vec = _mm512_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
330
|
-
}
|
|
331
|
-
simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
|
|
332
|
-
simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
|
|
333
|
-
simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
|
|
334
|
-
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
335
|
-
#else
|
|
336
|
-
(void)a, (void)b, (void)d;
|
|
337
|
-
return -1;
|
|
338
|
-
#endif
|
|
339
|
-
}
|
|
365
|
+
__attribute__((target("avx2"))) //
|
|
366
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
367
|
+
simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
340
368
|
|
|
341
|
-
inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
342
|
-
#if defined(__AVX2__)
|
|
343
369
|
__m128 ab_vec = _mm_set1_ps(0);
|
|
344
370
|
__m128 a2_vec = _mm_set1_ps(0);
|
|
345
371
|
__m128 b2_vec = _mm_set1_ps(0);
|
|
@@ -356,18 +382,143 @@ inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsi
|
|
|
356
382
|
a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
|
|
357
383
|
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
358
384
|
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
359
|
-
simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
|
|
360
|
-
simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
|
|
361
|
-
simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
|
|
385
|
+
union simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
|
|
386
|
+
union simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
|
|
387
|
+
union simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
|
|
362
388
|
return 1 - ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
|
|
363
|
-
#else
|
|
364
|
-
(void)a, (void)b, (void)d;
|
|
365
|
-
return -1;
|
|
366
|
-
#endif
|
|
367
389
|
}
|
|
368
390
|
|
|
369
|
-
|
|
370
|
-
|
|
391
|
+
__attribute__((target("avx2"))) //
|
|
392
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
393
|
+
simsimd_l2sq_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
394
|
+
__m128 sum_vec = _mm_set1_ps(0);
|
|
395
|
+
for (size_t i = 0; i != d; i += 4) {
|
|
396
|
+
__m128 diff_vec = _mm_sub_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i));
|
|
397
|
+
sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
|
|
398
|
+
}
|
|
399
|
+
sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
|
|
400
|
+
sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
|
|
401
|
+
float result[1];
|
|
402
|
+
_mm_store_ss(result, sum_vec);
|
|
403
|
+
return result[0];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
__attribute__((target("avx2"))) //
|
|
407
|
+
__attribute__((target("f16c"))) //
|
|
408
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
409
|
+
simsimd_dot_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
410
|
+
__m128 ab_vec = _mm_set1_ps(0);
|
|
411
|
+
for (size_t i = 0; i != d; i += 8) {
|
|
412
|
+
__m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
|
|
413
|
+
__m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
|
|
414
|
+
ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
415
|
+
}
|
|
416
|
+
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
417
|
+
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
418
|
+
float result[1];
|
|
419
|
+
_mm_store_ss(result, ab_vec);
|
|
420
|
+
return 1 - result[0];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
__attribute__((target("avx2"))) //
|
|
424
|
+
__attribute__((target("f16c"))) //
|
|
425
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
426
|
+
simsimd_cos_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
427
|
+
__m128 ab_vec = _mm_set1_ps(0);
|
|
428
|
+
__m128 a2_vec = _mm_set1_ps(0);
|
|
429
|
+
__m128 b2_vec = _mm_set1_ps(0);
|
|
430
|
+
for (size_t i = 0; i != d; i += 8) {
|
|
431
|
+
__m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
|
|
432
|
+
__m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
|
|
433
|
+
ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
434
|
+
a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
|
|
435
|
+
b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
436
|
+
}
|
|
437
|
+
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
438
|
+
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
439
|
+
a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
|
|
440
|
+
a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
|
|
441
|
+
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
442
|
+
b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
|
|
443
|
+
float ab_result[1], a2_result[1], b2_result[1];
|
|
444
|
+
_mm_store_ss(ab_result, ab_vec);
|
|
445
|
+
_mm_store_ss(a2_result, a2_vec);
|
|
446
|
+
_mm_store_ss(b2_result, b2_vec);
|
|
447
|
+
return 1 - ab_result[0] / (sqrt(a2_result[0]) * sqrt(b2_result[0]));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
__attribute__((target("avx2"))) //
|
|
451
|
+
__attribute__((target("f16c"))) //
|
|
452
|
+
__attribute__((target("fma"))) inline static simsimd_f32_t //
|
|
453
|
+
simsimd_l2sq_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
454
|
+
__m128 sum_vec = _mm_set1_ps(0);
|
|
455
|
+
for (size_t i = 0; i != d; i += 8) {
|
|
456
|
+
__m128 a_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
|
|
457
|
+
__m128 b_vec = _mm_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
|
|
458
|
+
__m128 diff_vec = _mm_sub_ps(a_vec, b_vec);
|
|
459
|
+
sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
|
|
460
|
+
}
|
|
461
|
+
sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
|
|
462
|
+
sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
|
|
463
|
+
float result[1];
|
|
464
|
+
_mm_store_ss(result, sum_vec);
|
|
465
|
+
return result[0];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
#endif // SIMSIMD_TARGET_X86_AVX2
|
|
469
|
+
|
|
470
|
+
#if SIMSIMD_TARGET_X86_AVX512
|
|
471
|
+
|
|
472
|
+
__attribute__((target("avx512fp16"))) //
|
|
473
|
+
__attribute__((target("avx512f"))) inline static simsimd_f32_t //
|
|
474
|
+
simsimd_dot_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
475
|
+
__m512 ab_vec = _mm512_set1_ps(0);
|
|
476
|
+
for (size_t i = 0; i != d; i += 16) {
|
|
477
|
+
__m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
|
|
478
|
+
__m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
|
|
479
|
+
ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
480
|
+
}
|
|
481
|
+
simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
|
|
482
|
+
return 1 - ab;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
__attribute__((target("avx512fp16"))) //
|
|
486
|
+
__attribute__((target("avx512f"))) inline static simsimd_f32_t //
|
|
487
|
+
simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
488
|
+
__m512 ab_vec = _mm512_set1_ps(0);
|
|
489
|
+
__m512 a2_vec = _mm512_set1_ps(0);
|
|
490
|
+
__m512 b2_vec = _mm512_set1_ps(0);
|
|
491
|
+
for (size_t i = 0; i != d; i += 16) {
|
|
492
|
+
__m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
|
|
493
|
+
__m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
|
|
494
|
+
ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
495
|
+
a2_vec = _mm512_fmadd_ps(a_vec, a_vec, a2_vec);
|
|
496
|
+
b2_vec = _mm512_fmadd_ps(b_vec, b_vec, b2_vec);
|
|
497
|
+
}
|
|
498
|
+
simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
|
|
499
|
+
simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
|
|
500
|
+
simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
|
|
501
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
__attribute__((target("avx512fp16"))) //
|
|
505
|
+
__attribute__((target("avx512f"))) inline static simsimd_f32_t //
|
|
506
|
+
simsimd_l2sq_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
507
|
+
__m512 sum_vec = _mm512_set1_ps(0);
|
|
508
|
+
for (size_t i = 0; i != d; i += 16) {
|
|
509
|
+
__m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
|
|
510
|
+
__m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
|
|
511
|
+
__m512 diff_vec = _mm512_sub_ps(a_vec, b_vec);
|
|
512
|
+
sum_vec = _mm512_fmadd_ps(diff_vec, diff_vec, sum_vec);
|
|
513
|
+
}
|
|
514
|
+
simsimd_f32_t sum = _mm512_reduce_add_ps(sum_vec);
|
|
515
|
+
return sum;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
__attribute__((target("avx512vpopcntdq"))) //
|
|
519
|
+
__attribute__((target("avx512vl"))) //
|
|
520
|
+
__attribute__((target("avx512f"))) inline static simsimd_f32_t //
|
|
521
|
+
simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
371
522
|
size_t words = d / 128;
|
|
372
523
|
uint64_t const* a64 = (uint64_t const*)(a);
|
|
373
524
|
uint64_t const* b64 = (uint64_t const*)(b);
|
|
@@ -380,20 +531,29 @@ inline static simsimd_f32_t simsimd_hamming_b1x128_avx512(uint8_t const* a, uint
|
|
|
380
531
|
_mm_xor_si128( //
|
|
381
532
|
_mm_load_si128((__m128i const*)(a64 + i)), _mm_load_si128((__m128i const*)(b64 + i)))));
|
|
382
533
|
return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
|
|
383
|
-
#else
|
|
384
|
-
(void)a, (void)b, (void)d;
|
|
385
|
-
return -1;
|
|
386
|
-
#endif
|
|
387
534
|
}
|
|
388
535
|
|
|
389
|
-
|
|
536
|
+
#endif // SIMSIMD_TARGET_X86_AVX512
|
|
537
|
+
#endif // SIMSIMD_TARGET_X86
|
|
538
|
+
|
|
539
|
+
inline static simsimd_f32_t //
|
|
540
|
+
simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
390
541
|
size_t and_count = 0, or_count = 0;
|
|
391
|
-
|
|
392
|
-
|
|
542
|
+
uint8_t const* a_end = a + d / 8;
|
|
543
|
+
// Misaligned prefix
|
|
544
|
+
for (; a != a_end && (size_t)(a) % 8 != 0; ++a, ++b)
|
|
545
|
+
and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
|
|
546
|
+
// Properly aligned body
|
|
547
|
+
for (; a + 8 <= a_end; a += 8, b += 8)
|
|
548
|
+
and_count += popcount64(*(uint64_t*)a & *(uint64_t*)b), or_count += popcount64(*(uint64_t*)a | *(uint64_t*)b);
|
|
549
|
+
// Misaligned suffix
|
|
550
|
+
for (; a != a_end; ++a, ++b)
|
|
551
|
+
and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
|
|
393
552
|
return 1 - (simsimd_f32_t)(and_count) / or_count;
|
|
394
553
|
}
|
|
395
554
|
|
|
396
555
|
#undef popcount32
|
|
556
|
+
#undef popcount64
|
|
397
557
|
|
|
398
558
|
#ifdef __cplusplus
|
|
399
559
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @brief Collection of Biology-oriented Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
|
|
3
|
+
*
|
|
4
|
+
* @author Ash Vardanian
|
|
5
|
+
* @date August 8, 2023
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <simsimd/simsimd_chem.h>
|
|
11
|
+
|
|
12
|
+
#ifdef _MSC_VER
|
|
13
|
+
#define hash32
|
|
14
|
+
#define hash64
|
|
15
|
+
#else
|
|
16
|
+
#define hash32
|
|
17
|
+
#define hash64
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
#ifdef __cplusplus
|
|
21
|
+
extern "C" {
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @brief Used to compute the similarity of two DNA or RNA sequences in "TwoBit" form.
|
|
26
|
+
*
|
|
27
|
+
* By design uses a separate bit-set for every string, expanding the compressed
|
|
28
|
+
* representation into bitsets, and then computes the tanimoto distance on those.
|
|
29
|
+
*
|
|
30
|
+
* Optimal `length_hash` should be derived from `max(a_length, b_length)`, as
|
|
31
|
+
*/
|
|
32
|
+
inline static simsimd_f32_t simsimd_tanimoto_twobit( //
|
|
33
|
+
uint8_t const* a_chars, uint8_t const* b_chars, //
|
|
34
|
+
size_t a_length, size_t b_length, //
|
|
35
|
+
uint8_t* a_hash, uint8_t* b_hash, size_t length_hash) {
|
|
36
|
+
|
|
37
|
+
// uint64 -> 32-gram
|
|
38
|
+
// uint32 -> 16-gram
|
|
39
|
+
uint64_t last_word = 0;
|
|
40
|
+
for (size_t i = 0; i + 8 <= a_length; i += 8) {
|
|
41
|
+
uint64_t a_word;
|
|
42
|
+
memcpy(&a_word, a_chars, 8);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
size_t b_words = (b_length + 7) / 8;
|
|
46
|
+
|
|
47
|
+
return simsimd_tanimoto_b1x8_naive(a_hash, b_hash, length_hash);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#undef popcount64
|
|
51
|
+
|
|
52
|
+
#ifdef __cplusplus
|
|
53
|
+
}
|
|
54
|
+
#endif
|