usearch 2.11.1 → 2.13.1

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 (50) hide show
  1. package/README.md +100 -30
  2. package/binding.gyp +11 -12
  3. package/c/README.md +3 -0
  4. package/c/lib.cpp +50 -13
  5. package/c/test.c +60 -55
  6. package/c/usearch.h +16 -0
  7. package/cpp/CMakeLists.txt +6 -0
  8. package/cpp/README.md +34 -13
  9. package/cpp/bench.cpp +99 -47
  10. package/cpp/test.cpp +947 -132
  11. package/include/usearch/index.hpp +894 -272
  12. package/include/usearch/index_dense.hpp +418 -156
  13. package/include/usearch/index_plugins.hpp +99 -49
  14. package/javascript/dist/cjs/usearch.d.ts +39 -1
  15. package/javascript/dist/cjs/usearch.js +11 -1
  16. package/javascript/dist/esm/usearch.d.ts +39 -1
  17. package/javascript/dist/esm/usearch.js +11 -1
  18. package/javascript/tsconfig-esm.json +3 -2
  19. package/javascript/usearch.ts +18 -7
  20. package/package.json +2 -2
  21. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  22. package/prebuilds/linux-arm64/usearch.node +0 -0
  23. package/prebuilds/linux-x64/usearch.node +0 -0
  24. package/simsimd/.cmake-format.py +19 -0
  25. package/simsimd/CONTRIBUTING.md +49 -8
  26. package/simsimd/Cargo.lock +1 -1
  27. package/simsimd/Cargo.toml +10 -5
  28. package/simsimd/Package.swift +16 -0
  29. package/simsimd/README.md +69 -15
  30. package/simsimd/binding.gyp +14 -19
  31. package/simsimd/build.rs +2 -0
  32. package/simsimd/c/lib.c +18 -0
  33. package/simsimd/include/module.modulemap +4 -0
  34. package/simsimd/include/simsimd/binary.h +2 -0
  35. package/simsimd/include/simsimd/dot.h +466 -32
  36. package/simsimd/include/simsimd/probability.h +20 -10
  37. package/simsimd/include/simsimd/simsimd.h +444 -98
  38. package/simsimd/include/simsimd/spatial.h +480 -44
  39. package/simsimd/include/simsimd/types.h +161 -39
  40. package/simsimd/javascript/simsimd.ts +26 -2
  41. package/simsimd/package.json +1 -1
  42. package/simsimd/rust/benches/cosine.rs +8 -2
  43. package/simsimd/rust/benches/native.rs +240 -2
  44. package/simsimd/rust/benches/sqeuclidean.rs +8 -2
  45. package/simsimd/rust/lib.rs +50 -13
  46. package/simsimd/swift/source/SimSIMD.swift +123 -0
  47. package/simsimd/swift/tests/SimSIMDTests.swift +92 -0
  48. package/prebuilds/win32-ia32/usearch.node +0 -0
  49. package/prebuilds/win32-x64/usearch.node +0 -0
  50. package/simsimd/.releaserc +0 -87
@@ -6,17 +6,89 @@
6
6
  * @copyright Copyright (c) 2023
7
7
  *
8
8
  * References:
9
- * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
10
- * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
9
+ * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide
10
+ * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics
11
11
  * Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
12
+ *
13
+ * @section Choosing x86 Target Generations
14
+ *
15
+ * It's important to provide fine-grained controls over AVX512 families, as they are very fragmented:
16
+ *
17
+ * - Intel Skylake servers: F, CD, VL, DQ, BW
18
+ * - Intel Cascade Lake workstations: F, CD, VL, DQ, BW, VNNI
19
+ * > In other words, it extends Skylake with VNNI support
20
+ * - Intel Sunny Cove (Ice Lake) servers:
21
+ * F, CD, VL, DQ, BW, VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ
22
+ * - AMD Zen4 (Genoa):
23
+ * F, CD, VL, DQ, BW, VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, BF16
24
+ * > In other words, it extends Sunny Cove with BF16 support
25
+ * - Intel Golden Cove (Sapphire Rapids): extends Zen4 and Sunny Cove with FP16 support
26
+ *
27
+ * Intel Palm Cove was an irrelevant intermediate release extending Skylake with IFMA and VBMI.
28
+ * Intel Willow Cove was an irrelevant intermediate release extending Sunny Cove with VP2INTERSECT,
29
+ * that aren't supported by any other CPU built to date... and those are only available in Tiger Lake laptops.
30
+ * Intel Cooper Lake was the only intermediary platform, that supported BF16, but not FP16.
31
+ * It's mostly used in 4-socket and 8-socket high-memory configurations.
32
+ *
33
+ * In practical terms, it makes sense to differentiate only 3 AVX512 generations:
34
+ * 1. Intel Skylake (pre 2019): supports single-precision dot-products.
35
+ * 2. Intel Ice Lake (2019-2021): advanced integer algorithms.
36
+ * 3. AMD Genoa (2023+): brain-floating point support.
37
+ * 4. Intel Sapphire Rapids (2023+): advanced mixed-precision float processing.
38
+ *
39
+ * To list all available macros for x86, take a recent compiler, like GCC 12 and run:
40
+ * gcc-12 -march=sapphirerapids -dM -E - < /dev/null | egrep "SSE|AVX" | sort
41
+ * On Arm machines you may want to check for other flags:
42
+ * gcc-12 -march=native -dM -E - < /dev/null | egrep "NEON|SVE|FP16|FMA" | sort
43
+ *
44
+ * @section Choosing Arm Target Generations
45
+ *
46
+ * Arm CPUs share design IP, but are produced by different vendors, potentially making the platform
47
+ * even more fragmented than x86. There are 2 imporatant families of SIMD extensions - NEON and SVE.
48
+ *
49
+ * - Armv8-A: +fp, +simd
50
+ * - Armv8.1-A: armv8-a, +crc, +lse, +rdma
51
+ * - Armv8.2-A: armv8.1-a
52
+ * - Armv8.3-A: armv8.2-a, +pauth
53
+ * - Armv8.4-A: armv8.3-a, +flagm, +fp16fml, +dotprod
54
+ * - Armv8.5-A: armv8.4-a, +sb, +ssbs, +predres
55
+ * - Armv8.6-A: armv8.5-a, +bf16, +i8mm
56
+ * - Armv8.7-A: armv8.6-a, +ls64
57
+ * - Armv8.8-A: armv8.7-a, +mops
58
+ * - Armv8.9-A: armv8.8-a
59
+ * - Armv9-A: armv8.5-a, +sve, +sve2
60
+ * - Armv9.1-A: armv9-a, +bf16, +i8mm
61
+ * - Armv9.2-A: armv9.1-a, +ls64
62
+ * - Armv9.3-A: armv9.2-a, +mops
63
+ * - Armv9.4-A: armv9.3-a
64
+ *
65
+ * SVE has been optional since Armv8.2-A, but it's a requirement for Armv9.0-A.
66
+ * A 512-bit SVE variant has already been implemented on the Fugaku supercomputer.
67
+ * A more flexible version, 2x256 SVE, was implemented by the AWS Graviton3 ARM processor.
68
+ * Here are the most important recent families of CPU cores designed by Arm:
69
+ *
70
+ * - Neoverse N1: armv8.2-a, extended with Armv8.4 "dotprod" instructions.
71
+ * Used in AWS @b Graviton2 and Amere @b Altra.
72
+ * https://developer.arm.com/Processors/Neoverse%20N1
73
+ * - Neoverse V1: armv8.4-a, extended with Armv8.6 bfloat/int8 "matmul" instructions.
74
+ * Used in AWS @b Graviton3, which also enables `sve`, `svebf16`, and `svei8mm`.
75
+ * https://developer.arm.com/Processors/Neoverse%20V1
76
+ * - Neoverse V2: armv9.0 with SVE2 and SVE bit-permutes
77
+ * Used in AWS @b Graviton4, NVIDIA @b Grace, Google @b Axion.
78
+ * https://developer.arm.com/Processors/Neoverse%20V2
79
+ * The N2 core is very similar to V2 and is used by Microsoft @b Cobalt.
80
+ * https://developer.arm.com/Processors/Neoverse%20N2
81
+ *
82
+ * On Consumer side, Apple is the biggest player with mobile @b A chips and desktop @b M chips.
83
+ * The M1 implements Armv8.5-A, both M2 and M3 implement Armv8.6-A, and M4 is expected to have Armv9.1-A.
12
84
  */
13
85
 
14
86
  #ifndef SIMSIMD_H
15
87
  #define SIMSIMD_H
16
88
 
17
- #define SIMSIMD_VERSION_MAJOR 4
18
- #define SIMSIMD_VERSION_MINOR 2
19
- #define SIMSIMD_VERSION_PATCH 2
89
+ #define SIMSIMD_VERSION_MAJOR 5
90
+ #define SIMSIMD_VERSION_MINOR 0
91
+ #define SIMSIMD_VERSION_PATCH 0
20
92
 
21
93
  /**
22
94
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -34,11 +106,9 @@
34
106
  #include "probability.h" // Kullback-Leibler, Jensen–Shannon
35
107
  #include "spatial.h" // L2, Cosine
36
108
 
37
- #if SIMSIMD_TARGET_ARM
38
- #ifdef __linux__
39
- #include <asm/hwcap.h>
40
- #include <sys/auxv.h>
41
- #endif
109
+ // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
110
+ #if defined(SIMSIMD_DEFINED_APPLE)
111
+ #include <sys/sysctl.h>
42
112
  #endif
43
113
 
44
114
  #ifdef __cplusplus
@@ -88,14 +158,20 @@ typedef enum {
88
158
  simsimd_cap_serial_k = 1, ///< Serial (non-SIMD) capability
89
159
  simsimd_cap_any_k = 0x7FFFFFFF, ///< Mask representing any capability with `INT_MAX`
90
160
 
91
- simsimd_cap_neon_k = 1 << 10, ///< ARM NEON capability
92
- simsimd_cap_sve_k = 1 << 11, ///< ARM SVE capability
93
- simsimd_cap_sve2_k = 1 << 12, ///< ARM SVE2 capability
94
-
95
- simsimd_cap_haswell_k = 1 << 20, ///< x86 AVX2 capability with FMA and F16C extensions
96
- simsimd_cap_skylake_k = 1 << 21, ///< x86 AVX512 baseline capability
97
- simsimd_cap_ice_k = 1 << 22, ///< x86 AVX512 capability with advanced integer algos
98
- simsimd_cap_sapphire_k = 1 << 23, ///< x86 AVX512 capability with `f16` support
161
+ simsimd_cap_haswell_k = 1 << 10, ///< x86 AVX2 capability with FMA and F16C extensions
162
+ simsimd_cap_skylake_k = 1 << 11, ///< x86 AVX512 baseline capability
163
+ simsimd_cap_ice_k = 1 << 12, ///< x86 AVX512 capability with advanced integer algos
164
+ simsimd_cap_genoa_k = 1 << 13, ///< x86 AVX512 capability with `bf16` support
165
+ simsimd_cap_sapphire_k = 1 << 14, ///< x86 AVX512 capability with `f16` support
166
+
167
+ simsimd_cap_neon_k = 1 << 20, ///< ARM NEON capability
168
+ simsimd_cap_neon_f16_k = 1 << 21, ///< ARM NEON FP16 capability
169
+ simsimd_cap_neon_bf16_k = 1 << 22, ///< ARM NEON BF16 capability
170
+ simsimd_cap_neon_i8_k = 1 << 23, ///< ARM NEON Int8 capability
171
+ simsimd_cap_sve_k = 1 << 24, ///< ARM SVE capability
172
+ simsimd_cap_sve_f16_k = 1 << 25, ///< ARM SVE FP16 capability
173
+ simsimd_cap_sve_bf16_k = 1 << 26, ///< ARM SVE BF16 capability
174
+ simsimd_cap_sve_i8_k = 1 << 27, ///< ARM SVE Int8 capability
99
175
 
100
176
  } simsimd_capability_t;
101
177
 
@@ -107,16 +183,20 @@ typedef enum {
107
183
  * interfaces.
108
184
  */
109
185
  typedef enum {
110
- simsimd_datatype_unknown_k, ///< Unknown data type
111
- simsimd_datatype_f64_k, ///< Double precision floating point
112
- simsimd_datatype_f32_k, ///< Single precision floating point
113
- simsimd_datatype_f16_k, ///< Half precision floating point
114
- simsimd_datatype_i8_k, ///< 8-bit integer
115
- simsimd_datatype_b8_k, ///< Single-bit values packed into 8-bit words
116
-
117
- simsimd_datatype_f64c_k, ///< Complex double precision floating point
118
- simsimd_datatype_f32c_k, ///< Complex single precision floating point
119
- simsimd_datatype_f16c_k, ///< Complex half precision floating point
186
+ simsimd_datatype_unknown_k = 0, ///< Unknown data type
187
+
188
+ simsimd_datatype_b8_k = 1 << 1, ///< Single-bit values packed into 8-bit words
189
+ simsimd_datatype_i8_k = 1 << 2, ///< 8-bit integer
190
+
191
+ simsimd_datatype_f64_k = 1 << 10, ///< Double precision floating point
192
+ simsimd_datatype_f32_k = 1 << 11, ///< Single precision floating point
193
+ simsimd_datatype_f16_k = 1 << 12, ///< Half precision floating point
194
+ simsimd_datatype_bf16_k = 1 << 13, ///< Brain floating point
195
+
196
+ simsimd_datatype_f64c_k = 1 << 20, ///< Complex double precision floating point
197
+ simsimd_datatype_f32c_k = 1 << 21, ///< Complex single precision floating point
198
+ simsimd_datatype_f16c_k = 1 << 22, ///< Complex half precision floating point
199
+ simsimd_datatype_bf16c_k = 1 << 23, ///< Complex brain floating point
120
200
  } simsimd_datatype_t;
121
201
 
122
202
  /**
@@ -137,13 +217,13 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
137
217
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
138
218
  #endif
139
219
 
220
+ #if SIMSIMD_TARGET_X86
221
+
140
222
  /**
141
- * @brief Function to determine the SIMD capabilities of the current machine at @b runtime.
223
+ * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
142
224
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
143
225
  */
144
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
145
-
146
- #if SIMSIMD_TARGET_X86
226
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
147
227
 
148
228
  /// The states of 4 registers populated for a specific "cpuid" assembly call
149
229
  union four_registers_t {
@@ -151,11 +231,12 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
151
231
  struct separate_t {
152
232
  unsigned eax, ebx, ecx, edx;
153
233
  } named;
154
- } info1, info7;
234
+ } info1, info7, info7sub1;
155
235
 
156
- #ifdef _MSC_VER
236
+ #if defined(_MSC_VER)
157
237
  __cpuidex(info1.array, 1, 0);
158
238
  __cpuidex(info7.array, 7, 0);
239
+ __cpuidex(info7sub1.array, 7, 1);
159
240
  #else
160
241
  __asm__ __volatile__("cpuid"
161
242
  : "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
@@ -163,6 +244,10 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
163
244
  __asm__ __volatile__("cpuid"
164
245
  : "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
165
246
  : "a"(7), "c"(0));
247
+ __asm__ __volatile__("cpuid"
248
+ : "=a"(info7sub1.named.eax), "=b"(info7sub1.named.ebx), "=c"(info7sub1.named.ecx),
249
+ "=d"(info7sub1.named.edx)
250
+ : "a"(7), "c"(1));
166
251
  #endif
167
252
 
168
253
  // Check for AVX2 (Function ID 7, EBX register)
@@ -188,45 +273,155 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
188
273
  unsigned supports_avx512vbmi2 = (info7.named.ecx & 0x00000040) != 0;
189
274
  // Check for AVX512VPOPCNTDQ (Function ID 7, ECX register)
190
275
  unsigned supports_avx512vpopcntdq = (info7.named.ecx & 0x00004000) != 0;
276
+ // Check for AVX512BF16 (Function ID 7, Sub-leaf 1, EAX register)
277
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L205
278
+ unsigned supports_avx512bf16 = (info7sub1.named.eax & 0x00000020) != 0;
191
279
 
192
280
  // Convert specific features into CPU generations
193
281
  unsigned supports_haswell = supports_avx2 && supports_f16c && supports_fma;
194
282
  unsigned supports_skylake = supports_avx512f;
195
283
  unsigned supports_ice = supports_avx512vnni && supports_avx512ifma && supports_avx512bitalg &&
196
284
  supports_avx512vbmi2 && supports_avx512vpopcntdq;
285
+ unsigned supports_genoa = supports_avx512bf16;
197
286
  unsigned supports_sapphire = supports_avx512fp16;
198
287
 
199
288
  return (simsimd_capability_t)( //
200
289
  (simsimd_cap_haswell_k * supports_haswell) | //
201
290
  (simsimd_cap_skylake_k * supports_skylake) | //
202
291
  (simsimd_cap_ice_k * supports_ice) | //
292
+ (simsimd_cap_genoa_k * supports_genoa) | //
203
293
  (simsimd_cap_sapphire_k * supports_sapphire) | //
204
294
  (simsimd_cap_serial_k));
295
+ }
205
296
 
206
297
  #endif // SIMSIMD_TARGET_X86
207
298
 
208
299
  #if SIMSIMD_TARGET_ARM
209
300
 
210
- // Every 64-bit Arm CPU supports NEON
211
- unsigned supports_neon = 1;
212
- unsigned supports_sve = 0;
213
- unsigned supports_sve2 = 0;
301
+ /* Compiling the next section one may get: selected processor does not support system register name 'id_aa64zfr0_el1'.
302
+ * Suppressing assembler errors is very complicated, so when dealing with older ARM CPUs it's simpler to compile this
303
+ * function targetting newer ones.
304
+ */
305
+ #pragma GCC push_options
306
+ #pragma GCC target("arch=armv8.5-a+sve")
307
+ #pragma clang attribute push(__attribute__((target("arch=armv8.5-a+sve"))), apply_to = function)
214
308
 
215
- #ifdef __linux__
216
- unsigned long hwcap = getauxval(AT_HWCAP);
217
- unsigned long hwcap2 = getauxval(AT_HWCAP2);
218
- supports_sve = (hwcap & HWCAP_SVE) != 0;
219
- supports_sve2 = (hwcap2 & HWCAP2_SVE2) != 0;
220
- #endif
309
+ /**
310
+ * @brief Function to determine the SIMD capabilities of the current 64-bit Arm machine at @b runtime.
311
+ * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
312
+ */
313
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
314
+ #if defined(SIMSIMD_DEFINED_APPLE)
315
+ // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
316
+ uint32_t supports_neon = 0, supports_fp16 = 0, supports_bf16 = 0, supports_i8mm = 0;
317
+ size_t size = sizeof(supports_neon);
318
+ if (sysctlbyname("hw.optional.neon", &supports_neon, &size, NULL, 0) != 0)
319
+ supports_neon = 0;
320
+ if (sysctlbyname("hw.optional.arm.FEAT_FP16", &supports_fp16, &size, NULL, 0) != 0)
321
+ supports_fp16 = 0;
322
+ if (sysctlbyname("hw.optional.arm.FEAT_BF16", &supports_bf16, &size, NULL, 0) != 0)
323
+ supports_bf16 = 0;
324
+ if (sysctlbyname("hw.optional.arm.FEAT_I8MM", &supports_i8mm, &size, NULL, 0) != 0)
325
+ supports_i8mm = 0;
326
+
327
+ return (simsimd_capability_t)( //
328
+ (simsimd_cap_neon_k * (supports_neon)) | //
329
+ (simsimd_cap_neon_f16_k * (supports_neon && supports_fp16)) | //
330
+ (simsimd_cap_neon_bf16_k * (supports_neon && supports_bf16)) | //
331
+ (simsimd_cap_neon_i8_k * (supports_neon && supports_i8mm)) | //
332
+ (simsimd_cap_serial_k));
221
333
 
222
- return (simsimd_capability_t)( //
223
- (simsimd_cap_neon_k * supports_neon) | //
224
- (simsimd_cap_sve_k * supports_sve) | //
225
- (simsimd_cap_sve2_k * supports_sve2) | //
334
+ #elif defined(SIMSIMD_DEFINED_LINUX)
335
+ // This is how the `arm-cpusysregs` library does it:
336
+ //
337
+ // int ID_AA64ISAR1_EL1_BF16() const { return (int)(_aa64isar1 >> 44) & 0x0F; }
338
+ // int ID_AA64ZFR0_EL1_BF16() const { return (int)(_aa64zfr0 >> 20) & 0x0F; }
339
+ // int ID_AA64PFR0_EL1_FP() const { return (int)(_aa64pfr0 >> 16) & 0x0F; }
340
+ // int ID_AA64ISAR0_EL1_DP() const { return (int)(_aa64isar0 >> 44) & 0x0F; }
341
+ // int ID_AA64PFR0_EL1_SVE() const { return (int)(_aa64pfr0 >> 32) & 0x0F; }
342
+ // int ID_AA64ZFR0_EL1_SVEver() const { return (int)(_aa64zfr0) & 0x0F; }
343
+ // bool FEAT_BF16() const { return ID_AA64ISAR1_EL1_BF16() >= 1 || ID_AA64ZFR0_EL1_BF16() >= 1; }
344
+ // bool FEAT_FP16() const { return ID_AA64PFR0_EL1_FP() >= 1 && ID_AA64PFR0_EL1_FP() < 15; }
345
+ // bool FEAT_DotProd() const { return ID_AA64ISAR0_EL1_DP() >= 1; }
346
+ // bool FEAT_SVE() const { return ID_AA64PFR0_EL1_SVE() >= 1; }
347
+ // bool FEAT_SVE2() const { return ID_AA64ZFR0_EL1_SVEver() >= 1; }
348
+ // bool FEAT_I8MM() const { return ID_AA64ZFR0_EL1_I8MM() >= 1; }
349
+ //
350
+ // https://github.com/lelegard/arm-cpusysregs/tree/4837c62e619a5e5f12bf41b16a1ee1e71d62c76d
351
+
352
+ // Read CPUID registers directly
353
+ unsigned long id_aa64isar0_el1 = 0, id_aa64isar1_el1 = 0, id_aa64pfr0_el1 = 0, id_aa64zfr0_el1 = 0;
354
+
355
+ // Now let's unpack the status flags from ID_AA64ISAR0_EL1
356
+ // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64ISAR0-EL1--AArch64-Instruction-Set-Attribute-Register-0?lang=en
357
+ __asm__ __volatile__("mrs %0, ID_AA64ISAR0_EL1" : "=r"(id_aa64isar0_el1));
358
+ // DP, bits [47:44] of ID_AA64ISAR0_EL1
359
+ unsigned supports_integer_dot_products = ((id_aa64isar0_el1 >> 44) & 0xF) >= 1;
360
+ // Now let's unpack the status flags from ID_AA64ISAR1_EL1
361
+ // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64ISAR1-EL1--AArch64-Instruction-Set-Attribute-Register-1?lang=en
362
+ __asm__ __volatile__("mrs %0, ID_AA64ISAR1_EL1" : "=r"(id_aa64isar1_el1));
363
+ // I8MM, bits [55:52] of ID_AA64ISAR1_EL1
364
+ unsigned supports_i8mm = ((id_aa64isar1_el1 >> 52) & 0xF) >= 1;
365
+ // BF16, bits [47:44] of ID_AA64ISAR1_EL1
366
+ unsigned supports_bf16 = ((id_aa64isar1_el1 >> 44) & 0xF) >= 1;
367
+
368
+ // Now let's unpack the status flags from ID_AA64PFR0_EL1
369
+ // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64PFR0-EL1--AArch64-Processor-Feature-Register-0?lang=en
370
+ __asm__ __volatile__("mrs %0, ID_AA64PFR0_EL1" : "=r"(id_aa64pfr0_el1));
371
+ // SVE, bits [35:32] of ID_AA64PFR0_EL1
372
+ unsigned supports_sve = ((id_aa64pfr0_el1 >> 32) & 0xF) >= 1;
373
+ // AdvSIMD, bits [23:20] of ID_AA64PFR0_EL1 can be used to check for `fp16` support
374
+ // - 0b0000: integers, single, double precision arithmetic
375
+ // - 0b0001: includes support for half-precision floating-point arithmetic
376
+ unsigned supports_fp16 = ((id_aa64pfr0_el1 >> 20) & 0xF) == 1;
377
+
378
+ // Now let's unpack the status flags from ID_AA64ZFR0_EL1
379
+ // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64ZFR0-EL1--SVE-Feature-ID-Register-0?lang=en
380
+ if (supports_sve)
381
+ __asm__ __volatile__("mrs %0, ID_AA64ZFR0_EL1" : "=r"(id_aa64zfr0_el1));
382
+ // I8MM, bits [47:44] of ID_AA64ZFR0_EL1
383
+ unsigned supports_sve_i8mm = ((id_aa64zfr0_el1 >> 44) & 0xF) >= 1;
384
+ // BF16, bits [23:20] of ID_AA64ZFR0_EL1
385
+ unsigned supports_sve_bf16 = ((id_aa64zfr0_el1 >> 20) & 0xF) >= 1;
386
+ // SVEver, bits [3:0] can be used to check for capability levels:
387
+ // - 0b0000: SVE is implemented
388
+ // - 0b0001: SVE2 is implemented
389
+ // - 0b0010: SVE2.1 is implemented
390
+ // This value must match the existing indicator obtained from ID_AA64PFR0_EL1:
391
+ // unsigned supports_sve = ((id_aa64zfr0_el1) & 0xF) >= 1;
392
+ // unsigned supports_sve2 = ((id_aa64zfr0_el1) & 0xF) >= 2;
393
+ unsigned supports_neon = 1; // NEON is always supported
394
+
395
+ return (simsimd_capability_t)( //
396
+ (simsimd_cap_neon_k * (supports_neon)) | //
397
+ (simsimd_cap_neon_f16_k * (supports_neon && supports_fp16)) | //
398
+ (simsimd_cap_neon_bf16_k * (supports_neon && supports_bf16)) | //
399
+ (simsimd_cap_neon_i8_k * (supports_neon && supports_i8mm && supports_integer_dot_products)) | //
400
+ (simsimd_cap_sve_k * (supports_sve)) | //
401
+ (simsimd_cap_sve_f16_k * (supports_sve && supports_fp16)) | //
402
+ (simsimd_cap_sve_bf16_k * (supports_sve && supports_sve_bf16)) | //
403
+ (simsimd_cap_sve_i8_k * (supports_sve && supports_sve_i8mm)) | //
226
404
  (simsimd_cap_serial_k));
405
+ #else // SIMSIMD_DEFINED_LINUX
406
+ return simsimd_cap_serial_k;
407
+ #endif
408
+ }
227
409
 
228
- #endif // SIMSIMD_TARGET_ARM
410
+ #pragma clang attribute pop
411
+ #pragma GCC pop_options
412
+ #endif
229
413
 
414
+ /**
415
+ * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
416
+ * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
417
+ */
418
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
419
+ #if SIMSIMD_TARGET_X86
420
+ return simsimd_capabilities_x86();
421
+ #endif // SIMSIMD_TARGET_X86
422
+ #if SIMSIMD_TARGET_ARM
423
+ return simsimd_capabilities_arm();
424
+ #endif // SIMSIMD_TARGET_ARM
230
425
  return simsimd_cap_serial_k;
231
426
  }
232
427
 
@@ -357,13 +552,13 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
357
552
  }
358
553
  #endif
359
554
  #if SIMSIMD_TARGET_NEON
360
- if (viable & simsimd_cap_neon_k)
555
+ if (viable & simsimd_cap_neon_f16_k)
361
556
  switch (kind) {
362
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_k; return;
363
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_k; return;
364
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_k; return;
365
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_k; return;
366
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_k; return;
557
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_f16_k; return;
558
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_f16_k; return;
559
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_f16_k; return;
560
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_f16_k; return;
561
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_f16_k; return;
367
562
  default: break;
368
563
  }
369
564
  #endif
@@ -402,21 +597,62 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
402
597
 
403
598
  break;
404
599
 
600
+ // Brain floating-point vectors
601
+ case simsimd_datatype_bf16_k:
602
+ #if SIMSIMD_TARGET_NEON_BF16
603
+ if (viable & simsimd_cap_neon_bf16_k)
604
+ switch (kind) {
605
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
606
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
607
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
608
+ default: break;
609
+ }
610
+ #endif
611
+ #if SIMSIMD_TARGET_HASWELL
612
+ if (viable & simsimd_cap_haswell_k)
613
+ switch (kind) {
614
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_haswell, *c = simsimd_cap_haswell_k; return;
615
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_haswell, *c = simsimd_cap_haswell_k; return;
616
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_haswell, *c = simsimd_cap_haswell_k; return;
617
+ default: break;
618
+ }
619
+ #endif
620
+ #if SIMSIMD_TARGET_GENOA
621
+ if (viable & simsimd_cap_genoa_k)
622
+ switch (kind) {
623
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_genoa, *c = simsimd_cap_genoa_k; return;
624
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_genoa, *c = simsimd_cap_genoa_k; return;
625
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_genoa, *c = simsimd_cap_genoa_k; return;
626
+ default: break;
627
+ }
628
+ #endif
629
+ if (viable & simsimd_cap_serial_k)
630
+ switch (kind) {
631
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_serial, *c = simsimd_cap_serial_k; return;
632
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_serial, *c = simsimd_cap_serial_k; return;
633
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_serial, *c = simsimd_cap_serial_k; return;
634
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_bf16_serial, *c = simsimd_cap_serial_k; return;
635
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_bf16_serial, *c = simsimd_cap_serial_k; return;
636
+ default: break;
637
+ }
638
+
639
+ break;
640
+
405
641
  // Single-byte integer vectors
406
642
  case simsimd_datatype_i8_k:
407
643
  #if SIMSIMD_TARGET_NEON
408
- if (viable & simsimd_cap_neon_k)
644
+ if (viable & simsimd_cap_neon_i8_k)
409
645
  switch (kind) {
410
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_k; return;
411
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_k; return;
412
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_k; return;
646
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_neon, *c = simsimd_cap_neon_i8_k; return;
647
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_i8_k; return;
648
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_i8_k; return;
413
649
  default: break;
414
650
  }
415
651
  #endif
416
652
  #if SIMSIMD_TARGET_ICE
417
653
  if (viable & simsimd_cap_ice_k)
418
654
  switch (kind) {
419
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
655
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_ice, *c = simsimd_cap_ice_k; return;
420
656
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
421
657
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_ice, *c = simsimd_cap_ice_k; return;
422
658
  default: break;
@@ -425,7 +661,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
425
661
  #if SIMSIMD_TARGET_HASWELL
426
662
  if (viable & simsimd_cap_haswell_k)
427
663
  switch (kind) {
428
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
664
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_haswell, *c = simsimd_cap_haswell_k; return;
429
665
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
430
666
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_haswell, *c = simsimd_cap_haswell_k; return;
431
667
  default: break;
@@ -434,7 +670,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
434
670
 
435
671
  if (viable & simsimd_cap_serial_k)
436
672
  switch (kind) {
437
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
673
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_serial, *c = simsimd_cap_serial_k; return;
438
674
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
439
675
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_serial, *c = simsimd_cap_serial_k; return;
440
676
  default: break;
@@ -505,14 +741,6 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
505
741
  default: break;
506
742
  }
507
743
  #endif
508
- #if SIMSIMD_TARGET_HASWELL
509
- if (viable & simsimd_cap_haswell_k)
510
- switch (kind) {
511
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
512
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
513
- default: break;
514
- }
515
- #endif
516
744
  #if SIMSIMD_TARGET_SKYLAKE
517
745
  if (viable & simsimd_cap_skylake_k)
518
746
  switch (kind) {
@@ -521,6 +749,14 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
521
749
  default: break;
522
750
  }
523
751
  #endif
752
+ #if SIMSIMD_TARGET_HASWELL
753
+ if (viable & simsimd_cap_haswell_k)
754
+ switch (kind) {
755
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
756
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
757
+ default: break;
758
+ }
759
+ #endif
524
760
 
525
761
  if (viable & simsimd_cap_serial_k)
526
762
  switch (kind) {
@@ -570,10 +806,18 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
570
806
  }
571
807
  #endif
572
808
  #if SIMSIMD_TARGET_NEON
573
- if (viable & simsimd_cap_neon_k)
809
+ if (viable & simsimd_cap_neon_f16_k)
810
+ switch (kind) {
811
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
812
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
813
+ default: break;
814
+ }
815
+ #endif
816
+ #if SIMSIMD_TARGET_SAPPHIRE
817
+ if (viable & simsimd_cap_sapphire_k)
574
818
  switch (kind) {
575
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_k; return;
576
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_k; return;
819
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
820
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
577
821
  default: break;
578
822
  }
579
823
  #endif
@@ -585,19 +829,38 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
585
829
  default: break;
586
830
  }
587
831
  #endif
588
- #if SIMSIMD_TARGET_SAPPHIRE
589
- if (viable & simsimd_cap_sapphire_k)
832
+
833
+ if (viable & simsimd_cap_serial_k)
590
834
  switch (kind) {
591
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
592
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
835
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
836
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
837
+ default: break;
838
+ }
839
+
840
+ break;
841
+ case simsimd_datatype_bf16c_k:
842
+
843
+ #if SIMSIMD_TARGET_NEON_BF16
844
+ if (viable & simsimd_cap_neon_bf16_k)
845
+ switch (kind) {
846
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
847
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
848
+ default: break;
849
+ }
850
+ #endif
851
+ #if SIMSIMD_TARGET_GENOA
852
+ if (viable & simsimd_cap_genoa_k)
853
+ switch (kind) {
854
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
855
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
593
856
  default: break;
594
857
  }
595
858
  #endif
596
859
 
597
860
  if (viable & simsimd_cap_serial_k)
598
861
  switch (kind) {
599
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
600
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
862
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_serial, *c = simsimd_cap_serial_k; return;
863
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_serial, *c = simsimd_cap_serial_k; return;
601
864
  default: break;
602
865
  }
603
866
 
@@ -605,8 +868,8 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
605
868
  }
606
869
  }
607
870
 
608
- #pragma clang diagnostic pop
609
871
  #pragma GCC diagnostic pop
872
+ #pragma clang diagnostic pop
610
873
 
611
874
  /**
612
875
  * @brief Selects the most suitable metric implementation based on the given metric kind, datatype,
@@ -642,11 +905,18 @@ SIMSIMD_PUBLIC simsimd_metric_punned_t simsimd_metric_punned( //
642
905
  * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
643
906
  */
644
907
  SIMSIMD_DYNAMIC int simsimd_uses_neon(void);
908
+ SIMSIMD_DYNAMIC int simsimd_uses_neon_f16(void);
909
+ SIMSIMD_DYNAMIC int simsimd_uses_neon_bf16(void);
910
+ SIMSIMD_DYNAMIC int simsimd_uses_neon_i8(void);
645
911
  SIMSIMD_DYNAMIC int simsimd_uses_sve(void);
912
+ SIMSIMD_DYNAMIC int simsimd_uses_sve_f16(void);
913
+ SIMSIMD_DYNAMIC int simsimd_uses_sve_bf16(void);
914
+ SIMSIMD_DYNAMIC int simsimd_uses_sve_i8(void);
646
915
  SIMSIMD_DYNAMIC int simsimd_uses_haswell(void);
647
916
  SIMSIMD_DYNAMIC int simsimd_uses_skylake(void);
648
917
  SIMSIMD_DYNAMIC int simsimd_uses_ice(void);
649
918
  SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void);
919
+ SIMSIMD_DYNAMIC int simsimd_uses_genoa(void);
650
920
  SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
651
921
 
652
922
  /* Inner products
@@ -665,18 +935,24 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
665
935
  */
666
936
  SIMSIMD_DYNAMIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
667
937
  simsimd_distance_t* d);
938
+ SIMSIMD_DYNAMIC void simsimd_dot_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
939
+ simsimd_distance_t* d);
668
940
  SIMSIMD_DYNAMIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
669
941
  simsimd_distance_t* d);
670
942
  SIMSIMD_DYNAMIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
671
943
  simsimd_distance_t* d);
672
944
  SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
673
945
  simsimd_distance_t* d);
946
+ SIMSIMD_DYNAMIC void simsimd_dot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
947
+ simsimd_distance_t* d);
674
948
  SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
675
949
  simsimd_distance_t* d);
676
950
  SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
677
951
  simsimd_distance_t* d);
678
952
  SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
679
953
  simsimd_distance_t* d);
954
+ SIMSIMD_DYNAMIC void simsimd_vdot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
955
+ simsimd_distance_t* d);
680
956
  SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
681
957
  simsimd_distance_t* d);
682
958
  SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
@@ -699,6 +975,8 @@ SIMSIMD_DYNAMIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b
699
975
  simsimd_distance_t* d);
700
976
  SIMSIMD_DYNAMIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
701
977
  simsimd_distance_t* d);
978
+ SIMSIMD_DYNAMIC void simsimd_cos_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
979
+ simsimd_distance_t* d);
702
980
  SIMSIMD_DYNAMIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
703
981
  simsimd_distance_t* d);
704
982
  SIMSIMD_DYNAMIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
@@ -707,6 +985,8 @@ SIMSIMD_DYNAMIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const*
707
985
  simsimd_distance_t* d);
708
986
  SIMSIMD_DYNAMIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
709
987
  simsimd_distance_t* d);
988
+ SIMSIMD_DYNAMIC void simsimd_l2sq_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
989
+ simsimd_distance_t* d);
710
990
  SIMSIMD_DYNAMIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
711
991
  simsimd_distance_t* d);
712
992
  SIMSIMD_DYNAMIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
@@ -746,12 +1026,16 @@ SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t cons
746
1026
  */
747
1027
  SIMSIMD_DYNAMIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
748
1028
  simsimd_distance_t* d);
1029
+ SIMSIMD_DYNAMIC void simsimd_kl_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1030
+ simsimd_distance_t* d);
749
1031
  SIMSIMD_DYNAMIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
750
1032
  simsimd_distance_t* d);
751
1033
  SIMSIMD_DYNAMIC void simsimd_kl_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
752
1034
  simsimd_distance_t* d);
753
1035
  SIMSIMD_DYNAMIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
754
1036
  simsimd_distance_t* d);
1037
+ SIMSIMD_DYNAMIC void simsimd_js_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1038
+ simsimd_distance_t* d);
755
1039
  SIMSIMD_DYNAMIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
756
1040
  simsimd_distance_t* d);
757
1041
  SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
@@ -765,17 +1049,28 @@ SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const*
765
1049
  * - Check if the CPU supports AVX512F and AVX512BW extensions on Skylake x86 CPUs and newer
766
1050
  * - Check if the CPU supports AVX512VNNI, AVX512IFMA, AVX512BITALG, AVX512VBMI2, and AVX512VPOPCNTDQ
767
1051
  * extensions on Ice Lake x86 CPUs and newer
1052
+ * - Check if the CPU supports AVX512BF16 extensions on Genoa x86 CPUs and newer
768
1053
  * - Check if the CPU supports AVX512FP16 extensions on Sapphire Rapids x86 CPUs and newer
769
1054
  *
770
1055
  * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
771
1056
  */
1057
+
1058
+ // clang-format off
772
1059
  SIMSIMD_PUBLIC int simsimd_uses_neon(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
1060
+ SIMSIMD_PUBLIC int simsimd_uses_neon_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON && SIMSIMD_NATIVE_F16; }
1061
+ SIMSIMD_PUBLIC int simsimd_uses_neon_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON && SIMSIMD_NATIVE_BF16; }
1062
+ SIMSIMD_PUBLIC int simsimd_uses_neon_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
773
1063
  SIMSIMD_PUBLIC int simsimd_uses_sve(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
1064
+ SIMSIMD_PUBLIC int simsimd_uses_sve_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE && SIMSIMD_NATIVE_F16; }
1065
+ SIMSIMD_PUBLIC int simsimd_uses_sve_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE && SIMSIMD_NATIVE_BF16; }
1066
+ SIMSIMD_PUBLIC int simsimd_uses_sve_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
774
1067
  SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; }
775
1068
  SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; }
776
1069
  SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; }
777
1070
  SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SAPPHIRE; }
1071
+ SIMSIMD_PUBLIC int simsimd_uses_genoa(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_GENOA; }
778
1072
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return simsimd_capabilities_implementation(); }
1073
+ // clang-format on
779
1074
 
780
1075
  /* Inner products
781
1076
  * - Dot product: the sum of the products of the corresponding elements of two vectors.
@@ -797,15 +1092,26 @@ SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const*
797
1092
  simsimd_dot_f16_sve(a, b, n, d);
798
1093
  #elif SIMSIMD_TARGET_NEON
799
1094
  simsimd_dot_f16_neon(a, b, n, d);
800
- #elif SIMSIMD_TARGET_HASWELL
801
- simsimd_dot_f16_haswell(a, b, n, d);
802
1095
  #elif SIMSIMD_TARGET_SAPPHIRE
803
1096
  simsimd_dot_f16_sapphire(a, b, n, d);
1097
+ #elif SIMSIMD_TARGET_HASWELL
1098
+ simsimd_dot_f16_haswell(a, b, n, d);
804
1099
  #else
805
1100
  simsimd_dot_f16_serial(a, b, n, d);
806
1101
  #endif
807
1102
  }
808
1103
 
1104
+ SIMSIMD_PUBLIC void simsimd_dot_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1105
+ simsimd_distance_t* d) {
1106
+ #if SIMSIMD_TARGET_GENOA
1107
+ simsimd_dot_bf16_genoa(a, b, n, d);
1108
+ #elif SIMSIMD_TARGET_HASWELL
1109
+ simsimd_dot_bf16_haswell(a, b, n, d);
1110
+ #else
1111
+ simsimd_dot_bf16_serial(a, b, n, d);
1112
+ #endif
1113
+ }
1114
+
809
1115
  SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
810
1116
  simsimd_distance_t* d) {
811
1117
  #if SIMSIMD_TARGET_SVE
@@ -834,24 +1140,32 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const
834
1140
  simsimd_dot_f16c_sve(a, b, n, d);
835
1141
  #elif SIMSIMD_TARGET_NEON
836
1142
  simsimd_dot_f16c_neon(a, b, n, d);
1143
+ #elif SIMSIMD_TARGET_SAPPHIRE
1144
+ simsimd_dot_f16c_sapphire(a, b, n, d);
837
1145
  #elif SIMSIMD_TARGET_HASWELL
838
1146
  simsimd_dot_f16c_haswell(a, b, n, d);
839
- #elif SIMSIMD_TARGET_sapphire
840
- simsimd_dot_f16c_sapphire(a, b, n, d);
841
1147
  #else
842
1148
  simsimd_dot_f16c_serial(a, b, n, d);
843
1149
  #endif
844
1150
  }
1151
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1152
+ simsimd_distance_t* d) {
1153
+ #if SIMSIMD_TARGET_GENOA
1154
+ simsimd_dot_bf16c_genoa(a, b, n, d);
1155
+ #else
1156
+ simsimd_dot_bf16c_serial(a, b, n, d);
1157
+ #endif
1158
+ }
845
1159
  SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
846
1160
  simsimd_distance_t* d) {
847
1161
  #if SIMSIMD_TARGET_SVE
848
1162
  simsimd_dot_f32c_sve(a, b, n, d);
849
1163
  #elif SIMSIMD_TARGET_NEON
850
1164
  simsimd_dot_f32c_neon(a, b, n, d);
851
- #elif SIMSIMD_TARGET_HASWELL
852
- simsimd_dot_f32c_haswell(a, b, n, d);
853
1165
  #elif SIMSIMD_TARGET_SKYLAKE
854
1166
  simsimd_dot_f32c_skylake(a, b, n, d);
1167
+ #elif SIMSIMD_TARGET_HASWELL
1168
+ simsimd_dot_f32c_haswell(a, b, n, d);
855
1169
  #else
856
1170
  simsimd_dot_f32c_serial(a, b, n, d);
857
1171
  #endif
@@ -869,39 +1183,43 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const
869
1183
  SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
870
1184
  simsimd_distance_t* d) {
871
1185
  #if SIMSIMD_TARGET_SVE
872
- simsimd_dot_f16c_sve(a, b, n, d);
1186
+ simsimd_vdot_f16c_sve(a, b, n, d);
873
1187
  #elif SIMSIMD_TARGET_NEON
874
1188
  simsimd_dot_f16c_neon(a, b, n, d);
1189
+ #elif SIMSIMD_TARGET_SAPPHIRE
1190
+ simsimd_dot_f16c_sapphire(a, b, n, d);
875
1191
  #elif SIMSIMD_TARGET_HASWELL
876
1192
  simsimd_dot_f16c_haswell(a, b, n, d);
877
- #elif SIMSIMD_TARGET_sapphire
878
- simsimd_dot_f16c_sapphire(a, b, n, d);
879
1193
  #else
880
- simsimd_dot_f16c_serial(a, b, n, d);
1194
+ simsimd_vdot_f16c_serial(a, b, n, d);
881
1195
  #endif
882
1196
  }
1197
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1198
+ simsimd_distance_t* d) {
1199
+ simsimd_vdot_bf16c_serial(a, b, n, d);
1200
+ }
883
1201
  SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
884
1202
  simsimd_distance_t* d) {
885
1203
  #if SIMSIMD_TARGET_SVE
886
- simsimd_dot_f32c_sve(a, b, n, d);
1204
+ simsimd_vdot_f32c_sve(a, b, n, d);
887
1205
  #elif SIMSIMD_TARGET_NEON
888
1206
  simsimd_dot_f32c_neon(a, b, n, d);
889
- #elif SIMSIMD_TARGET_HASWELL
890
- simsimd_dot_f32c_haswell(a, b, n, d);
891
1207
  #elif SIMSIMD_TARGET_SKYLAKE
892
1208
  simsimd_dot_f32c_skylake(a, b, n, d);
1209
+ #elif SIMSIMD_TARGET_HASWELL
1210
+ simsimd_dot_f32c_haswell(a, b, n, d);
893
1211
  #else
894
- simsimd_dot_f32c_serial(a, b, n, d);
1212
+ simsimd_vdot_f32c_serial(a, b, n, d);
895
1213
  #endif
896
1214
  }
897
1215
  SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
898
1216
  simsimd_distance_t* d) {
899
1217
  #if SIMSIMD_TARGET_SVE
900
- simsimd_dot_f64c_sve(a, b, n, d);
1218
+ simsimd_vdot_f64c_sve(a, b, n, d);
901
1219
  #elif SIMSIMD_TARGET_SKYLAKE
902
- simsimd_dot_f64c_skylake(a, b, n, d);
1220
+ simsimd_vdot_f64c_skylake(a, b, n, d);
903
1221
  #else
904
- simsimd_dot_f64c_serial(a, b, n, d);
1222
+ simsimd_vdot_f64c_serial(a, b, n, d);
905
1223
  #endif
906
1224
  }
907
1225
 
@@ -956,6 +1274,16 @@ SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const*
956
1274
  simsimd_cos_f16_serial(a, b, n, d);
957
1275
  #endif
958
1276
  }
1277
+ SIMSIMD_PUBLIC void simsimd_cos_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1278
+ simsimd_distance_t* d) {
1279
+ #if SIMSIMD_TARGET_GENOA
1280
+ simsimd_cos_bf16_genoa(a, b, n, d);
1281
+ #elif SIMSIMD_TARGET_HASWELL
1282
+ simsimd_cos_bf16_haswell(a, b, n, d);
1283
+ #else
1284
+ simsimd_cos_bf16_serial(a, b, n, d);
1285
+ #endif
1286
+ }
959
1287
  SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
960
1288
  simsimd_distance_t* d) {
961
1289
  #if SIMSIMD_TARGET_SVE
@@ -984,7 +1312,7 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const
984
1312
  simsimd_l2sq_f16_sve(a, b, n, d);
985
1313
  #elif SIMSIMD_TARGET_NEON
986
1314
  simsimd_l2sq_f16_neon(a, b, n, d);
987
- #elif SIMSIMD_TARGET_sapphire
1315
+ #elif SIMSIMD_TARGET_SAPPHIRE
988
1316
  simsimd_l2sq_f16_sapphire(a, b, n, d);
989
1317
  #elif SIMSIMD_TARGET_HASWELL
990
1318
  simsimd_l2sq_f16_haswell(a, b, n, d);
@@ -992,6 +1320,16 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const
992
1320
  simsimd_l2sq_f16_serial(a, b, n, d);
993
1321
  #endif
994
1322
  }
1323
+ SIMSIMD_PUBLIC void simsimd_l2sq_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1324
+ simsimd_distance_t* d) {
1325
+ #if SIMSIMD_TARGET_GENOA
1326
+ simsimd_l2sq_bf16_genoa(a, b, n, d);
1327
+ #elif SIMSIMD_TARGET_HASWELL
1328
+ simsimd_l2sq_bf16_haswell(a, b, n, d);
1329
+ #else
1330
+ simsimd_l2sq_bf16_serial(a, b, n, d);
1331
+ #endif
1332
+ }
995
1333
  SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
996
1334
  simsimd_distance_t* d) {
997
1335
  #if SIMSIMD_TARGET_SVE
@@ -1081,6 +1419,10 @@ SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1081
1419
  simsimd_kl_f16_serial(a, b, n, d);
1082
1420
  #endif
1083
1421
  }
1422
+ SIMSIMD_PUBLIC void simsimd_kl_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1423
+ simsimd_distance_t* d) {
1424
+ simsimd_kl_bf16_serial(a, b, n, d);
1425
+ }
1084
1426
  SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1085
1427
  simsimd_distance_t* d) {
1086
1428
  #if SIMSIMD_TARGET_NEON
@@ -1105,6 +1447,10 @@ SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1105
1447
  simsimd_js_f16_serial(a, b, n, d);
1106
1448
  #endif
1107
1449
  }
1450
+ SIMSIMD_PUBLIC void simsimd_js_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1451
+ simsimd_distance_t* d) {
1452
+ simsimd_js_bf16_serial(a, b, n, d);
1453
+ }
1108
1454
  SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1109
1455
  simsimd_distance_t* d) {
1110
1456
  #if SIMSIMD_TARGET_NEON