usearch 0.22.3 → 1.0.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 +3 -3
- package/.releaserc +10 -2
- package/.vscode/launch.json +10 -10
- package/.vscode/settings.json +9 -1
- package/.vscode/tasks.json +4 -4
- package/CMakeLists.txt +6 -1
- package/Cargo.toml +2 -2
- package/README.md +35 -41
- package/build.rs +2 -2
- package/c/CMakeLists.txt +48 -0
- package/c/README.md +34 -0
- package/c/lib.cpp +83 -72
- package/c/test.c +20 -20
- package/c/usearch.h +132 -32
- package/conanfile.py +1 -1
- package/cpp/CMakeLists.txt +3 -1
- package/cpp/README.md +16 -16
- package/cpp/bench.cpp +50 -126
- package/cpp/test.cpp +164 -80
- package/docs/benchmarks.md +26 -5
- package/docs/c/reference.rst +3 -3
- package/docs/compilation.md +31 -49
- package/docs/cpp/index.rst +1 -1
- package/docs/cpp/reference.rst +4 -4
- package/golang/lib.go +275 -141
- package/golang/lib_test.go +105 -0
- package/include/usearch/index.hpp +1681 -1656
- package/include/usearch/index_dense.hpp +1400 -0
- package/include/usearch/index_plugins.hpp +1263 -0
- package/java/README.md +1 -1
- package/java/cloud/unum/usearch/Index.java +10 -10
- package/java/cloud/unum/usearch/cloud_unum_usearch_Index.cpp +34 -37
- package/java/test/IndexTest.java +1 -1
- package/javascript/README.md +6 -6
- package/javascript/lib.cpp +94 -55
- package/javascript/test.js +25 -11
- package/javascript/usearch.d.ts +87 -0
- package/javascript/usearch.js +2 -118
- package/objc/USearchObjective.mm +57 -31
- package/objc/include/USearchObjective.h +32 -22
- package/package.json +5 -3
- package/python/README.md +23 -14
- package/python/lib.cpp +468 -444
- package/python/scripts/bench.ipynb +3 -2
- package/python/scripts/bench.py +4 -4
- package/python/scripts/index_faiss.py +5 -5
- package/python/scripts/join.py +4 -4
- package/python/scripts/test.py +152 -61
- package/python/scripts/test_jit.py +10 -12
- package/python/scripts/test_sparse.py +32 -0
- package/python/usearch/client.py +26 -28
- package/python/usearch/eval.py +73 -23
- package/python/usearch/index.py +520 -228
- package/python/usearch/io.py +57 -25
- package/python/usearch/numba.py +4 -4
- package/python/usearch/server.py +10 -10
- package/rust/lib.cpp +22 -27
- package/rust/lib.hpp +11 -12
- package/rust/lib.rs +3 -3
- package/setup.py +5 -2
- package/simsimd/.github/workflows/prerelease.yml +73 -0
- package/simsimd/.github/workflows/release.yml +110 -0
- package/simsimd/.github/workflows/update_version.sh +3 -0
- package/simsimd/.releaserc +77 -0
- package/simsimd/.vscode/launch.json +27 -1
- package/simsimd/.vscode/settings.json +22 -1
- package/simsimd/.vscode/tasks.json +6 -0
- package/simsimd/CMakeLists.txt +3 -2
- package/simsimd/README.md +82 -46
- package/simsimd/bench.cxx +63 -24
- package/simsimd/include/simsimd/simsimd.h +88 -66
- package/simsimd/include/simsimd/simsimd_chem.h +126 -0
- package/simsimd/package.json +8 -0
- package/simsimd/pyproject.toml +42 -0
- package/simsimd/python/python.c +81 -0
- package/simsimd/python/run.sh +1 -0
- package/simsimd/python/test.c +109 -0
- package/simsimd/python/test.py +23 -0
- package/simsimd/setup.py +44 -0
- package/swift/Index+Sugar.swift +75 -23
- package/swift/README.md +26 -2
- package/swift/Test.swift +19 -7
- package/swift/USearch.swift +2 -2
- package/tsconfig.json +8 -0
- package/wasm/CMakeLists.txt +1 -0
- package/wasmer.toml +1 -1
- package/wolfram/CMakeLists.txt +1 -0
- package/wolfram/lib.cpp +11 -13
- package/golang/usearch.h +0 -9
- package/golang/usearch_par_test.go +0 -40
- package/golang/usearch_test.go +0 -82
- package/include/usearch/index_punned_dense.hpp +0 -1001
- package/include/usearch/index_punned_helpers.hpp +0 -676
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#pragma once
|
|
13
13
|
#include <math.h> // `sqrt`
|
|
14
14
|
#include <stddef.h> // `size_t`
|
|
15
|
+
#include <stdint.h>
|
|
15
16
|
|
|
16
17
|
#if defined(__AVX2__)
|
|
17
18
|
#include <immintrin.h>
|
|
@@ -25,6 +26,13 @@
|
|
|
25
26
|
#include <arm_sve.h>
|
|
26
27
|
#endif
|
|
27
28
|
|
|
29
|
+
#ifdef _MSC_VER
|
|
30
|
+
#include <intrin.h>
|
|
31
|
+
#define popcount32 __popcnt
|
|
32
|
+
#else
|
|
33
|
+
#define popcount32 __builtin_popcount
|
|
34
|
+
#endif
|
|
35
|
+
|
|
28
36
|
#ifdef __cplusplus
|
|
29
37
|
extern "C" {
|
|
30
38
|
#endif
|
|
@@ -38,7 +46,7 @@ union simsimd_f32i32_t {
|
|
|
38
46
|
float f;
|
|
39
47
|
};
|
|
40
48
|
|
|
41
|
-
inline static simsimd_f32_t
|
|
49
|
+
inline static simsimd_f32_t simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
42
50
|
#if defined(__ARM_FEATURE_SVE)
|
|
43
51
|
size_t i = 0;
|
|
44
52
|
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
@@ -50,14 +58,14 @@ inline static simsimd_f32_t simsimd_dot_f32sve(simsimd_f32_t const* a, simsimd_f
|
|
|
50
58
|
i += svcntw();
|
|
51
59
|
pg_vec = svwhilelt_b32(i, d);
|
|
52
60
|
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
53
|
-
return svaddv_f32(svptrue_b32(), ab_vec);
|
|
61
|
+
return 1 - svaddv_f32(svptrue_b32(), ab_vec);
|
|
54
62
|
#else
|
|
55
63
|
(void)a, (void)b, (void)d;
|
|
56
|
-
return
|
|
64
|
+
return -1;
|
|
57
65
|
#endif
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
inline static simsimd_f32_t
|
|
68
|
+
inline static simsimd_f32_t simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
61
69
|
#if defined(__ARM_FEATURE_SVE)
|
|
62
70
|
size_t i = 0;
|
|
63
71
|
svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
@@ -76,14 +84,14 @@ inline static simsimd_f32_t simsimd_cos_f32sve(simsimd_f32_t const* a, simsimd_f
|
|
|
76
84
|
simsimd_f32_t ab = svaddv_f32(svptrue_b32(), ab_vec);
|
|
77
85
|
simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
|
|
78
86
|
simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
|
|
79
|
-
return ab / (sqrt(a2) * sqrt(b2));
|
|
87
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
80
88
|
#else
|
|
81
89
|
(void)a, (void)b, (void)d;
|
|
82
|
-
return
|
|
90
|
+
return -1;
|
|
83
91
|
#endif
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
inline static simsimd_f32_t
|
|
94
|
+
inline static simsimd_f32_t simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
87
95
|
#if defined(__ARM_FEATURE_SVE)
|
|
88
96
|
size_t i = 0;
|
|
89
97
|
svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
|
|
@@ -100,11 +108,39 @@ inline static simsimd_f32_t simsimd_l2sq_f32sve(simsimd_f32_t const* a, simsimd_
|
|
|
100
108
|
return sqrt(d2);
|
|
101
109
|
#else
|
|
102
110
|
(void)a, (void)b, (void)d;
|
|
103
|
-
return
|
|
111
|
+
return -1;
|
|
104
112
|
#endif
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
inline static
|
|
115
|
+
inline static simsimd_f32_t simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
|
|
116
|
+
#if defined(__ARM_FEATURE_SVE)
|
|
117
|
+
size_t i = 0;
|
|
118
|
+
svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
119
|
+
svfloat16_t a2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
120
|
+
svfloat16_t b2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
121
|
+
svbool_t pg_vec = svwhilelt_b16(i, d);
|
|
122
|
+
simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
|
|
123
|
+
simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
|
|
124
|
+
do {
|
|
125
|
+
svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
|
|
126
|
+
svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
|
|
127
|
+
ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
128
|
+
a2_vec = svmla_f16_x(pg_vec, a2_vec, a_vec, a_vec);
|
|
129
|
+
b2_vec = svmla_f16_x(pg_vec, b2_vec, b_vec, b_vec);
|
|
130
|
+
i += svcnth();
|
|
131
|
+
pg_vec = svwhilelt_b16(i, d);
|
|
132
|
+
} while (svptest_any(svptrue_b16(), pg_vec));
|
|
133
|
+
simsimd_f16_t ab = svaddv_f16(svptrue_b16(), ab_vec);
|
|
134
|
+
simsimd_f16_t a2 = svaddv_f16(svptrue_b16(), a2_vec);
|
|
135
|
+
simsimd_f16_t b2 = svaddv_f16(svptrue_b16(), b2_vec);
|
|
136
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
137
|
+
#else
|
|
138
|
+
(void)a_enum, (void)b_enum, (void)d;
|
|
139
|
+
return -1;
|
|
140
|
+
#endif
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
inline static simsimd_f32_t simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
|
|
108
144
|
#if defined(__ARM_FEATURE_SVE)
|
|
109
145
|
size_t i = 0;
|
|
110
146
|
svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
|
|
@@ -120,16 +156,14 @@ inline static simsimd_f16_t simsimd_l2sq_f16sve(simsimd_f16_t const* a_enum, sim
|
|
|
120
156
|
pg_vec = svwhilelt_b16(i, d);
|
|
121
157
|
} while (svptest_any(svptrue_b16(), pg_vec));
|
|
122
158
|
float16_t f16 = svaddv_f16(svptrue_b16(), d2_vec);
|
|
123
|
-
|
|
124
|
-
memcpy(&i16, &f16, sizeof(i16));
|
|
125
|
-
return i16;
|
|
159
|
+
return 1 - simsimd_f32_t(f16);
|
|
126
160
|
#else
|
|
127
161
|
(void)a_enum, (void)b_enum, (void)d;
|
|
128
|
-
return
|
|
162
|
+
return -1;
|
|
129
163
|
#endif
|
|
130
164
|
}
|
|
131
165
|
|
|
132
|
-
inline static
|
|
166
|
+
inline static simsimd_f32_t simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
133
167
|
#if defined(__ARM_FEATURE_SVE)
|
|
134
168
|
size_t i = 0;
|
|
135
169
|
svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
@@ -142,26 +176,26 @@ inline static size_t simsimd_hamming_b1x8sve(uint8_t const* a, uint8_t const* b,
|
|
|
142
176
|
i += svcntb() * __CHAR_BIT__;
|
|
143
177
|
pg_vec = svwhilelt_b32(i, d);
|
|
144
178
|
} while (svptest_any(svptrue_b32(), pg_vec));
|
|
145
|
-
return svaddv_u8(svptrue_b32(), d_vec);
|
|
179
|
+
return 1 - svaddv_u8(svptrue_b32(), d_vec);
|
|
146
180
|
#else
|
|
147
181
|
(void)a, (void)b, (void)d;
|
|
148
|
-
return
|
|
182
|
+
return -1;
|
|
149
183
|
#endif
|
|
150
184
|
}
|
|
151
185
|
|
|
152
|
-
inline static simsimd_f32_t
|
|
186
|
+
inline static simsimd_f32_t simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
153
187
|
#if defined(__ARM_NEON)
|
|
154
188
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
155
189
|
for (size_t i = 0; i != d; i += 4)
|
|
156
190
|
ab_vec = vfmaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
|
|
157
|
-
return vaddvq_f32(ab_vec);
|
|
191
|
+
return 1 - vaddvq_f32(ab_vec);
|
|
158
192
|
#else
|
|
159
193
|
(void)a, (void)b, (void)d;
|
|
160
|
-
return
|
|
194
|
+
return -1;
|
|
161
195
|
#endif
|
|
162
196
|
}
|
|
163
197
|
|
|
164
|
-
inline static simsimd_f32_t
|
|
198
|
+
inline static simsimd_f32_t simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
165
199
|
#if defined(__ARM_NEON)
|
|
166
200
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
167
201
|
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
@@ -177,14 +211,14 @@ inline static simsimd_f32_t simsimd_cos_f16x4neon(simsimd_f16_t const* a, simsim
|
|
|
177
211
|
simsimd_f32_t ab = vaddvq_f32(ab_vec);
|
|
178
212
|
simsimd_f32_t a2 = vaddvq_f32(a2_vec);
|
|
179
213
|
simsimd_f32_t b2 = vaddvq_f32(b2_vec);
|
|
180
|
-
return ab / (sqrt(a2) * sqrt(b2));
|
|
214
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
181
215
|
#else
|
|
182
216
|
(void)a, (void)b, (void)d;
|
|
183
|
-
return
|
|
217
|
+
return -1;
|
|
184
218
|
#endif
|
|
185
219
|
}
|
|
186
220
|
|
|
187
|
-
inline static simsimd_f32_t
|
|
221
|
+
inline static simsimd_f32_t simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
|
|
188
222
|
#if defined(__ARM_NEON)
|
|
189
223
|
int32x4_t ab_vec = vdupq_n_s32(0);
|
|
190
224
|
int32x4_t a2_vec = vdupq_n_s32(0);
|
|
@@ -205,11 +239,11 @@ inline static simsimd_f32_t simsimd_cos_i8x16neon(int8_t const* a, int8_t const*
|
|
|
205
239
|
int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
|
|
206
240
|
int16x8_t a2_part_vec = vmulq_s16(a_vec, a_vec);
|
|
207
241
|
int16x8_t b2_part_vec = vmulq_s16(b_vec, b_vec);
|
|
208
|
-
ab_vec =
|
|
242
|
+
ab_vec = //
|
|
209
243
|
vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), vmovl_s16(vget_low_s16(ab_part_vec))));
|
|
210
|
-
a2_vec =
|
|
244
|
+
a2_vec = //
|
|
211
245
|
vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), vmovl_s16(vget_low_s16(a2_part_vec))));
|
|
212
|
-
b2_vec =
|
|
246
|
+
b2_vec = //
|
|
213
247
|
vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), vmovl_s16(vget_low_s16(b2_part_vec))));
|
|
214
248
|
}
|
|
215
249
|
#endif
|
|
@@ -220,14 +254,14 @@ inline static simsimd_f32_t simsimd_cos_i8x16neon(int8_t const* a, int8_t const*
|
|
|
220
254
|
int32_t a2 = vget_lane_s32(vpadd_s32(a2_part, a2_part), 0);
|
|
221
255
|
int32x2_t b2_part = vadd_s32(vget_high_s32(b2_vec), vget_low_s32(b2_vec));
|
|
222
256
|
int32_t b2 = vget_lane_s32(vpadd_s32(b2_part, b2_part), 0);
|
|
223
|
-
return ab / (sqrt(a2) * sqrt(b2));
|
|
257
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
224
258
|
#else
|
|
225
259
|
(void)a, (void)b, (void)d;
|
|
226
|
-
return
|
|
260
|
+
return -1;
|
|
227
261
|
#endif
|
|
228
262
|
}
|
|
229
263
|
|
|
230
|
-
inline static simsimd_f32_t
|
|
264
|
+
inline static simsimd_f32_t simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
231
265
|
#if defined(__ARM_NEON)
|
|
232
266
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
233
267
|
float32x4_t a2_vec = vdupq_n_f32(0);
|
|
@@ -242,14 +276,14 @@ inline static simsimd_f32_t simsimd_cos_f32x4neon(simsimd_f32_t const* a, simsim
|
|
|
242
276
|
simsimd_f32_t ab = vaddvq_f32(ab_vec);
|
|
243
277
|
simsimd_f32_t a2 = vaddvq_f32(a2_vec);
|
|
244
278
|
simsimd_f32_t b2 = vaddvq_f32(b2_vec);
|
|
245
|
-
return ab / (sqrt(a2) * sqrt(b2));
|
|
279
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
246
280
|
#else
|
|
247
281
|
(void)a, (void)b, (void)d;
|
|
248
|
-
return
|
|
282
|
+
return -1;
|
|
249
283
|
#endif
|
|
250
284
|
}
|
|
251
285
|
|
|
252
|
-
inline static
|
|
286
|
+
inline static simsimd_f32_t simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
253
287
|
#if defined(__ARM_NEON)
|
|
254
288
|
/// Contains 16x 8-bit integers with running population count sums.
|
|
255
289
|
uint8x16_t d_vec = vdupq_n_u8(0);
|
|
@@ -259,14 +293,14 @@ inline static size_t simsimd_hamming_b1x128sve(uint8_t const* a, uint8_t const*
|
|
|
259
293
|
uint8x16_t a_xor_b_vec = veorq_u8(a_vec, b_vec);
|
|
260
294
|
d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
|
|
261
295
|
}
|
|
262
|
-
return vaddvq_u8(d_vec);
|
|
296
|
+
return 1 - vaddvq_u8(d_vec);
|
|
263
297
|
#else
|
|
264
298
|
(void)a, (void)b, (void)d;
|
|
265
|
-
return
|
|
299
|
+
return -1;
|
|
266
300
|
#endif
|
|
267
301
|
}
|
|
268
302
|
|
|
269
|
-
inline static simsimd_f32_t
|
|
303
|
+
inline static simsimd_f32_t simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
270
304
|
#if defined(__AVX2__)
|
|
271
305
|
__m128 ab_vec = _mm_set1_ps(0);
|
|
272
306
|
for (size_t i = 0; i != d; i += 4)
|
|
@@ -275,14 +309,14 @@ inline static simsimd_f32_t simsimd_dot_f32x4avx2(simsimd_f32_t const* a, simsim
|
|
|
275
309
|
ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
|
|
276
310
|
simsimd_f32i32_t ab_union;
|
|
277
311
|
ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
|
|
278
|
-
return ab_union.f;
|
|
312
|
+
return 1 - ab_union.f;
|
|
279
313
|
#else
|
|
280
314
|
(void)a, (void)b, (void)d;
|
|
281
|
-
return
|
|
315
|
+
return -1;
|
|
282
316
|
#endif
|
|
283
317
|
}
|
|
284
318
|
|
|
285
|
-
inline static simsimd_f32_t
|
|
319
|
+
inline static simsimd_f32_t simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
|
|
286
320
|
#if defined(__AVX512F__)
|
|
287
321
|
__m512 ab_vec = _mm512_set1_ps(0);
|
|
288
322
|
__m512 a2_vec = _mm512_set1_ps(0);
|
|
@@ -297,35 +331,14 @@ inline static simsimd_f32_t simsimd_cos_f16x16avx512(simsimd_f16_t const* a, sim
|
|
|
297
331
|
simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
|
|
298
332
|
simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
|
|
299
333
|
simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
|
|
300
|
-
return ab / (sqrt(a2) * sqrt(b2));
|
|
334
|
+
return 1 - ab / (sqrt(a2) * sqrt(b2));
|
|
301
335
|
#else
|
|
302
336
|
(void)a, (void)b, (void)d;
|
|
303
|
-
return
|
|
337
|
+
return -1;
|
|
304
338
|
#endif
|
|
305
339
|
}
|
|
306
340
|
|
|
307
|
-
inline static
|
|
308
|
-
#if defined(__AVX2__)
|
|
309
|
-
__m256i ab_vec = _mm256_set1_epi32(0);
|
|
310
|
-
for (size_t i = 0; i != d; i += 8) {
|
|
311
|
-
ab_vec = _mm256_add_epi32( //
|
|
312
|
-
ab_vec, _mm256_madd_epi16( //
|
|
313
|
-
_mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(a + i))),
|
|
314
|
-
_mm256_cvtepi8_epi16(_mm_loadu_si128((__m128i const*)(b + i)))));
|
|
315
|
-
}
|
|
316
|
-
ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
|
|
317
|
-
ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
|
|
318
|
-
ab_vec = _mm256_hadd_epi32(ab_vec, ab_vec);
|
|
319
|
-
simsimd_f32i32_t ab_union;
|
|
320
|
-
ab_union.f = _mm256_cvtss_f32(_mm256_castsi256_ps(ab_vec));
|
|
321
|
-
return ab_union.i;
|
|
322
|
-
#else
|
|
323
|
-
(void)a, (void)b, (void)d;
|
|
324
|
-
return 0;
|
|
325
|
-
#endif
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
inline static simsimd_f32_t simsimd_cos_f32x4avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
341
|
+
inline static simsimd_f32_t simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
|
|
329
342
|
#if defined(__AVX2__)
|
|
330
343
|
__m128 ab_vec = _mm_set1_ps(0);
|
|
331
344
|
__m128 a2_vec = _mm_set1_ps(0);
|
|
@@ -346,14 +359,14 @@ inline static simsimd_f32_t simsimd_cos_f32x4avx2(simsimd_f32_t const* a, simsim
|
|
|
346
359
|
simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
|
|
347
360
|
simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
|
|
348
361
|
simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
|
|
349
|
-
return ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
|
|
362
|
+
return 1 - ab_union.f / (sqrt(a2_union.f) * sqrt(b2_union.f));
|
|
350
363
|
#else
|
|
351
364
|
(void)a, (void)b, (void)d;
|
|
352
|
-
return
|
|
365
|
+
return -1;
|
|
353
366
|
#endif
|
|
354
367
|
}
|
|
355
368
|
|
|
356
|
-
inline static
|
|
369
|
+
inline static simsimd_f32_t simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
357
370
|
#if defined(__AVX512VPOPCNTDQ__)
|
|
358
371
|
size_t words = d / 128;
|
|
359
372
|
uint64_t const* a64 = (uint64_t const*)(a);
|
|
@@ -369,10 +382,19 @@ inline static size_t simsimd_hamming_b1x128avx512(uint8_t const* a, uint8_t cons
|
|
|
369
382
|
return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
|
|
370
383
|
#else
|
|
371
384
|
(void)a, (void)b, (void)d;
|
|
372
|
-
return
|
|
385
|
+
return -1;
|
|
373
386
|
#endif
|
|
374
387
|
}
|
|
375
388
|
|
|
389
|
+
inline static simsimd_f32_t simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
|
|
390
|
+
size_t and_count = 0, or_count = 0;
|
|
391
|
+
for (size_t i = 0; i != d; ++i)
|
|
392
|
+
and_count += popcount32(a[i] & b[i]), or_count += popcount32(a[i] | b[i]);
|
|
393
|
+
return 1 - (simsimd_f32_t)(and_count) / or_count;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
#undef popcount32
|
|
397
|
+
|
|
376
398
|
#ifdef __cplusplus
|
|
377
399
|
}
|
|
378
400
|
#endif
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @brief Collection of Chemistry-oriented Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
|
|
3
|
+
*
|
|
4
|
+
* @author Ashot Vardanian
|
|
5
|
+
* @date July 1, 2023
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
#include <string.h> // `memcpy`
|
|
10
|
+
|
|
11
|
+
#include <simsimd/simsimd.h>
|
|
12
|
+
|
|
13
|
+
#ifdef _MSC_VER
|
|
14
|
+
#include <intrin.h>
|
|
15
|
+
#define popcount64 __popcnt64
|
|
16
|
+
#else
|
|
17
|
+
#define popcount64 __builtin_popcountll
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
#ifdef __cplusplus
|
|
21
|
+
extern "C" {
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
|
|
26
|
+
* forming 21 incomplete bytes for the MACCS fingerprints used in
|
|
27
|
+
* computation chemistry.
|
|
28
|
+
*/
|
|
29
|
+
inline static simsimd_f32_t simsimd_tanimoto_maccs_naive(uint8_t const* a_chars, uint8_t const* b_chars) {
|
|
30
|
+
unsigned long a[3] = {0};
|
|
31
|
+
unsigned long b[3] = {0};
|
|
32
|
+
memcpy(&a[0], a_chars, 21);
|
|
33
|
+
memcpy(&b[0], b_chars, 21);
|
|
34
|
+
float and_count = popcount64(a[0] & b[0]) + popcount64(a[1] & b[1]) + popcount64(a[2] & b[2]);
|
|
35
|
+
float or_count = popcount64(a[0] | b[0]) + popcount64(a[1] | b[1]) + popcount64(a[2] | b[2]);
|
|
36
|
+
return 1 - and_count / or_count;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
|
|
41
|
+
* forming 21 incomplete bytes for the MACCS fingerprints used in
|
|
42
|
+
* computation chemistry, accelerated for Arm NEON ISA.
|
|
43
|
+
*/
|
|
44
|
+
inline static simsimd_f32_t simsimd_tanimoto_maccs_neon(uint8_t const* a_chars, uint8_t const* b_chars) {
|
|
45
|
+
#if defined(__ARM_NEON)
|
|
46
|
+
unsigned char a[32] = {0};
|
|
47
|
+
unsigned char b[32] = {0};
|
|
48
|
+
memcpy(&a[0], a_chars, 21);
|
|
49
|
+
memcpy(&b[0], b_chars, 21);
|
|
50
|
+
uint8x16_t a_first = vld1q_u8(&a[0]);
|
|
51
|
+
uint8x16_t a_second = vld1q_u8(&a[16]);
|
|
52
|
+
uint8x16_t b_first = vld1q_u8(&b[0]);
|
|
53
|
+
uint8x16_t b_second = vld1q_u8(&b[16]);
|
|
54
|
+
uint8x16_t a_and_b_first = vandq_u8(a_first, b_first);
|
|
55
|
+
uint8x16_t a_and_b_second = vandq_u8(a_second, b_second);
|
|
56
|
+
uint8x16_t a_or_b_first = vorrq_u8(a_first, b_first);
|
|
57
|
+
uint8x16_t a_or_b_second = vorrq_u8(a_second, b_second);
|
|
58
|
+
float and_populations = vaddvq_u8(vaddq_u8(vcntq_u8(a_and_b_first), vcntq_u8(a_and_b_second)));
|
|
59
|
+
float or_populations = vaddvq_u8(vaddq_u8(vcntq_u8(a_or_b_first), vcntq_u8(a_or_b_second)));
|
|
60
|
+
return 1 - and_populations / or_populations;
|
|
61
|
+
#else
|
|
62
|
+
(void)a_chars, (void)b_chars;
|
|
63
|
+
return 0;
|
|
64
|
+
#endif
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
|
|
69
|
+
* forming 21 incomplete bytes for the MACCS fingerprints used in
|
|
70
|
+
* computation chemistry, accelerated for Arm NEON ISA.
|
|
71
|
+
*/
|
|
72
|
+
inline static simsimd_f32_t simsimd_tanimoto_maccs_sve(uint8_t const* a_chars, uint8_t const* b_chars) {
|
|
73
|
+
#if defined(__ARM_FEATURE_SVE)
|
|
74
|
+
svbool_t pg_vec = svwhilelt_b8(0ul, 21ul);
|
|
75
|
+
svuint8_t a_vec = svld1_u8(pg_vec, (uint8_t const*)a_chars);
|
|
76
|
+
svuint8_t b_vec = svld1_u8(pg_vec, (uint8_t const*)b_chars);
|
|
77
|
+
svuint8_t a_and_b_vec = svand_u8_m(pg_vec, a_vec, b_vec);
|
|
78
|
+
svuint8_t a_or_b_vec = svorr_u8_m(pg_vec, a_vec, b_vec);
|
|
79
|
+
svuint8_t and_populations_vec = svcnt_u8_x(pg_vec, a_and_b_vec);
|
|
80
|
+
svuint8_t or_populations_vec = svcnt_u8_x(pg_vec, a_or_b_vec);
|
|
81
|
+
|
|
82
|
+
// We are gonna need at most two cycles like this, regardless of the architecture,
|
|
83
|
+
// assuming the SVE registers are at least 128 bits (16 bytes), and we onlu need 21 bytes.
|
|
84
|
+
pg_vec = svwhilelt_b8(svcntb(), 21ul);
|
|
85
|
+
a_vec = svld1_u8(pg_vec, (uint8_t const*)a_chars + svcntb());
|
|
86
|
+
b_vec = svld1_u8(pg_vec, (uint8_t const*)b_chars + svcntb());
|
|
87
|
+
a_and_b_vec = svand_u8_m(pg_vec, a_vec, b_vec);
|
|
88
|
+
a_or_b_vec = svorr_u8_m(pg_vec, a_vec, b_vec);
|
|
89
|
+
and_populations_vec = svadd_u8_m(pg_vec, svcnt_u8_x(pg_vec, a_and_b_vec), and_populations_vec);
|
|
90
|
+
or_populations_vec = svadd_u8_m(pg_vec, svcnt_u8_x(pg_vec, a_or_b_vec), or_populations_vec);
|
|
91
|
+
|
|
92
|
+
float and_populations = svaddv_u8(svptrue_b8(), and_populations_vec);
|
|
93
|
+
float or_populations = svaddv_u8(svptrue_b8(), or_populations_vec);
|
|
94
|
+
return 1 - and_populations / or_populations;
|
|
95
|
+
#else
|
|
96
|
+
(void)a_chars, (void)b_chars;
|
|
97
|
+
return 0;
|
|
98
|
+
#endif
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @brief Optimized version for Tanimoto distance on @b exactly 166 bits,
|
|
103
|
+
* forming 21 incomplete bytes for the MACCS fingerprints used in
|
|
104
|
+
* computation chemistry, accelerated with AVX-512 population count
|
|
105
|
+
* instructions.
|
|
106
|
+
*/
|
|
107
|
+
inline static simsimd_f32_t simsimd_tanimoto_maccs_avx512(uint8_t const* a, uint8_t const* b) {
|
|
108
|
+
#if defined(__AVX512VPOPCNTDQ__)
|
|
109
|
+
__m256i a_vec = _mm256_maskz_loadu_epi8(0b11111111111111111111100000000000, a);
|
|
110
|
+
__m256i b_vec = _mm256_maskz_loadu_epi8(0b11111111111111111111100000000000, b);
|
|
111
|
+
__m256i and_vec _mm256_and_si256(a_vec, b_vec);
|
|
112
|
+
__m256i or_vec _mm256_or_si256(a_vec, b_vec);
|
|
113
|
+
__m256i and_counts = _mm256_popcnt_epi8(and_vec);
|
|
114
|
+
__m256i or_counts = _mm256_popcnt_epi8(or_vec);
|
|
115
|
+
return 1 - float(_mm256_reduce_add_epi8(and_counts)) / _mm256_reduce_add_epi8(or_counts);
|
|
116
|
+
#else
|
|
117
|
+
(void)a, (void)b;
|
|
118
|
+
return 0;
|
|
119
|
+
#endif
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#undef popcount64
|
|
123
|
+
|
|
124
|
+
#ifdef __cplusplus
|
|
125
|
+
}
|
|
126
|
+
#endif
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel", "cmake>=3.22", "pybind11"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.pytest.ini_options]
|
|
6
|
+
minversion = "6.0"
|
|
7
|
+
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config"]
|
|
8
|
+
xfail_strict = true
|
|
9
|
+
filterwarnings = ["error"]
|
|
10
|
+
|
|
11
|
+
[tool.cibuildwheel]
|
|
12
|
+
test-requires = ["pytest"]
|
|
13
|
+
test-command = "pytest {project}/python/test.py -x"
|
|
14
|
+
build-verbosity = 0
|
|
15
|
+
skip = ["*musllinux*", "*i686*", "pp*"]
|
|
16
|
+
|
|
17
|
+
[tool.cibuildwheel.linux]
|
|
18
|
+
before-build = [
|
|
19
|
+
"rm -rf {project}/build",
|
|
20
|
+
]
|
|
21
|
+
manylinux-x86_64-image = "manylinux_2_28"
|
|
22
|
+
manylinux-aarch64-image = "manylinux_2_28"
|
|
23
|
+
|
|
24
|
+
archs = ["x86_64", "aarch64"]
|
|
25
|
+
before-all = [
|
|
26
|
+
"yum install -y glibc-devel wget python3-devel"
|
|
27
|
+
]
|
|
28
|
+
repair-wheel-command = "auditwheel repair --lib-sdir . -w {dest_dir} {wheel}"
|
|
29
|
+
|
|
30
|
+
[tool.cibuildwheel.macos]
|
|
31
|
+
before-build = [
|
|
32
|
+
"rm -rf {project}/build",
|
|
33
|
+
]
|
|
34
|
+
archs = ["x86_64", "universal2", "arm64"]
|
|
35
|
+
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
|
|
36
|
+
|
|
37
|
+
[tool.cibuildwheel.windows]
|
|
38
|
+
before-build = [
|
|
39
|
+
"rd /s /q {project}\\build || echo Done",
|
|
40
|
+
]
|
|
41
|
+
archs = ["x86", "AMD64"]
|
|
42
|
+
skip = ["*win32*", "pp*"]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file python.c
|
|
3
|
+
* @author Ashot Vardanian
|
|
4
|
+
* @date 2023-01-30
|
|
5
|
+
* @copyright Copyright (c) 2023
|
|
6
|
+
*
|
|
7
|
+
* @brief Pure CPython bindings for simsimd.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#include "simsimd/simsimd.h"
|
|
11
|
+
#include "simsimd/simsimd_chem.h"
|
|
12
|
+
|
|
13
|
+
#define PY_SSIZE_T_CLEAN
|
|
14
|
+
#include <Python.h>
|
|
15
|
+
|
|
16
|
+
static void pseudo_destroy(PyObject* obj) { (void)obj; }
|
|
17
|
+
|
|
18
|
+
PyObject* distance(void* func) { return PyCapsule_New(func, NULL, pseudo_destroy); }
|
|
19
|
+
|
|
20
|
+
static PyObject* to_int(PyObject* self, PyObject* args) {
|
|
21
|
+
PyObject* capsule;
|
|
22
|
+
if (!PyArg_ParseTuple(args, "O", &capsule)) {
|
|
23
|
+
return NULL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!PyCapsule_IsValid(capsule, NULL)) {
|
|
27
|
+
PyErr_SetString(PyExc_ValueError, "Object is not a valid capsule");
|
|
28
|
+
return NULL;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void* pointer = PyCapsule_GetPointer(capsule, NULL);
|
|
32
|
+
|
|
33
|
+
return PyLong_FromVoidPtr(pointer);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static PyMethodDef simsimd_methods[] = {
|
|
37
|
+
{"to_int", (PyCFunction)to_int, METH_VARARGS, "Converts CPython capsule to `int`"},
|
|
38
|
+
{NULL, NULL, 0, NULL} /* Sentinel */
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
static PyModuleDef simsimd_module = {
|
|
42
|
+
PyModuleDef_HEAD_INIT,
|
|
43
|
+
.m_name = "SimSIMD",
|
|
44
|
+
.m_doc = "SIMD-accelerated similarity measures for x86 and Arm: AVX2, AVX512, NEON, SVE",
|
|
45
|
+
.m_size = -1,
|
|
46
|
+
.m_methods = simsimd_methods,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
PyMODINIT_FUNC PyInit_simsimd(void) {
|
|
50
|
+
PyObject* m = PyModule_Create(&simsimd_module);
|
|
51
|
+
if (m == NULL)
|
|
52
|
+
return NULL;
|
|
53
|
+
|
|
54
|
+
PyModule_AddObject(m, "dot_f32_sve", distance(&simsimd_dot_f32_sve));
|
|
55
|
+
PyModule_AddObject(m, "dot_f32x4_neon", distance(&simsimd_dot_f32x4_neon));
|
|
56
|
+
|
|
57
|
+
PyModule_AddObject(m, "cos_f32_sve", distance(&simsimd_cos_f32_sve));
|
|
58
|
+
PyModule_AddObject(m, "cos_f16_sve", distance(&simsimd_cos_f16_sve));
|
|
59
|
+
PyModule_AddObject(m, "cos_f16x4_neon", distance(&simsimd_cos_f16x4_neon));
|
|
60
|
+
PyModule_AddObject(m, "cos_i8x16_neon", distance(&simsimd_cos_i8x16_neon));
|
|
61
|
+
PyModule_AddObject(m, "cos_f32x4_neon", distance(&simsimd_cos_f32x4_neon));
|
|
62
|
+
PyModule_AddObject(m, "cos_f16x16_avx512", distance(&simsimd_cos_f16x16_avx512));
|
|
63
|
+
PyModule_AddObject(m, "cos_f32x4_avx2", distance(&simsimd_cos_f32x4_avx2));
|
|
64
|
+
|
|
65
|
+
PyModule_AddObject(m, "l2sq_f32_sve", distance(&simsimd_l2sq_f32_sve));
|
|
66
|
+
PyModule_AddObject(m, "l2sq_f16_sve", distance(&simsimd_l2sq_f16_sve));
|
|
67
|
+
|
|
68
|
+
PyModule_AddObject(m, "hamming_b1x8_sve", distance(&simsimd_hamming_b1x8_sve));
|
|
69
|
+
PyModule_AddObject(m, "hamming_b1x128_sve", distance(&simsimd_hamming_b1x128_sve));
|
|
70
|
+
PyModule_AddObject(m, "hamming_b1x128_avx512", distance(&simsimd_hamming_b1x128_avx512));
|
|
71
|
+
|
|
72
|
+
PyModule_AddObject(m, "tanimoto_b1x8_naive", distance(&simsimd_tanimoto_b1x8_naive));
|
|
73
|
+
|
|
74
|
+
// Specific vector sizes for chemistry
|
|
75
|
+
PyModule_AddObject(m, "tanimoto_maccs_naive", distance(&simsimd_tanimoto_maccs_naive));
|
|
76
|
+
PyModule_AddObject(m, "tanimoto_maccs_neon", distance(&simsimd_tanimoto_maccs_neon));
|
|
77
|
+
PyModule_AddObject(m, "tanimoto_maccs_sve", distance(&simsimd_tanimoto_maccs_sve));
|
|
78
|
+
PyModule_AddObject(m, "tanimoto_maccs_avx512", distance(&simsimd_tanimoto_maccs_avx512));
|
|
79
|
+
|
|
80
|
+
return m;
|
|
81
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pip install --upgrade --force-reinstall . && pytest --capture=no python/tests/
|