usearch 2.7.3 → 2.7.6

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.
Files changed (55) hide show
  1. package/.github/workflows/Dockerfile.libs +4 -6
  2. package/.github/workflows/prerelease.yml +4 -1
  3. package/.github/workflows/release.yml +8 -28
  4. package/.github/workflows/update_version.sh +1 -0
  5. package/CITATION.cff +2 -2
  6. package/CMakeLists.txt +1 -1
  7. package/Cargo.toml +1 -1
  8. package/README.md +4 -4
  9. package/binding.gyp +1 -1
  10. package/build.gradle +20 -5
  11. package/conanfile.py +1 -1
  12. package/csharp/nuget/nuget-package.props +1 -1
  13. package/docs/compilation.md +0 -2
  14. package/golang/README.md +0 -1
  15. package/include/usearch/index.hpp +1 -1
  16. package/include/usearch/index_plugins.hpp +119 -243
  17. package/package.json +1 -1
  18. package/simsimd/.github/workflows/prerelease.yml +33 -58
  19. package/simsimd/.github/workflows/release.yml +22 -9
  20. package/simsimd/.github/workflows/update_version.sh +3 -1
  21. package/simsimd/.releaserc +9 -1
  22. package/simsimd/.vscode/launch.json +3 -0
  23. package/simsimd/.vscode/settings.json +15 -2
  24. package/simsimd/.vscode/tasks.json +2 -2
  25. package/simsimd/CMakeLists.txt +36 -55
  26. package/simsimd/README.md +163 -109
  27. package/simsimd/binding.gyp +18 -0
  28. package/simsimd/cpp/bench.cxx +187 -0
  29. package/simsimd/go.mod +3 -0
  30. package/simsimd/golang/bench_test.go +40 -0
  31. package/simsimd/golang/simsimd.go +47 -0
  32. package/simsimd/golang/unit_test.go +40 -0
  33. package/simsimd/include/simsimd/binary.h +186 -0
  34. package/simsimd/include/simsimd/probability.h +645 -0
  35. package/simsimd/include/simsimd/simsimd.h +335 -575
  36. package/simsimd/include/simsimd/spatial.h +924 -0
  37. package/simsimd/include/simsimd/types.h +196 -0
  38. package/simsimd/javascript/bench.js +117 -0
  39. package/simsimd/javascript/lib.c +102 -0
  40. package/simsimd/javascript/simsimd.js +45 -0
  41. package/simsimd/javascript/test.js +29 -0
  42. package/simsimd/package.json +31 -2
  43. package/simsimd/pyproject.toml +5 -6
  44. package/simsimd/python/bench.ipynb +187 -0
  45. package/simsimd/python/bench.py +268 -0
  46. package/simsimd/python/lib.c +453 -72
  47. package/simsimd/python/test.py +167 -35
  48. package/simsimd/setup.py +51 -8
  49. package/wasmer.toml +1 -1
  50. package/simsimd/bench.cxx +0 -137
  51. package/simsimd/include/simsimd/simsimd_bio.h +0 -54
  52. package/simsimd/include/simsimd/simsimd_chem.h +0 -167
  53. package/simsimd/include/simsimd/simsimd_text.h +0 -92
  54. package/simsimd/python/run.sh +0 -1
  55. package/simsimd/python/test.c +0 -109
@@ -1,626 +1,386 @@
1
1
  /**
2
- * @brief Collection of Similarity Measures, SIMD-accelerated with SSE, AVX, NEON, SVE.
3
- *
4
- * @author Ash Vardanian
5
- * @date March 14, 2023
2
+ * @brief SIMD-accelerated Similarity Measures and Distance Functions.
3
+ * @file simsimd.h
4
+ * @author Ash Vardanian
5
+ * @date March 14, 2023
6
+ * @copyright Copyright (c) 2023
6
7
  *
8
+ * References:
7
9
  * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
8
10
  * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
9
11
  * Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
10
12
  */
11
13
 
12
14
  #pragma once
13
- #include <math.h> // `sqrt`
14
- #include <stddef.h> // `size_t`
15
- #include <stdint.h>
16
-
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
24
- #endif
25
-
26
- // Compiling for Arm: SIMSIMD_TARGET_ARM
27
- #if defined(__aarch64__) || defined(_M_ARM64)
28
- #define SIMSIMD_TARGET_ARM 1
15
+ #include "binary.h" // Hamming, Jaccard
16
+ #include "probability.h" // Kullback-Leibler, Jensen–Shannon
17
+ #include "spatial.h" // L2, Inner Product, Cosine
29
18
 
30
- // Compiling for Arm: SIMSIMD_TARGET_ARM_NEON
31
- #if !defined(SIMSIMD_TARGET_ARM_NEON)
32
- #if defined(__ARM_NEON)
33
- #define SIMSIMD_TARGET_ARM_NEON 1
34
- #else
35
- #define SIMSIMD_TARGET_ARM_NEON 0
36
- #endif
37
- #endif
38
-
39
- // Compiling for Arm: SIMSIMD_TARGET_ARM_SVE
40
- #if !defined(SIMSIMD_TARGET_ARM_SVE)
41
- #if defined(__ARM_FEATURE_SVE)
42
- #define SIMSIMD_TARGET_ARM_SVE 1
43
- #else
44
- #define SIMSIMD_TARGET_ARM_SVE 0
45
- #endif
46
- #endif
47
-
48
- #undef SIMSIMD_TARGET_X86
49
- #undef SIMSIMD_TARGET_X86_AVX2
50
- #undef SIMSIMD_TARGET_X86_AVX512
51
- #define SIMSIMD_TARGET_X86 0
52
- #define SIMSIMD_TARGET_X86_AVX2 0
53
- #define SIMSIMD_TARGET_X86_AVX512 0
54
-
55
- // Compiling for x86: SIMSIMD_TARGET_X86
56
- #elif defined(__x86_64__) || defined(_M_X64)
57
- #define SIMSIMD_TARGET_X86 1
58
-
59
- // Compiling for x86: SIMSIMD_TARGET_X86_AVX2
60
- #if !defined(SIMSIMD_TARGET_X86_AVX2)
61
- #if defined(__AVX2__)
62
- #define SIMSIMD_TARGET_X86_AVX2 1
63
- #else
64
- #define SIMSIMD_TARGET_X86_AVX2 0
65
- #endif
66
- #endif
67
-
68
- // Compiling for x86: SIMSIMD_TARGET_X86_AVX512
69
- #if !defined(SIMSIMD_TARGET_X86_AVX512)
70
- #if defined(__AVX512F__) || defined(__AVX512VPOPCNTDQ__)
71
- #define SIMSIMD_TARGET_X86_AVX512 1
72
- #else
73
- #define SIMSIMD_TARGET_X86_AVX512 0
74
- #endif
75
- #endif
76
-
77
- #undef SIMSIMD_TARGET_ARM
78
- #undef SIMSIMD_TARGET_ARM_NEON
79
- #undef SIMSIMD_TARGET_ARM_SVE
80
- #define SIMSIMD_TARGET_ARM 0
81
- #define SIMSIMD_TARGET_ARM_NEON 0
82
- #define SIMSIMD_TARGET_ARM_SVE 0
83
-
84
- // Compiling for unknown hardware architecture
85
- #else
86
- #error Unknown hardware architecture!
87
- #endif
88
-
89
- #if SIMSIMD_TARGET_ARM_NEON
90
- #include <arm_neon.h>
19
+ #if SIMSIMD_TARGET_ARM
20
+ #ifdef __linux__
21
+ #include <asm/hwcap.h>
22
+ #include <sys/auxv.h>
91
23
  #endif
92
-
93
- #if SIMSIMD_TARGET_ARM_SVE
94
- #include <arm_sve.h>
95
- #endif
96
-
97
- #if SIMSIMD_TARGET_X86_AVX2 || SIMSIMD_TARGET_X86_AVX512
98
- #include <immintrin.h>
99
24
  #endif
100
25
 
101
26
  #ifdef __cplusplus
102
27
  extern "C" {
103
28
  #endif
104
29
 
105
- typedef float simsimd_f32_t;
106
- typedef double simsimd_f64_t;
107
- typedef int16_t simsimd_f16_t;
108
-
109
- union simsimd_f32i32_t {
110
- int32_t i;
111
- float f;
112
- };
113
-
114
- #if SIMSIMD_TARGET_ARM
115
- #if SIMSIMD_TARGET_ARM_SVE
116
-
117
- __attribute__((target("+sve"))) inline static simsimd_f32_t //
118
- simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
119
-
120
- size_t i = 0;
121
- svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
122
- svbool_t pg_vec = svwhilelt_b32(i, d);
123
- do {
124
- svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
125
- svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
126
- ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
127
- i += svcntw();
128
- pg_vec = svwhilelt_b32(i, d);
129
- } while (svptest_any(svptrue_b32(), pg_vec));
130
- return 1 - svaddv_f32(svptrue_b32(), ab_vec);
131
- }
132
-
133
- __attribute__((target("+sve"))) inline static simsimd_f32_t //
134
- simsimd_cos_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
135
- size_t i = 0;
136
- svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
137
- svfloat32_t a2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
138
- svfloat32_t b2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
139
- svbool_t pg_vec = svwhilelt_b32(i, d);
140
- do {
141
- svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
142
- svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
143
- ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
144
- a2_vec = svmla_f32_x(pg_vec, a2_vec, a_vec, a_vec);
145
- b2_vec = svmla_f32_x(pg_vec, b2_vec, b_vec, b_vec);
146
- i += svcntw();
147
- pg_vec = svwhilelt_b32(i, d);
148
- } while (svptest_any(svptrue_b32(), pg_vec));
149
- simsimd_f32_t ab = svaddv_f32(svptrue_b32(), ab_vec);
150
- simsimd_f32_t a2 = svaddv_f32(svptrue_b32(), a2_vec);
151
- simsimd_f32_t b2 = svaddv_f32(svptrue_b32(), b2_vec);
152
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
153
- }
154
-
155
- __attribute__((target("+sve"))) inline static simsimd_f32_t //
156
- simsimd_l2sq_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
157
- size_t i = 0;
158
- svfloat32_t d2_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
159
- svbool_t pg_vec = svwhilelt_b32(i, d);
160
- do {
161
- svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
162
- svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
163
- svfloat32_t a_minus_b_vec = svsub_f32_x(pg_vec, a_vec, b_vec);
164
- d2_vec = svmla_f32_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
165
- i += svcntw();
166
- pg_vec = svwhilelt_b32(i, d);
167
- } while (svptest_any(svptrue_b32(), pg_vec));
168
- simsimd_f32_t d2 = svaddv_f32(svptrue_b32(), d2_vec);
169
- return d2;
170
- }
171
-
172
- __attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
173
- simsimd_cos_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
174
- size_t i = 0;
175
- svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
176
- svfloat16_t a2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
177
- svfloat16_t b2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
178
- svbool_t pg_vec = svwhilelt_b16(i, d);
179
- simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
180
- simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
181
- do {
182
- svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
183
- svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
184
- ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
185
- a2_vec = svmla_f16_x(pg_vec, a2_vec, a_vec, a_vec);
186
- b2_vec = svmla_f16_x(pg_vec, b2_vec, b_vec, b_vec);
187
- i += svcnth();
188
- pg_vec = svwhilelt_b16(i, d);
189
- } while (svptest_any(svptrue_b16(), pg_vec));
190
- simsimd_f16_t ab = svaddv_f16(svptrue_b16(), ab_vec);
191
- simsimd_f16_t a2 = svaddv_f16(svptrue_b16(), a2_vec);
192
- simsimd_f16_t b2 = svaddv_f16(svptrue_b16(), b2_vec);
193
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
194
- }
195
-
196
- __attribute__((target("+sve+fp16"))) inline static simsimd_f32_t //
197
- simsimd_l2sq_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, size_t d) {
198
- size_t i = 0;
199
- svfloat16_t d2_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
200
- svbool_t pg_vec = svwhilelt_b16(i, d);
201
- simsimd_f16_t const* a = (simsimd_f16_t const*)(a_enum);
202
- simsimd_f16_t const* b = (simsimd_f16_t const*)(b_enum);
203
- do {
204
- svfloat16_t a_vec = svld1_f16(pg_vec, (float16_t const*)a + i);
205
- svfloat16_t b_vec = svld1_f16(pg_vec, (float16_t const*)b + i);
206
- svfloat16_t a_minus_b_vec = svsub_f16_x(pg_vec, a_vec, b_vec);
207
- d2_vec = svmla_f16_x(pg_vec, d2_vec, a_minus_b_vec, a_minus_b_vec);
208
- i += svcnth();
209
- pg_vec = svwhilelt_b16(i, d);
210
- } while (svptest_any(svptrue_b16(), pg_vec));
211
- float16_t d2_f16 = svaddv_f16(svptrue_b16(), d2_vec);
212
- return 1 - d2_f16;
213
- }
214
-
215
- __attribute__((target("+sve"))) inline static simsimd_f32_t //
216
- simsimd_hamming_b1x8_sve(uint8_t const* a, uint8_t const* b, size_t d) {
217
- size_t i = 0;
218
- svuint8_t d_vec = svdupq_n_u8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
219
- svbool_t pg_vec = svwhilelt_b8(i, d);
220
- do {
221
- svuint8_t a_vec = svld1_u8(pg_vec, a + i);
222
- svuint8_t b_vec = svld1_u8(pg_vec, b + i);
223
- svuint8_t a_xor_b_vec = sveor_u8_m(pg_vec, a_vec, b_vec);
224
- d_vec = svadd_u8_m(pg_vec, d_vec, svcnt_u8_x(pg_vec, a_xor_b_vec));
225
- i += svcntb() * __CHAR_BIT__;
226
- pg_vec = svwhilelt_b32(i, d);
227
- } while (svptest_any(svptrue_b32(), pg_vec));
228
- return 1 - svaddv_u8(svptrue_b32(), d_vec);
229
- }
30
+ /**
31
+ * @brief Enumeration of supported metric kinds.
32
+ */
33
+ typedef enum {
34
+ simsimd_metric_unknown_k = 0, ///< Unknown metric kind
230
35
 
231
- #endif // SIMSIMD_TARGET_ARM_SVE
36
+ // Classics:
37
+ simsimd_metric_ip_k = 'i', ///< Inner product
38
+ simsimd_metric_dot_k = 'i', ///< Inner product alias
39
+ simsimd_metric_inner_k = 'i', ///< Inner product alias
232
40
 
233
- #if SIMSIMD_TARGET_ARM_NEON
41
+ simsimd_metric_cos_k = 'c', ///< Cosine similarity
42
+ simsimd_metric_cosine_k = 'c', ///< Cosine similarity alias
43
+ simsimd_metric_angular_k = 'c', ///< Cosine similarity alias
234
44
 
235
- __attribute__((target("+simd"))) inline static simsimd_f32_t //
236
- simsimd_dot_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
45
+ simsimd_metric_l2sq_k = 'e', ///< Squared Euclidean distance
46
+ simsimd_metric_sqeuclidean_k = 'e', ///< Squared Euclidean distance alias
237
47
 
238
- float32x4_t ab_vec = vdupq_n_f32(0);
239
- for (size_t i = 0; i != d; i += 4)
240
- ab_vec = vfmaq_f32(ab_vec, vld1q_f32(a + i), vld1q_f32(b + i));
241
- return 1 - vaddvq_f32(ab_vec);
242
- }
48
+ // Binary:
49
+ simsimd_metric_hamming_k = 'b', ///< Hamming distance
50
+ simsimd_metric_jaccard_k = 'j', ///< Jaccard coefficient
243
51
 
244
- __attribute__((target("+simd+fp16"))) inline static simsimd_f32_t //
245
- simsimd_dot_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
52
+ // Probability:
53
+ simsimd_metric_kl_k = 'k', ///< Kullback-Leibler divergence
54
+ simsimd_metric_kullback_leibler_k = 'k', ///< Kullback-Leibler divergence alias
246
55
 
247
- float32x4_t ab_vec = vdupq_n_f32(0);
248
- for (size_t i = 0; i != d; i += 4) {
249
- float32x4_t a_vec = vcvt_f32_f16(vld1_f16((float16_t const*)a + i));
250
- float32x4_t b_vec = vcvt_f32_f16(vld1_f16((float16_t const*)b + i));
251
- ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
252
- }
253
- return 1 - vaddvq_f32(ab_vec);
254
- }
56
+ simsimd_metric_js_k = 's', ///< Jensen-Shannon divergence
57
+ simsimd_metric_jensen_shannon_k = 's', ///< Jensen-Shannon divergence alias
255
58
 
256
- __attribute__((target("+simd+fp16"))) inline static simsimd_f32_t //
257
- simsimd_cos_f16x4_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
258
-
259
- float32x4_t ab_vec = vdupq_n_f32(0);
260
- float32x4_t a2_vec = vdupq_n_f32(0);
261
- float32x4_t b2_vec = vdupq_n_f32(0);
262
- for (size_t i = 0; i != d; i += 4) {
263
- float32x4_t a_vec = vcvt_f32_f16(vld1_f16((float16_t const*)a + i));
264
- float32x4_t b_vec = vcvt_f32_f16(vld1_f16((float16_t const*)b + i));
265
- // vfmaq_f32(a, b, c) == vaddq_f32(vmulq_f32(b, c), a)
266
- ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
267
- a2_vec = vfmaq_f32(a2_vec, a_vec, a_vec);
268
- b2_vec = vfmaq_f32(b2_vec, b_vec, b_vec);
269
- }
270
- simsimd_f32_t ab = vaddvq_f32(ab_vec);
271
- simsimd_f32_t a2 = vaddvq_f32(a2_vec);
272
- simsimd_f32_t b2 = vaddvq_f32(b2_vec);
273
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
274
- }
59
+ } simsimd_metric_kind_t;
275
60
 
276
- __attribute__((target("+simd"))) inline static simsimd_f32_t //
277
- simsimd_cos_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
278
-
279
- int32x4_t ab_vec = vdupq_n_s32(0);
280
- int32x4_t a2_vec = vdupq_n_s32(0);
281
- int32x4_t b2_vec = vdupq_n_s32(0);
282
-
283
- // This 128-bit `vdot_s32` intrinsic is often unavailable, so we use the 64-bit `vdot_s32`.
284
- // for (size_t i = 0; i != d; i += 16) {
285
- // int8x16_t a_vec = vld1q_s8(a + i);
286
- // int8x16_t b_vec = vld1q_s8(b + i);
287
- // ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
288
- // a2_vec = vdotq_s32(a2_vec, a_vec, a_vec);
289
- // b2_vec = vdotq_s32(b2_vec, b_vec, b_vec);
290
- // }
291
- for (size_t i = 0; i != d; i += 8) {
292
- int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
293
- int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
294
- int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
295
- int16x8_t a2_part_vec = vmulq_s16(a_vec, a_vec);
296
- int16x8_t b2_part_vec = vmulq_s16(b_vec, b_vec);
297
- ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
298
- vmovl_s16(vget_low_s16(ab_part_vec))));
299
- a2_vec = vaddq_s32(a2_vec, vaddq_s32(vmovl_s16(vget_high_s16(a2_part_vec)), //
300
- vmovl_s16(vget_low_s16(a2_part_vec))));
301
- b2_vec = vaddq_s32(b2_vec, vaddq_s32(vmovl_s16(vget_high_s16(b2_part_vec)), //
302
- vmovl_s16(vget_low_s16(b2_part_vec))));
303
- }
61
+ /**
62
+ * @brief Enumeration of SIMD capabilities of the target architecture.
63
+ */
64
+ typedef enum {
65
+ simsimd_cap_serial_k = 1, ///< Serial (non-SIMD) capability
66
+ simsimd_cap_any_k = 0xFFFFFFFF, ///< Mask representing any capability
304
67
 
305
- int32x2_t ab_part = vadd_s32(vget_high_s32(ab_vec), vget_low_s32(ab_vec));
306
- int32_t ab = vget_lane_s32(vpadd_s32(ab_part, ab_part), 0);
307
- int32x2_t a2_part = vadd_s32(vget_high_s32(a2_vec), vget_low_s32(a2_vec));
308
- int32_t a2 = vget_lane_s32(vpadd_s32(a2_part, a2_part), 0);
309
- int32x2_t b2_part = vadd_s32(vget_high_s32(b2_vec), vget_low_s32(b2_vec));
310
- int32_t b2 = vget_lane_s32(vpadd_s32(b2_part, b2_part), 0);
311
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
312
- }
68
+ simsimd_cap_arm_neon_k = 1 << 10, ///< ARM NEON capability
69
+ simsimd_cap_arm_sve_k = 1 << 11, ///< ARM SVE capability
70
+ simsimd_cap_arm_sve2_k = 1 << 12, ///< ARM SVE2 capability
313
71
 
314
- __attribute__((target("+simd"))) inline static simsimd_f32_t //
315
- simsimd_l2sq_i8x16_neon(int8_t const* a, int8_t const* b, size_t d) {
316
-
317
- int32x4_t d2_vec = vdupq_n_s32(0);
318
- for (size_t i = 0; i != d; i += 8) {
319
- int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
320
- int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
321
- int16x8_t d2_part_vec = vsubq_s16(a_vec, b_vec);
322
- d2_part_vec = vmulq_s16(d2_part_vec, d2_part_vec);
323
- d2_vec = vaddq_s32(d2_vec, vaddq_s32(vmovl_s16(vget_high_s16(d2_part_vec)), //
324
- vmovl_s16(vget_low_s16(d2_part_vec))));
325
- }
72
+ simsimd_cap_x86_avx2_k = 1 << 20, ///< x86 AVX2 capability
73
+ simsimd_cap_x86_avx512_k = 1 << 21, ///< x86 AVX512 capability
74
+ simsimd_cap_x86_avx2fp16_k = 1 << 22, ///< x86 AVX2 with FP16 capability
75
+ simsimd_cap_x86_avx512fp16_k = 1 << 23, ///< x86 AVX512 with FP16 capability
76
+ simsimd_cap_x86_avx512vpopcntdq_k = 1 << 24, ///< x86 AVX512 VPOPCNTDQ instruction capability
326
77
 
327
- int32x2_t d2_part = vadd_s32(vget_high_s32(d2_vec), vget_low_s32(d2_vec));
328
- int32_t d2 = vget_lane_s32(vpadd_s32(d2_part, d2_part), 0);
329
- return d2;
330
- }
78
+ } simsimd_capability_t;
331
79
 
332
- __attribute__((target("+simd"))) inline static simsimd_f32_t //
333
- simsimd_cos_f32x4_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
334
-
335
- float32x4_t ab_vec = vdupq_n_f32(0);
336
- float32x4_t a2_vec = vdupq_n_f32(0);
337
- float32x4_t b2_vec = vdupq_n_f32(0);
338
- for (size_t i = 0; i != d; i += 4) {
339
- float32x4_t a_vec = vld1q_f32(a + i);
340
- float32x4_t b_vec = vld1q_f32(b + i);
341
- ab_vec = vmlaq_f32(ab_vec, a_vec, b_vec);
342
- a2_vec = vmlaq_f32(a2_vec, a_vec, a_vec);
343
- b2_vec = vmlaq_f32(b2_vec, b_vec, b_vec);
344
- }
345
- simsimd_f32_t ab = vaddvq_f32(ab_vec);
346
- simsimd_f32_t a2 = vaddvq_f32(a2_vec);
347
- simsimd_f32_t b2 = vaddvq_f32(b2_vec);
348
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
349
- }
80
+ /**
81
+ * @brief Enumeration of supported data types.
82
+ */
83
+ typedef enum {
84
+ simsimd_datatype_unknown_k, ///< Unknown data type
85
+ simsimd_datatype_f64_k, ///< Double precision floating point
86
+ simsimd_datatype_f32_k, ///< Single precision floating point
87
+ simsimd_datatype_f16_k, ///< Half precision floating point
88
+ simsimd_datatype_i8_k, ///< 8-bit integer
89
+ simsimd_datatype_b8_k, ///< Single-bit values packed into 8-bit words
90
+ } simsimd_datatype_t;
350
91
 
351
- __attribute__((target("+sve"))) inline static simsimd_f32_t //
352
- simsimd_hamming_b1x128_sve(uint8_t const* a, uint8_t const* b, size_t d) {
92
+ /**
93
+ * @brief Type-punned function pointer accepting two vectors and outputting their similarity/distance.
94
+ *
95
+ * @param[in] a Pointer to the first data array.
96
+ * @param[in] b Pointer to the second data array.
97
+ * @param[in] size_a Size of the first data array.
98
+ * @param[in] size_b Size of the second data array.
99
+ * @return Computed metric as a single-precision floating-point value.
100
+ */
101
+ typedef simsimd_f32_t (*simsimd_metric_punned_t)(void const* a, void const* b, simsimd_size_t size_a,
102
+ simsimd_size_t size_b);
353
103
 
354
- /// Contains 16x 8-bit integers with running population count sums.
355
- uint8x16_t d_vec = vdupq_n_u8(0);
356
- for (size_t i = 0; i != d; i += 16) {
357
- uint8x16_t a_vec = vld1q_u8(a + i);
358
- uint8x16_t b_vec = vld1q_u8(b + i);
359
- uint8x16_t a_xor_b_vec = veorq_u8(a_vec, b_vec);
360
- d_vec = vaddq_u8(d_vec, vcntq_u8(a_xor_b_vec));
361
- }
362
- return 1 - vaddvq_u8(d_vec);
363
- }
364
- #endif // SIMSIMD_TARGET_ARM_NEON
365
- #endif // SIMSIMD_TARGET_ARM
104
+ /**
105
+ * @brief Function to determine the SIMD capabilities of the current machine at @b runtime.
106
+ * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
107
+ */
108
+ inline static simsimd_capability_t simsimd_capabilities() {
366
109
 
367
110
  #if SIMSIMD_TARGET_X86
368
- #if SIMSIMD_TARGET_X86_AVX2
369
-
370
- __attribute__((target("avx2"))) //
371
- __attribute__((target("fma"))) inline static simsimd_f32_t //
372
- simsimd_dot_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
373
- __m128 ab_vec = _mm_set1_ps(0);
374
- for (size_t i = 0; i != d; i += 4)
375
- ab_vec = _mm_fmadd_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i), ab_vec);
376
- ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
377
- ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
378
- union simsimd_f32i32_t ab_union;
379
- ab_union.i = _mm_cvtsi128_si32(_mm_castps_si128(ab_vec));
380
- return 1 - ab_union.f;
381
- }
382
-
383
- __attribute__((target("avx2"))) //
384
- __attribute__((target("fma"))) inline static simsimd_f32_t //
385
- simsimd_cos_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
386
-
387
- __m128 ab_vec = _mm_set1_ps(0);
388
- __m128 a2_vec = _mm_set1_ps(0);
389
- __m128 b2_vec = _mm_set1_ps(0);
390
- for (size_t i = 0; i != d; i += 4) {
391
- __m128 a_vec = _mm_loadu_ps(a + i);
392
- __m128 b_vec = _mm_loadu_ps(b + i);
393
- ab_vec = _mm_fmadd_ps(a_vec, b_vec, ab_vec);
394
- a2_vec = _mm_fmadd_ps(a_vec, a_vec, a2_vec);
395
- b2_vec = _mm_fmadd_ps(b_vec, b_vec, b2_vec);
396
- }
397
- ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
398
- ab_vec = _mm_hadd_ps(ab_vec, ab_vec);
399
- a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
400
- a2_vec = _mm_hadd_ps(a2_vec, a2_vec);
401
- b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
402
- b2_vec = _mm_hadd_ps(b2_vec, b2_vec);
403
- union simsimd_f32i32_t ab_union = {_mm_cvtsi128_si32(_mm_castps_si128(ab_vec))};
404
- union simsimd_f32i32_t a2_union = {_mm_cvtsi128_si32(_mm_castps_si128(a2_vec))};
405
- union simsimd_f32i32_t b2_union = {_mm_cvtsi128_si32(_mm_castps_si128(b2_vec))};
406
- return 1 - ab_union.f / (sqrtf(a2_union.f) * sqrtf(b2_union.f));
407
- }
408
-
409
- __attribute__((target("avx2"))) //
410
- __attribute__((target("fma"))) inline static simsimd_f32_t //
411
- simsimd_l2sq_f32x4_avx2(simsimd_f32_t const* a, simsimd_f32_t const* b, size_t d) {
412
- __m128 sum_vec = _mm_set1_ps(0);
413
- for (size_t i = 0; i != d; i += 4) {
414
- __m128 diff_vec = _mm_sub_ps(_mm_loadu_ps(a + i), _mm_loadu_ps(b + i));
415
- sum_vec = _mm_fmadd_ps(diff_vec, diff_vec, sum_vec);
416
- }
417
- sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
418
- sum_vec = _mm_hadd_ps(sum_vec, sum_vec);
419
- float result[1];
420
- _mm_store_ss(result, sum_vec);
421
- return result[0];
422
- }
423
-
424
- __attribute__((target("avx2"))) //
425
- __attribute__((target("f16c"))) //
426
- __attribute__((target("fma"))) inline static simsimd_f32_t //
427
- simsimd_dot_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
428
- __m256 ab_vec = _mm256_set1_ps(0);
429
- for (size_t i = 0; i != d; i += 8) {
430
- __m256 a_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
431
- __m256 b_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
432
- ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
433
- }
434
- ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 0x81), ab_vec);
435
- ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
436
- ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
437
- float result[1];
438
- _mm_store_ss(result, _mm256_castps256_ps128(ab_vec));
439
- return 1 - result[0];
440
- }
441
-
442
- __attribute__((target("avx2"))) //
443
- __attribute__((target("f16c"))) //
444
- __attribute__((target("fma"))) inline static simsimd_f32_t //
445
- simsimd_cos_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
446
- __m256 ab_vec = _mm256_set1_ps(0);
447
- __m256 a2_vec = _mm256_set1_ps(0);
448
- __m256 b2_vec = _mm256_set1_ps(0);
449
- for (size_t i = 0; i != d; i += 8) {
450
- __m256 a_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
451
- __m256 b_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
452
- ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
453
- a2_vec = _mm256_fmadd_ps(a_vec, a_vec, a2_vec);
454
- b2_vec = _mm256_fmadd_ps(b_vec, b_vec, b2_vec);
455
- }
456
- ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 0x81), ab_vec);
457
- ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
458
- ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
459
- a2_vec = _mm256_add_ps(_mm256_permute2f128_ps(a2_vec, a2_vec, 0x81), a2_vec);
460
- a2_vec = _mm256_hadd_ps(a2_vec, a2_vec);
461
- a2_vec = _mm256_hadd_ps(a2_vec, a2_vec);
462
- b2_vec = _mm256_add_ps(_mm256_permute2f128_ps(b2_vec, b2_vec, 0x81), b2_vec);
463
- b2_vec = _mm256_hadd_ps(b2_vec, b2_vec);
464
- b2_vec = _mm256_hadd_ps(b2_vec, b2_vec);
465
- float ab_result[1], a2_result[1], b2_result[1];
466
- _mm_store_ss(ab_result, _mm256_castps256_ps128(ab_vec));
467
- _mm_store_ss(a2_result, _mm256_castps256_ps128(a2_vec));
468
- _mm_store_ss(b2_result, _mm256_castps256_ps128(b2_vec));
469
- return 1 - ab_result[0] / (sqrtf(a2_result[0]) * sqrtf(b2_result[0]));
470
- }
471
-
472
- __attribute__((target("avx2"))) //
473
- __attribute__((target("f16c"))) //
474
- __attribute__((target("fma"))) inline static simsimd_f32_t //
475
- simsimd_l2sq_f16x8_avx2(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
476
- __m256 sum_vec = _mm256_set1_ps(0);
477
- for (size_t i = 0; i != d; i += 8) {
478
- __m256 a_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(a + i)));
479
- __m256 b_vec = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const*)(b + i)));
480
- __m256 diff_vec = _mm256_sub_ps(a_vec, b_vec);
481
- sum_vec = _mm256_fmadd_ps(diff_vec, diff_vec, sum_vec);
482
- }
483
- sum_vec = _mm256_add_ps(_mm256_permute2f128_ps(sum_vec, sum_vec, 0x81), sum_vec);
484
- sum_vec = _mm256_hadd_ps(sum_vec, sum_vec);
485
- sum_vec = _mm256_hadd_ps(sum_vec, sum_vec);
486
- float result[1];
487
- _mm_store_ss(result, _mm256_castps256_ps128(sum_vec));
488
- return result[0];
489
- }
490
111
 
491
- __attribute__((target("avx2"))) //
492
- inline static simsimd_f32_t
493
- simsimd_cos_i8x32_avx2(int8_t const* a, int8_t const* b, size_t d) {
112
+ /// The states of 4 registers populated for a specific "cpuid" assmebly call
113
+ union {
114
+ int array[4];
115
+ struct {
116
+ unsigned eax, ebx, ecx, edx;
117
+ };
118
+ } info1, info7;
494
119
 
495
- __m256i ab_vec = _mm256_setzero_si256();
496
- __m256i a2_vec = _mm256_setzero_si256();
497
- __m256i b2_vec = _mm256_setzero_si256();
498
-
499
- for (size_t i = 0; i != d; i += 32) {
500
- __m256i a_vec = _mm256_loadu_si256((__m256i const*)(a + i));
501
- __m256i b_vec = _mm256_loadu_si256((__m256i const*)(b + i));
502
-
503
- // Multiply and add packed 8-bit integers
504
- __m256i ab_part_vec = _mm256_maddubs_epi16(a_vec, b_vec);
505
- __m256i a2_part_vec = _mm256_maddubs_epi16(a_vec, a_vec);
506
- __m256i b2_part_vec = _mm256_maddubs_epi16(b_vec, b_vec);
507
-
508
- ab_vec = _mm256_add_epi32(ab_vec, ab_part_vec);
509
- a2_vec = _mm256_add_epi32(a2_vec, a2_part_vec);
510
- b2_vec = _mm256_add_epi32(b2_vec, b2_part_vec);
511
- }
512
-
513
- // Horizontal sum across the 256-bit register
514
- __m128i ab_low = _mm256_extracti128_si256(ab_vec, 0);
515
- __m128i ab_high = _mm256_extracti128_si256(ab_vec, 1);
516
- __m128i ab_sum = _mm_add_epi32(ab_low, ab_high);
120
+ #ifdef _MSC_VER
121
+ __cpuidex(info1.array, 1, 0);
122
+ __cpuidex(info7.array, 7, 0);
123
+ #else
124
+ __asm__ __volatile__("cpuid" : "=a"(info1.eax), "=b"(info1.ebx), "=c"(info1.ecx), "=d"(info1.edx) : "a"(1), "c"(0));
125
+ __asm__ __volatile__("cpuid" : "=a"(info7.eax), "=b"(info7.ebx), "=c"(info7.ecx), "=d"(info7.edx) : "a"(7), "c"(0));
126
+ #endif
517
127
 
518
- __m128i a2_low = _mm256_extracti128_si256(a2_vec, 0);
519
- __m128i a2_high = _mm256_extracti128_si256(a2_vec, 1);
520
- __m128i a2_sum = _mm_add_epi32(a2_low, a2_high);
128
+ // Check for AVX2 (Function ID 7, EBX register)
129
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
130
+ unsigned supports_avx2 = (info7.ebx & 0x00000020) != 0;
131
+ // Check for F16C (Function ID 1, ECX register)
132
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L107
133
+ unsigned supports_f16c = (info1.ecx & 0x20000000) != 0;
134
+ // Check for AVX512F (Function ID 7, EBX register)
135
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
136
+ unsigned supports_avx512f = (info7.ebx & 0x00010000) != 0;
137
+ // Check for AVX512FP16 (Function ID 7, EDX register)
138
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L198C9-L198C23
139
+ unsigned supports_avx512fp16 = (info7.edx & 0x00800000) != 0;
140
+ // Check for VPOPCNTDQ (Function ID 1, ECX register)
141
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L182C30-L182C40
142
+ unsigned supports_avx512vpopcntdq = (info1.ecx & 0x00004000) != 0;
143
+
144
+ return (simsimd_capability_t)( //
145
+ (simsimd_cap_x86_avx2_k * supports_avx2) | //
146
+ (simsimd_cap_x86_avx512_k * supports_avx512f) | //
147
+ (simsimd_cap_x86_avx2fp16_k * (supports_avx2 && supports_f16c)) | //
148
+ (simsimd_cap_x86_avx512fp16_k * (supports_avx512fp16 && supports_avx512f)) | //
149
+ (simsimd_cap_x86_avx512vpopcntdq_k * (supports_avx512vpopcntdq && supports_avx512vpopcntdq)) //
150
+ | (simsimd_cap_serial_k));
521
151
 
522
- __m128i b2_low = _mm256_extracti128_si256(b2_vec, 0);
523
- __m128i b2_high = _mm256_extracti128_si256(b2_vec, 1);
524
- __m128i b2_sum = _mm_add_epi32(b2_low, b2_high);
152
+ #endif // SIMSIMD_TARGET_X86
525
153
 
526
- // Further reduce to a single sum for each vector
527
- int ab = _mm_extract_epi32(_mm_hadd_epi32(_mm_hadd_epi32(ab_sum, ab_sum), ab_sum), 0);
528
- int a2 = _mm_extract_epi32(_mm_hadd_epi32(_mm_hadd_epi32(a2_sum, a2_sum), a2_sum), 0);
529
- int b2 = _mm_extract_epi32(_mm_hadd_epi32(_mm_hadd_epi32(b2_sum, b2_sum), b2_sum), 0);
154
+ #if SIMSIMD_TARGET_ARM
530
155
 
531
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
532
- }
156
+ // Every 64-bit Arm CPU supports NEON
157
+ unsigned supports_neon = 1;
158
+ unsigned supports_sve = 0;
159
+ unsigned supports_sve2 = 0;
533
160
 
534
- #endif // SIMSIMD_TARGET_X86_AVX2
161
+ #ifdef __linux__
162
+ unsigned long hwcap = getauxval(AT_HWCAP);
163
+ unsigned long hwcap2 = getauxval(AT_HWCAP2);
164
+ supports_sve = (hwcap & HWCAP_SVE) != 0;
165
+ supports_sve2 = (hwcap2 & HWCAP2_SVE2) != 0;
166
+ #endif
535
167
 
536
- #if SIMSIMD_TARGET_X86_AVX512
168
+ return (simsimd_capability_t)( //
169
+ (simsimd_cap_arm_neon_k * supports_neon) | //
170
+ (simsimd_cap_arm_sve_k * supports_sve) | //
171
+ (simsimd_cap_arm_sve2_k * supports_sve2) | //
172
+ (simsimd_cap_serial_k));
537
173
 
538
- __attribute__((target("avx512fp16"))) //
539
- __attribute__((target("avx512f"))) inline static simsimd_f32_t //
540
- simsimd_dot_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
541
- __m512 ab_vec = _mm512_set1_ps(0);
542
- for (size_t i = 0; i != d; i += 16) {
543
- __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
544
- __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
545
- ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
546
- }
547
- simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
548
- return 1 - ab;
549
- }
174
+ #endif // SIMSIMD_TARGET_ARM
550
175
 
551
- __attribute__((target("avx512fp16"))) //
552
- __attribute__((target("avx512f"))) inline static simsimd_f32_t //
553
- simsimd_cos_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
554
- __m512 ab_vec = _mm512_set1_ps(0);
555
- __m512 a2_vec = _mm512_set1_ps(0);
556
- __m512 b2_vec = _mm512_set1_ps(0);
557
- for (size_t i = 0; i != d; i += 16) {
558
- __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
559
- __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
560
- ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
561
- a2_vec = _mm512_fmadd_ps(a_vec, a_vec, a2_vec);
562
- b2_vec = _mm512_fmadd_ps(b_vec, b_vec, b2_vec);
563
- }
564
- simsimd_f32_t ab = _mm512_reduce_add_ps(ab_vec);
565
- simsimd_f32_t a2 = _mm512_reduce_add_ps(a2_vec);
566
- simsimd_f32_t b2 = _mm512_reduce_add_ps(b2_vec);
567
- return 1 - ab / (sqrtf(a2) * sqrtf(b2));
176
+ return simsimd_cap_serial_k;
568
177
  }
569
178
 
570
- __attribute__((target("avx512fp16"))) //
571
- __attribute__((target("avx512f"))) inline static simsimd_f32_t //
572
- simsimd_l2sq_f16x16_avx512(simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) {
573
- __m512 sum_vec = _mm512_set1_ps(0);
574
- for (size_t i = 0; i != d; i += 16) {
575
- __m512 a_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(a + i));
576
- __m512 b_vec = _mm512_cvtxph_ps(_mm256_loadu_ph(b + i));
577
- __m512 diff_vec = _mm512_sub_ps(a_vec, b_vec);
578
- sum_vec = _mm512_fmadd_ps(diff_vec, diff_vec, sum_vec);
179
+ /**
180
+ * @brief Determines the best suited metric implementation based on the given datatype,
181
+ * supported and allowed by hardware capabilities.
182
+ *
183
+ * @param kind The kind of metric to be evaluated.
184
+ * @param datatype The data type for which the metric needs to be evaluated.
185
+ * @param supported The hardware capabilities supported by the CPU.
186
+ * @param allowed The hardware capabilities allowed for use.
187
+ * @param metric_output Output variable for the selected similarity function.
188
+ * @param capability_output Output variable for the utilized hardware capabilities.
189
+ */
190
+ inline static void simsimd_find_metric_punned( //
191
+ simsimd_metric_kind_t kind, //
192
+ simsimd_datatype_t datatype, //
193
+ simsimd_capability_t supported, //
194
+ simsimd_capability_t allowed, //
195
+ simsimd_metric_punned_t* metric_output, //
196
+ simsimd_capability_t* capability_output) {
197
+
198
+ simsimd_metric_punned_t* m = metric_output;
199
+ simsimd_capability_t* c = capability_output;
200
+ simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
201
+ *m = (simsimd_metric_punned_t)0;
202
+ *c = (simsimd_capability_t)0;
203
+
204
+ // clang-format off
205
+ switch (datatype) {
206
+
207
+ case simsimd_datatype_unknown_k: break;
208
+ case simsimd_datatype_f64_k: break;
209
+
210
+ // Single-precision floating-point vectors
211
+ case simsimd_datatype_f32_k:
212
+
213
+ #if SIMSIMD_TARGET_ARM_NEON
214
+ if (viable & simsimd_cap_arm_neon_k)
215
+ switch (kind) {
216
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f32_ip, *c = simsimd_cap_arm_neon_k; break;
217
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f32_cos, *c = simsimd_cap_arm_neon_k; break;
218
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f32_l2sq, *c = simsimd_cap_arm_neon_k; break;
219
+ case simsimd_metric_js_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f32_js, *c = simsimd_cap_arm_neon_k; break;
220
+ case simsimd_metric_kl_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f32_kl, *c = simsimd_cap_arm_neon_k; break;
221
+ }
222
+ #endif
223
+ #if SIMSIMD_TARGET_X86_AVX512
224
+ if (viable & simsimd_cap_x86_avx512_k)
225
+ switch (kind) {
226
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f32_ip, *c = simsimd_cap_x86_avx512_k; break;
227
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f32_cos, *c = simsimd_cap_x86_avx512_k; break;
228
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f32_l2sq, *c = simsimd_cap_x86_avx512_k; break;
229
+ }
230
+ #endif
231
+ if (viable & simsimd_cap_serial_k)
232
+ switch (kind) {
233
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f32_ip, *c = simsimd_cap_serial_k; break;
234
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f32_cos, *c = simsimd_cap_serial_k; break;
235
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f32_l2sq, *c = simsimd_cap_serial_k; break;
236
+ case simsimd_metric_js_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f32_js, *c = simsimd_cap_serial_k; break;
237
+ case simsimd_metric_kl_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f32_kl, *c = simsimd_cap_serial_k; break;
238
+ }
239
+
240
+ break;
241
+
242
+ // Half-precision floating-point vectors
243
+ case simsimd_datatype_f16_k:
244
+
245
+ #if SIMSIMD_TARGET_ARM_NEON
246
+ if (viable & simsimd_cap_arm_neon_k)
247
+ switch (kind) {
248
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f16_ip, *c = simsimd_cap_arm_neon_k; break;
249
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f16_cos, *c = simsimd_cap_arm_neon_k; break;
250
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f16_l2sq, *c = simsimd_cap_arm_neon_k; break;
251
+ case simsimd_metric_js_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f16_js, *c = simsimd_cap_arm_neon_k; break;
252
+ case simsimd_metric_kl_k: *m = (simsimd_metric_punned_t)&simsimd_neon_f16_kl, *c = simsimd_cap_arm_neon_k; break;
253
+ }
254
+ #endif
255
+ #if SIMSIMD_TARGET_ARM_SVE
256
+ if (viable & simsimd_cap_arm_sve_k)
257
+ switch (kind) {
258
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_sve_f16_ip, *c = simsimd_cap_arm_sve_k; break;
259
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_sve_f16_cos, *c = simsimd_cap_arm_sve_k; break;
260
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_sve_f16_l2sq, *c = simsimd_cap_arm_sve_k; break;
261
+ }
262
+ #endif
263
+ #if SIMSIMD_TARGET_X86_AVX2
264
+ if (viable & simsimd_cap_x86_avx2_k)
265
+ switch (kind) {
266
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_f16_ip, *c = simsimd_cap_x86_avx2_k; break;
267
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_f16_cos, *c = simsimd_cap_x86_avx2_k; break;
268
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_f16_l2sq, *c = simsimd_cap_x86_avx2_k; break;
269
+ }
270
+ #endif
271
+ #if SIMSIMD_TARGET_X86_AVX512
272
+ if (viable & simsimd_cap_x86_avx512_k)
273
+ switch (kind) {
274
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f16_ip, *c = simsimd_cap_x86_avx512_k; break;
275
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f16_cos, *c = simsimd_cap_x86_avx512_k; break;
276
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_f16_l2sq, *c = simsimd_cap_x86_avx512_k; break;
277
+ }
278
+ #endif
279
+
280
+ if (viable & simsimd_cap_serial_k)
281
+ switch (kind) {
282
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f16_ip, *c = simsimd_cap_serial_k; break;
283
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f16_cos, *c = simsimd_cap_serial_k; break;
284
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f16_l2sq, *c = simsimd_cap_serial_k; break;
285
+ case simsimd_metric_js_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f16_js, *c = simsimd_cap_serial_k; break;
286
+ case simsimd_metric_kl_k: *m = (simsimd_metric_punned_t)&simsimd_serial_f16_kl, *c = simsimd_cap_serial_k; break;
287
+ }
288
+
289
+ break;
290
+
291
+ // Single-byte integer vectors
292
+ case simsimd_datatype_i8_k:
293
+ #if SIMSIMD_TARGET_ARM_NEON
294
+ if (viable & simsimd_cap_arm_neon_k)
295
+ switch (kind) {
296
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_neon_i8_ip, *c = simsimd_cap_arm_neon_k; break;
297
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_neon_i8_cos, *c = simsimd_cap_arm_neon_k; break;
298
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_neon_i8_l2sq, *c = simsimd_cap_arm_neon_k; break;
299
+ }
300
+ #endif
301
+ #if SIMSIMD_TARGET_X86_AVX2
302
+ if (viable & simsimd_cap_x86_avx2_k)
303
+ switch (kind) {
304
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_i8_ip, *c = simsimd_cap_x86_avx2_k; break;
305
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_i8_cos, *c = simsimd_cap_x86_avx2_k; break;
306
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx2_i8_l2sq, *c = simsimd_cap_x86_avx2_k; break;
307
+ }
308
+ #endif
309
+ #if SIMSIMD_TARGET_X86_AVX512
310
+ if (viable & simsimd_cap_x86_avx512_k)
311
+ switch (kind) {
312
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_ip, *c = simsimd_cap_x86_avx512_k; break;
313
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_cos, *c = simsimd_cap_x86_avx512_k; break;
314
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_i8_l2sq, *c = simsimd_cap_x86_avx512_k; break;
315
+ }
316
+ #endif
317
+
318
+ if (viable & simsimd_cap_serial_k)
319
+ switch (kind) {
320
+ case simsimd_metric_ip_k: *m = (simsimd_metric_punned_t)&simsimd_serial_i8_ip, *c = simsimd_cap_serial_k; break;
321
+ case simsimd_metric_cos_k: *m = (simsimd_metric_punned_t)&simsimd_serial_i8_cos, *c = simsimd_cap_serial_k; break;
322
+ case simsimd_metric_l2sq_k: *m = (simsimd_metric_punned_t)&simsimd_serial_i8_l2sq, *c = simsimd_cap_serial_k; break;
323
+ }
324
+
325
+ break;
326
+
327
+ // Binary vectors
328
+ case simsimd_datatype_b8_k:
329
+
330
+ #if SIMSIMD_TARGET_ARM_NEON
331
+ if (viable & simsimd_cap_arm_neon_k)
332
+ switch (kind) {
333
+ case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_hamming, *c = simsimd_cap_arm_neon_k; break;
334
+ case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_neon_b8_jaccard, *c = simsimd_cap_arm_neon_k; break;
335
+ }
336
+ #endif
337
+ #if SIMSIMD_TARGET_ARM_SVE
338
+ if (viable & simsimd_cap_arm_sve_k)
339
+ switch (kind) {
340
+ case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_sve_b8_hamming, *c = simsimd_cap_arm_sve_k; break;
341
+ case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_sve_b8_jaccard, *c = simsimd_cap_arm_sve_k; break;
342
+ }
343
+ #endif
344
+ #if SIMSIMD_TARGET_X86_AVX512
345
+ if (viable & simsimd_cap_x86_avx512vpopcntdq_k)
346
+ switch (kind) {
347
+ case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_b8_hamming, *c = simsimd_cap_x86_avx512vpopcntdq_k; break;
348
+ case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_avx512_b8_jaccard, *c = simsimd_cap_x86_avx512vpopcntdq_k; break;
349
+ }
350
+ #endif
351
+
352
+ if (viable & simsimd_cap_serial_k)
353
+ switch (kind) {
354
+ case simsimd_metric_hamming_k: *m = (simsimd_metric_punned_t)&simsimd_serial_b8_hamming, *c = simsimd_cap_serial_k; break;
355
+ case simsimd_metric_jaccard_k: *m = (simsimd_metric_punned_t)&simsimd_serial_b8_jaccard, *c = simsimd_cap_serial_k; break;
356
+ }
357
+
358
+ break;
579
359
  }
580
- simsimd_f32_t sum = _mm512_reduce_add_ps(sum_vec);
581
- return sum;
582
- }
583
-
584
- __attribute__((target("avx512vpopcntdq"))) //
585
- __attribute__((target("avx512vl"))) //
586
- __attribute__((target("avx512f"))) inline static simsimd_f32_t //
587
- simsimd_hamming_b1x128_avx512(uint8_t const* a, uint8_t const* b, size_t d) {
588
- size_t words = d / 128;
589
- uint64_t const* a64 = (uint64_t const*)(a);
590
- uint64_t const* b64 = (uint64_t const*)(b);
591
- /// Contains 2x 64-bit integers with running population count sums.
592
- __m128i d_vec = _mm_set_epi64x(0, 0);
593
- for (size_t i = 0; i != words; i += 2)
594
- d_vec = _mm_add_epi64( //
595
- d_vec, //
596
- _mm_popcnt_epi64( //
597
- _mm_xor_si128( //
598
- _mm_load_si128((__m128i const*)(a64 + i)), _mm_load_si128((__m128i const*)(b64 + i)))));
599
- return _mm_cvtm64_si64(_mm_movepi64_pi64(d_vec)) + _mm_extract_epi64(d_vec, 1);
360
+ // clang-format on
600
361
  }
601
362
 
602
- #endif // SIMSIMD_TARGET_X86_AVX512
603
- #endif // SIMSIMD_TARGET_X86
604
-
605
- inline static simsimd_f32_t //
606
- simsimd_tanimoto_b1x8_naive(uint8_t const* a, uint8_t const* b, size_t d) {
607
- size_t and_count = 0, or_count = 0;
608
- uint8_t const* a_end = a + d / 8;
609
- // Misaligned prefix
610
- for (; a != a_end && (size_t)(a) % 8 != 0; ++a, ++b)
611
- and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
612
- // Properly aligned body
613
- for (; a + 8 <= a_end; a += 8, b += 8)
614
- and_count += popcount64(*(uint64_t*)a & *(uint64_t*)b), or_count += popcount64(*(uint64_t*)a | *(uint64_t*)b);
615
- // Misaligned suffix
616
- for (; a != a_end; ++a, ++b)
617
- and_count += popcount32(*a & *b), or_count += popcount32(*a | *b);
618
- return 1 - (simsimd_f32_t)(and_count) / or_count;
363
+ /**
364
+ * @brief Selects the most suitable metric implementation based on the given metric kind, datatype,
365
+ * and allowed capabilities. @b Don't call too often and prefer caching the `simsimd_capabilities()`.
366
+ *
367
+ * @param kind The kind of metric to be evaluated.
368
+ * @param datatype The data type for which the metric needs to be evaluated.
369
+ * @param allowed The hardware capabilities allowed for use.
370
+ * @return A function pointer to the selected metric implementation.
371
+ */
372
+ inline static simsimd_metric_punned_t simsimd_metric_punned( //
373
+ simsimd_metric_kind_t kind, //
374
+ simsimd_datatype_t datatype, //
375
+ simsimd_capability_t allowed) {
376
+
377
+ simsimd_metric_punned_t result = 0;
378
+ simsimd_capability_t c = simsimd_cap_serial_k;
379
+ simsimd_capability_t supported = simsimd_capabilities();
380
+ simsimd_find_metric_punned(kind, datatype, supported, allowed, &result, &c);
381
+ return result;
619
382
  }
620
383
 
621
- #undef popcount32
622
- #undef popcount64
623
-
624
384
  #ifdef __cplusplus
625
385
  }
626
- #endif
386
+ #endif