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.
- package/.github/workflows/Dockerfile.libs +4 -6
- package/.github/workflows/prerelease.yml +4 -1
- package/.github/workflows/release.yml +8 -28
- package/.github/workflows/update_version.sh +1 -0
- package/CITATION.cff +2 -2
- package/CMakeLists.txt +1 -1
- package/Cargo.toml +1 -1
- package/README.md +4 -4
- package/binding.gyp +1 -1
- package/build.gradle +20 -5
- package/conanfile.py +1 -1
- package/csharp/nuget/nuget-package.props +1 -1
- package/docs/compilation.md +0 -2
- package/golang/README.md +0 -1
- package/include/usearch/index.hpp +1 -1
- package/include/usearch/index_plugins.hpp +119 -243
- package/package.json +1 -1
- package/simsimd/.github/workflows/prerelease.yml +33 -58
- package/simsimd/.github/workflows/release.yml +22 -9
- package/simsimd/.github/workflows/update_version.sh +3 -1
- package/simsimd/.releaserc +9 -1
- package/simsimd/.vscode/launch.json +3 -0
- package/simsimd/.vscode/settings.json +15 -2
- package/simsimd/.vscode/tasks.json +2 -2
- package/simsimd/CMakeLists.txt +36 -55
- package/simsimd/README.md +163 -109
- package/simsimd/binding.gyp +18 -0
- package/simsimd/cpp/bench.cxx +187 -0
- package/simsimd/go.mod +3 -0
- package/simsimd/golang/bench_test.go +40 -0
- package/simsimd/golang/simsimd.go +47 -0
- package/simsimd/golang/unit_test.go +40 -0
- package/simsimd/include/simsimd/binary.h +186 -0
- package/simsimd/include/simsimd/probability.h +645 -0
- package/simsimd/include/simsimd/simsimd.h +335 -575
- package/simsimd/include/simsimd/spatial.h +924 -0
- package/simsimd/include/simsimd/types.h +196 -0
- package/simsimd/javascript/bench.js +117 -0
- package/simsimd/javascript/lib.c +102 -0
- package/simsimd/javascript/simsimd.js +45 -0
- package/simsimd/javascript/test.js +29 -0
- package/simsimd/package.json +31 -2
- package/simsimd/pyproject.toml +5 -6
- package/simsimd/python/bench.ipynb +187 -0
- package/simsimd/python/bench.py +268 -0
- package/simsimd/python/lib.c +453 -72
- package/simsimd/python/test.py +167 -35
- package/simsimd/setup.py +51 -8
- package/wasmer.toml +1 -1
- package/simsimd/bench.cxx +0 -137
- package/simsimd/include/simsimd/simsimd_bio.h +0 -54
- package/simsimd/include/simsimd/simsimd_chem.h +0 -167
- package/simsimd/include/simsimd/simsimd_text.h +0 -92
- package/simsimd/python/run.sh +0 -1
- package/simsimd/python/test.c +0 -109
|
@@ -1,626 +1,386 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @brief
|
|
3
|
-
*
|
|
4
|
-
* @author
|
|
5
|
-
* @date
|
|
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
|
|
14
|
-
#include
|
|
15
|
-
#include
|
|
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
|
-
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
236
|
-
|
|
45
|
+
simsimd_metric_l2sq_k = 'e', ///< Squared Euclidean distance
|
|
46
|
+
simsimd_metric_sqeuclidean_k = 'e', ///< Squared Euclidean distance alias
|
|
237
47
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
245
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
352
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
//
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|