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
@@ -12,6 +12,8 @@
12
12
  * - 64-bit IEEE floating point numbers
13
13
  * - 32-bit IEEE floating point numbers
14
14
  * - 16-bit IEEE floating point numbers
15
+ * - 16-bit brain floating point numbers
16
+ * - 8-bit signed integers
15
17
  *
16
18
  * For hardware architectures:
17
19
  * - Arm (NEON, SVE?)
@@ -38,45 +40,63 @@ extern "C" {
38
40
  SIMSIMD_PUBLIC void simsimd_dot_f64_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
39
41
  SIMSIMD_PUBLIC void simsimd_dot_f64c_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
40
42
  SIMSIMD_PUBLIC void simsimd_vdot_f64c_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
43
+
41
44
  SIMSIMD_PUBLIC void simsimd_dot_f32_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
42
45
  SIMSIMD_PUBLIC void simsimd_dot_f32c_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
43
46
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
47
+
44
48
  SIMSIMD_PUBLIC void simsimd_dot_f16_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
45
49
  SIMSIMD_PUBLIC void simsimd_dot_f16c_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
46
50
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
47
51
 
52
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
53
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
54
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
55
+
56
+ SIMSIMD_PUBLIC void simsimd_dot_i8_serial(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
57
+
48
58
  /* Double-precision serial backends for all numeric types.
49
59
  * For single-precision computation check out the "*_serial" counterparts of those "*_accurate" functions.
50
60
  */
51
61
  SIMSIMD_PUBLIC void simsimd_dot_f32_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
52
62
  SIMSIMD_PUBLIC void simsimd_dot_f32c_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
53
63
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
64
+
54
65
  SIMSIMD_PUBLIC void simsimd_dot_f16_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
55
66
  SIMSIMD_PUBLIC void simsimd_dot_f16c_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
56
67
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
57
68
 
69
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
70
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
71
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
72
+
58
73
  /* SIMD-powered backends for Arm NEON, mostly using 32-bit arithmetic over 128-bit words.
59
74
  * By far the most portable backend, covering most Arm v8 devices, over a billion phones, and almost all
60
75
  * server CPUs produced before 2023.
61
76
  */
62
77
  SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
63
- SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
64
78
  SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
65
- SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
66
79
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
80
+
81
+ SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
82
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
67
83
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
68
84
 
85
+ SIMSIMD_PUBLIC void simsimd_dot_i8_serial(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
86
+
69
87
  /* SIMD-powered backends for Arm SVE, mostly using 32-bit arithmetic over variable-length platform-defined word sizes.
70
88
  * Designed for Arm Graviton 3, Microsoft Cobalt, as well as Nvidia Grace and newer Ampere Altra CPUs.
71
89
  */
72
- SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
73
90
  SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
74
- SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
75
- SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
76
91
  SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
77
- SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
78
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
79
92
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
93
+
94
+ SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
95
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
96
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
97
+
98
+ SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
99
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
80
100
  SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
81
101
 
82
102
  /* SIMD-powered backends for AVX2 CPUs of Haswell generation and newer, using 32-bit arithmetic over 256-bit words.
@@ -85,24 +105,38 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t
85
105
  * On other hand, there is no need to implement AVX2 versions of `f32` and `f64` functions, as those are
86
106
  * properly vectorized by recent compilers.
87
107
  */
88
- SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
108
+ SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
89
109
  SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
90
110
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
111
+
112
+ SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
91
113
  SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
92
114
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
93
115
 
116
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
117
+
118
+ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
119
+
94
120
  /* SIMD-powered backends for various generations of AVX512 CPUs.
95
121
  * Skylake is handy, as it supports masked loads and other operations, avoiding the need for the tail loop.
96
122
  * Ice Lake added VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, and other extensions for integral operations.
123
+ * Genoa added only BF16.
97
124
  * Sapphire Rapids added tiled matrix operations, but we are most interested in the new mixed-precision FMA instructions.
98
125
  */
99
- SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
100
- SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
101
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
102
126
  SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
103
127
  SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
104
128
  SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
105
129
 
130
+ SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
131
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
132
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
133
+
134
+ SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t*);
135
+
136
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
137
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
138
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
139
+
106
140
  SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
107
141
  SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
108
142
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
@@ -167,6 +201,12 @@ SIMSIMD_MAKE_DOT(serial, f16, f32, SIMSIMD_UNCOMPRESS_F16) // simsimd_d
167
201
  SIMSIMD_MAKE_COMPLEX_DOT(serial, f16, f32, SIMSIMD_UNCOMPRESS_F16) // simsimd_dot_f16c_serial
168
202
  SIMSIMD_MAKE_COMPLEX_VDOT(serial, f16, f32, SIMSIMD_UNCOMPRESS_F16) // simsimd_vdot_f16c_serial
169
203
 
204
+ SIMSIMD_MAKE_DOT(serial, bf16, f32, SIMSIMD_UNCOMPRESS_BF16) // simsimd_dot_bf16_serial
205
+ SIMSIMD_MAKE_COMPLEX_DOT(serial, bf16, f32, SIMSIMD_UNCOMPRESS_BF16) // simsimd_dot_bf16c_serial
206
+ SIMSIMD_MAKE_COMPLEX_VDOT(serial, bf16, f32, SIMSIMD_UNCOMPRESS_BF16) // simsimd_vdot_bf16c_serial
207
+
208
+ SIMSIMD_MAKE_DOT(serial, i8, i64, SIMSIMD_IDENTIFY) // simsimd_dot_i8_serial
209
+
170
210
  SIMSIMD_MAKE_DOT(accurate, f32, f64, SIMSIMD_IDENTIFY) // simsimd_dot_f32_accurate
171
211
  SIMSIMD_MAKE_COMPLEX_DOT(accurate, f32, f64, SIMSIMD_IDENTIFY) // simsimd_dot_f32c_accurate
172
212
  SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f32, f64, SIMSIMD_IDENTIFY) // simsimd_vdot_f32c_accurate
@@ -175,6 +215,10 @@ SIMSIMD_MAKE_DOT(accurate, f16, f64, SIMSIMD_UNCOMPRESS_F16) // simsimd
175
215
  SIMSIMD_MAKE_COMPLEX_DOT(accurate, f16, f64, SIMSIMD_UNCOMPRESS_F16) // simsimd_dot_f16c_accurate
176
216
  SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f16, f64, SIMSIMD_UNCOMPRESS_F16) // simsimd_vdot_f16c_accurate
177
217
 
218
+ SIMSIMD_MAKE_DOT(accurate, bf16, f64, SIMSIMD_UNCOMPRESS_BF16) // simsimd_dot_bf16_accurate
219
+ SIMSIMD_MAKE_COMPLEX_DOT(accurate, bf16, f64, SIMSIMD_UNCOMPRESS_BF16) // simsimd_dot_bf16c_accurate
220
+ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16, f64, SIMSIMD_UNCOMPRESS_BF16) // simsimd_vdot_bf16c_accurate
221
+
178
222
  #if SIMSIMD_TARGET_ARM
179
223
  #if SIMSIMD_TARGET_NEON
180
224
  #pragma GCC push_options
@@ -269,6 +313,43 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const* a, simsimd_f32_t
269
313
  #pragma clang attribute pop
270
314
  #pragma GCC pop_options
271
315
 
316
+ #pragma GCC push_options
317
+ #pragma GCC target("arch=armv8.2-a+dotprod")
318
+ #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+dotprod"))), apply_to = function)
319
+
320
+ SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
321
+ simsimd_distance_t* result) {
322
+
323
+ int32x4_t ab_vec = vdupq_n_s32(0);
324
+ simsimd_size_t i = 0;
325
+
326
+ // If the 128-bit `vdot_s32` intrinsic is unavailable, we can use the 64-bit `vdot_s32`.
327
+ // for (simsimd_size_t i = 0; i != n; i += 8) {
328
+ // int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
329
+ // int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
330
+ // int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
331
+ // ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
332
+ // vmovl_s16(vget_low_s16(ab_part_vec))));
333
+ // }
334
+ for (; i + 16 <= n; i += 16) {
335
+ int8x16_t a_vec = vld1q_s8(a + i);
336
+ int8x16_t b_vec = vld1q_s8(b + i);
337
+ ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
338
+ }
339
+
340
+ // Take care of the tail:
341
+ int32_t ab = vaddvq_s32(ab_vec);
342
+ for (; i < n; ++i) {
343
+ int32_t ai = a[i], bi = b[i];
344
+ ab += ai * bi;
345
+ }
346
+
347
+ *result = ab;
348
+ }
349
+
350
+ #pragma clang attribute pop
351
+ #pragma GCC pop_options
352
+
272
353
  #pragma GCC push_options
273
354
  #pragma GCC target("+simd+fp16")
274
355
  #pragma clang attribute push(__attribute__((target("+simd+fp16"))), apply_to = function)
@@ -387,6 +468,127 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t
387
468
  #pragma GCC pop_options
388
469
  #endif // SIMSIMD_TARGET_NEON
389
470
 
471
+ #if SIMSIMD_TARGET_NEON_BF16
472
+ #pragma GCC push_options
473
+ #pragma GCC target("arch=armv8.6-a+simd+bf16")
474
+ #pragma clang attribute push(__attribute__((target("arch=armv8.6-a+simd+bf16"))), apply_to = function)
475
+
476
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
477
+ simsimd_distance_t* result) {
478
+ float32x4_t ab_high_vec = vdupq_n_f32(0), ab_low_vec = vdupq_n_f32(0);
479
+ simsimd_size_t i = 0;
480
+ for (; i + 8 <= n; i += 8) {
481
+ bfloat16x8_t a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const*)a + i);
482
+ bfloat16x8_t b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const*)b + i);
483
+ ab_high_vec = vbfmlaltq_f32(ab_high_vec, a_vec, b_vec);
484
+ ab_low_vec = vbfmlalbq_f32(ab_low_vec, a_vec, b_vec);
485
+ }
486
+
487
+ // In case the software emulation for `bf16` scalars is enabled, the `simsimd_uncompress_bf16`
488
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
489
+ // loads and stores with vectorized math.
490
+ if (i < n) {
491
+ union {
492
+ bfloat16x8_t bf16_vec;
493
+ simsimd_bf16_t bf16[8];
494
+ } a_padded_tail, b_padded_tail;
495
+ simsimd_size_t j = 0;
496
+ for (; i < n; ++i, ++j)
497
+ a_padded_tail.bf16[j] = a[i], b_padded_tail.bf16[j] = b[i];
498
+ for (; j < 8; ++j)
499
+ a_padded_tail.bf16[j] = 0, b_padded_tail.bf16[j] = 0;
500
+ ab_high_vec = vbfmlaltq_f32(ab_high_vec, a_padded_tail.bf16_vec, b_padded_tail.bf16_vec);
501
+ ab_low_vec = vbfmlalbq_f32(ab_low_vec, a_padded_tail.bf16_vec, b_padded_tail.bf16_vec);
502
+ }
503
+ *result = vaddvq_f32(ab_high_vec) + vaddvq_f32(ab_low_vec);
504
+ }
505
+
506
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, //
507
+ simsimd_distance_t* results) {
508
+
509
+ // A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
510
+ // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
511
+ // at once. That's how the original implementation worked, but compiling it was a nightmare :)
512
+ float32x4_t ab_real_vec = vdupq_n_f32(0);
513
+ float32x4_t ab_imag_vec = vdupq_n_f32(0);
514
+ simsimd_size_t i = 0;
515
+ for (; i + 8 <= n; i += 8) {
516
+ // Unpack the input arrays into real and imaginary parts.
517
+ // MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
518
+ // integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
519
+ int16x4x2_t a_vec = vld2_s16((short*)a + i);
520
+ int16x4x2_t b_vec = vld2_s16((short*)b + i);
521
+ float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
522
+ float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
523
+ float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
524
+ float32x4_t b_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[1]));
525
+
526
+ // Compute the dot product:
527
+ ab_real_vec = vfmaq_f32(ab_real_vec, a_real_vec, b_real_vec);
528
+ ab_real_vec = vfmsq_f32(ab_real_vec, a_imag_vec, b_imag_vec);
529
+ ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
530
+ ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
531
+ }
532
+
533
+ // Reduce horizontal sums:
534
+ simsimd_f32_t ab_real = vaddvq_f32(ab_real_vec);
535
+ simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
536
+
537
+ // Handle the tail:
538
+ for (; i + 2 <= n; i += 2) {
539
+ simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
540
+ ab_real += ar * br - ai * bi;
541
+ ab_imag += ar * bi + ai * br;
542
+ }
543
+ results[0] = ab_real;
544
+ results[1] = ab_imag;
545
+ }
546
+
547
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, //
548
+ simsimd_distance_t* results) {
549
+
550
+ // A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
551
+ // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
552
+ // at once. That's how the original implementation worked, but compiling it was a nightmare :)
553
+ float32x4_t ab_real_vec = vdupq_n_f32(0);
554
+ float32x4_t ab_imag_vec = vdupq_n_f32(0);
555
+ simsimd_size_t i = 0;
556
+ for (; i + 8 <= n; i += 8) {
557
+ // Unpack the input arrays into real and imaginary parts.
558
+ // MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
559
+ // integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
560
+ int16x4x2_t a_vec = vld2_s16((short*)a + i);
561
+ int16x4x2_t b_vec = vld2_s16((short*)b + i);
562
+ float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
563
+ float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
564
+ float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
565
+ float32x4_t b_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[1]));
566
+
567
+ // Compute the dot product:
568
+ ab_real_vec = vfmaq_f32(ab_real_vec, a_real_vec, b_real_vec);
569
+ ab_real_vec = vfmaq_f32(ab_real_vec, a_imag_vec, b_imag_vec);
570
+ ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
571
+ ab_imag_vec = vfmsq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
572
+ }
573
+
574
+ // Reduce horizontal sums:
575
+ simsimd_f32_t ab_real = vaddvq_f32(ab_real_vec);
576
+ simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
577
+
578
+ // Handle the tail:
579
+ for (; i + 2 <= n; i += 2) {
580
+ simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
581
+ ab_real += ar * br + ai * bi;
582
+ ab_imag += ar * bi - ai * br;
583
+ }
584
+ results[0] = ab_real;
585
+ results[1] = ab_imag;
586
+ }
587
+
588
+ #pragma clang attribute pop
589
+ #pragma GCC pop_options
590
+ #endif // SIMSIMD_TARGET_NEON_BF16
591
+
390
592
  #if SIMSIMD_TARGET_SVE
391
593
 
392
594
  #pragma GCC push_options
@@ -396,7 +598,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t
396
598
  SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
397
599
  simsimd_distance_t* result) {
398
600
  simsimd_size_t i = 0;
399
- svfloat32_t ab_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
601
+ svfloat32_t ab_vec = svdup_f32(0.f);
400
602
  do {
401
603
  svbool_t pg_vec = svwhilelt_b32((unsigned int)i, (unsigned int)n);
402
604
  svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
@@ -410,10 +612,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t co
410
612
  SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
411
613
  simsimd_distance_t* results) {
412
614
  simsimd_size_t i = 0;
413
- svfloat32_t ab_real_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
414
- svfloat32_t ab_imag_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
615
+ svfloat32_t ab_real_vec = svdup_f32(0.f);
616
+ svfloat32_t ab_imag_vec = svdup_f32(0.f);
415
617
  do {
416
- svbool_t pg_vec = svwhilelt_b32((unsigned int)i, (unsigned int)n);
618
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)i / 2, (unsigned int)n / 2);
417
619
  svfloat32x2_t a_vec = svld2_f32(pg_vec, a + i);
418
620
  svfloat32x2_t b_vec = svld2_f32(pg_vec, b + i);
419
621
  svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
@@ -433,10 +635,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t c
433
635
  SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
434
636
  simsimd_distance_t* results) {
435
637
  simsimd_size_t i = 0;
436
- svfloat32_t ab_real_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
437
- svfloat32_t ab_imag_vec = svdupq_n_f32(0.f, 0.f, 0.f, 0.f);
638
+ svfloat32_t ab_real_vec = svdup_f32(0.f);
639
+ svfloat32_t ab_imag_vec = svdup_f32(0.f);
438
640
  do {
439
- svbool_t pg_vec = svwhilelt_b32((unsigned int)i, (unsigned int)n);
641
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)i / 2, (unsigned int)n / 2);
440
642
  svfloat32x2_t a_vec = svld2_f32(pg_vec, a + i);
441
643
  svfloat32x2_t b_vec = svld2_f32(pg_vec, b + i);
442
644
  svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
@@ -456,7 +658,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t
456
658
  SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
457
659
  simsimd_distance_t* result) {
458
660
  simsimd_size_t i = 0;
459
- svfloat64_t ab_vec = svdupq_n_f64(0.0, 0.0);
661
+ svfloat64_t ab_vec = svdup_f64(0.);
460
662
  do {
461
663
  svbool_t pg_vec = svwhilelt_b64((unsigned int)i, (unsigned int)n);
462
664
  svfloat64_t a_vec = svld1_f64(pg_vec, a + i);
@@ -470,10 +672,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t co
470
672
  SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
471
673
  simsimd_distance_t* results) {
472
674
  simsimd_size_t i = 0;
473
- svfloat64_t ab_real_vec = svdupq_n_f64(0., 0.);
474
- svfloat64_t ab_imag_vec = svdupq_n_f64(0., 0.);
675
+ svfloat64_t ab_real_vec = svdup_f64(0.);
676
+ svfloat64_t ab_imag_vec = svdup_f64(0.);
475
677
  do {
476
- svbool_t pg_vec = svwhilelt_b64((unsigned int)i, (unsigned int)n);
678
+ svbool_t pg_vec = svwhilelt_b64((unsigned int)i / 2, (unsigned int)n / 2);
477
679
  svfloat64x2_t a_vec = svld2_f64(pg_vec, a + i);
478
680
  svfloat64x2_t b_vec = svld2_f64(pg_vec, b + i);
479
681
  svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
@@ -493,10 +695,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t c
493
695
  SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
494
696
  simsimd_distance_t* results) {
495
697
  simsimd_size_t i = 0;
496
- svfloat64_t ab_real_vec = svdupq_n_f64(0., 0.);
497
- svfloat64_t ab_imag_vec = svdupq_n_f64(0., 0.);
698
+ svfloat64_t ab_real_vec = svdup_f64(0.);
699
+ svfloat64_t ab_imag_vec = svdup_f64(0.);
498
700
  do {
499
- svbool_t pg_vec = svwhilelt_b64((unsigned int)i, (unsigned int)n);
701
+ svbool_t pg_vec = svwhilelt_b64((unsigned int)i / 2, (unsigned int)n / 2);
500
702
  svfloat64x2_t a_vec = svld2_f64(pg_vec, a + i);
501
703
  svfloat64x2_t b_vec = svld2_f64(pg_vec, b + i);
502
704
  svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
@@ -523,7 +725,7 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t
523
725
  SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16_t const* b_enum, simsimd_size_t n,
524
726
  simsimd_distance_t* result) {
525
727
  simsimd_size_t i = 0;
526
- svfloat16_t ab_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
728
+ svfloat16_t ab_vec = svdup_f16(0);
527
729
  simsimd_f16_for_arm_simd_t const* a = (simsimd_f16_for_arm_simd_t const*)(a_enum);
528
730
  simsimd_f16_for_arm_simd_t const* b = (simsimd_f16_for_arm_simd_t const*)(b_enum);
529
731
  do {
@@ -540,10 +742,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a_enum, simsimd_f16
540
742
  SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
541
743
  simsimd_distance_t* results) {
542
744
  simsimd_size_t i = 0;
543
- svfloat16_t ab_real_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
544
- svfloat16_t ab_imag_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
745
+ svfloat16_t ab_real_vec = svdup_f16(0);
746
+ svfloat16_t ab_imag_vec = svdup_f16(0);
545
747
  do {
546
- svbool_t pg_vec = svwhilelt_b16((unsigned int)i, (unsigned int)n);
748
+ svbool_t pg_vec = svwhilelt_b16((unsigned int)i / 2, (unsigned int)n / 2);
547
749
  svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const*)a + i);
548
750
  svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const*)b + i);
549
751
  svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
@@ -563,10 +765,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t c
563
765
  SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
564
766
  simsimd_distance_t* results) {
565
767
  simsimd_size_t i = 0;
566
- svfloat16_t ab_real_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
567
- svfloat16_t ab_imag_vec = svdupq_n_f16(0, 0, 0, 0, 0, 0, 0, 0);
768
+ svfloat16_t ab_real_vec = svdup_f16(0);
769
+ svfloat16_t ab_imag_vec = svdup_f16(0);
568
770
  do {
569
- svbool_t pg_vec = svwhilelt_b16((unsigned int)i, (unsigned int)n);
771
+ svbool_t pg_vec = svwhilelt_b16((unsigned int)i / 2, (unsigned int)n / 2);
570
772
  svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const*)a + i);
571
773
  svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const*)b + i);
572
774
  svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
@@ -874,6 +1076,82 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const* a, simsimd_f1
874
1076
  results[1] = _mm256_reduce_add_ps_dbl(ab_imag_vec);
875
1077
  }
876
1078
 
1079
+ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1080
+ simsimd_distance_t* result) {
1081
+
1082
+ __m256i ab_low_vec = _mm256_setzero_si256();
1083
+ __m256i ab_high_vec = _mm256_setzero_si256();
1084
+
1085
+ simsimd_size_t i = 0;
1086
+ for (; i + 32 <= n; i += 32) {
1087
+ __m256i a_vec = _mm256_loadu_si256((__m256i const*)(a + i));
1088
+ __m256i b_vec = _mm256_loadu_si256((__m256i const*)(b + i));
1089
+
1090
+ // Unpack int8 to int16
1091
+ __m256i a_low_16 = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a_vec, 0));
1092
+ __m256i a_high_16 = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a_vec, 1));
1093
+ __m256i b_low_16 = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(b_vec, 0));
1094
+ __m256i b_high_16 = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(b_vec, 1));
1095
+
1096
+ // Multiply and accumulate at int16 level, accumulate at int32 level
1097
+ ab_low_vec = _mm256_add_epi32(ab_low_vec, _mm256_madd_epi16(a_low_16, b_low_16));
1098
+ ab_high_vec = _mm256_add_epi32(ab_high_vec, _mm256_madd_epi16(a_high_16, b_high_16));
1099
+ }
1100
+
1101
+ // Horizontal sum across the 256-bit register
1102
+ __m256i ab_vec = _mm256_add_epi32(ab_low_vec, ab_high_vec);
1103
+ __m128i ab_sum = _mm_add_epi32(_mm256_extracti128_si256(ab_vec, 0), _mm256_extracti128_si256(ab_vec, 1));
1104
+ ab_sum = _mm_hadd_epi32(ab_sum, ab_sum);
1105
+ ab_sum = _mm_hadd_epi32(ab_sum, ab_sum);
1106
+
1107
+ // Take care of the tail:
1108
+ int ab = _mm_extract_epi32(ab_sum, 0);
1109
+ for (; i < n; ++i)
1110
+ ab += a[i] * b[i];
1111
+ *result = ab;
1112
+ }
1113
+
1114
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1115
+ simsimd_distance_t* result) {
1116
+ __m256 ab_vec = _mm256_setzero_ps();
1117
+ simsimd_size_t i = 0;
1118
+ for (; i + 8 <= n; i += 8) {
1119
+ // Upcasting from `bf16` to `f32` is done by shifting the `bf16` values by 16 bits to the left, like:
1120
+ // x = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(x), 16))
1121
+ __m256 a_vec =
1122
+ _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i const*)(a + i))), 16));
1123
+ __m256 b_vec =
1124
+ _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(_mm_loadu_si128((__m128i const*)(b + i))), 16));
1125
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
1126
+ }
1127
+
1128
+ // In case the software emulation for `bf16` scalars is enabled, the `simsimd_uncompress_bf16`
1129
+ // function will run. It is extremely slow, so even for the tail, let's combine serial
1130
+ // loads and stores with vectorized math.
1131
+ if (i < n) {
1132
+ union {
1133
+ __m128i bf16_vec;
1134
+ simsimd_bf16_t bf16[8];
1135
+ } a_padded_tail, b_padded_tail;
1136
+ simsimd_size_t j = 0;
1137
+ for (; i < n; ++i, ++j)
1138
+ a_padded_tail.bf16[j] = a[i], b_padded_tail.bf16[j] = b[i];
1139
+ for (; j < 8; ++j)
1140
+ a_padded_tail.bf16[j] = 0, b_padded_tail.bf16[j] = 0;
1141
+ __m256 a_vec = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(a_padded_tail.bf16_vec), 16));
1142
+ __m256 b_vec = _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(b_padded_tail.bf16_vec), 16));
1143
+ ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
1144
+ }
1145
+
1146
+ ab_vec = _mm256_add_ps(_mm256_permute2f128_ps(ab_vec, ab_vec, 1), ab_vec);
1147
+ ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
1148
+ ab_vec = _mm256_hadd_ps(ab_vec, ab_vec);
1149
+
1150
+ simsimd_f32_t f32_result;
1151
+ _mm_store_ss(&f32_result, _mm256_castps256_ps128(ab_vec));
1152
+ *result = f32_result;
1153
+ }
1154
+
877
1155
  #pragma clang attribute pop
878
1156
  #pragma GCC pop_options
879
1157
  #endif // SIMSIMD_TARGET_HASWELL
@@ -1109,6 +1387,125 @@ simsimd_vdot_f64c_skylake_cycle:
1109
1387
  #pragma GCC pop_options
1110
1388
  #endif // SIMSIMD_TARGET_SKYLAKE
1111
1389
 
1390
+ #if SIMSIMD_TARGET_GENOA
1391
+ #pragma GCC push_options
1392
+ #pragma GCC target("avx512f", "avx512vl", "bmi2", "avx512bw", "avx512bf16")
1393
+ #pragma clang attribute push(__attribute__((target("avx512f,avx512vl,bmi2,avx512bw,avx512bf16"))), apply_to = function)
1394
+
1395
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1396
+ simsimd_distance_t* result) {
1397
+ __m512 ab_vec = _mm512_setzero_ps();
1398
+ __m512i a_i16_vec, b_i16_vec;
1399
+
1400
+ simsimd_dot_bf16_genoa_cycle:
1401
+ if (n < 32) {
1402
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1403
+ a_i16_vec = _mm512_maskz_loadu_epi16(mask, a);
1404
+ b_i16_vec = _mm512_maskz_loadu_epi16(mask, b);
1405
+ n = 0;
1406
+ } else {
1407
+ a_i16_vec = _mm512_loadu_epi16(a);
1408
+ b_i16_vec = _mm512_loadu_epi16(b);
1409
+ a += 32, b += 32, n -= 32;
1410
+ }
1411
+ ab_vec = _mm512_dpbf16_ps(ab_vec, (__m512bh)(a_i16_vec), (__m512bh)(b_i16_vec));
1412
+ if (n)
1413
+ goto simsimd_dot_bf16_genoa_cycle;
1414
+
1415
+ *result = _mm512_reduce_add_ps(ab_vec);
1416
+ }
1417
+
1418
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1419
+ simsimd_distance_t* results) {
1420
+
1421
+ __m512 ab_real_vec = _mm512_setzero_ps();
1422
+ __m512 ab_imag_vec = _mm512_setzero_ps();
1423
+ __m512i a_vec;
1424
+ __m512i b_vec;
1425
+
1426
+ // We take into account, that FMS is the same as FMA with a negative multiplier.
1427
+ // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1428
+ // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1429
+ // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1430
+ // one of the vectors.
1431
+ __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1432
+ __m512i swap_adjacent_vec = _mm512_set_epi8( //
1433
+ 61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1434
+ 45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1435
+ 29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
1436
+ 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 // 1st 128-bit lane
1437
+ );
1438
+
1439
+ simsimd_dot_bf16c_genoa_cycle:
1440
+ if (n < 32) {
1441
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1442
+ a_vec = _mm512_maskz_loadu_epi16(mask, a);
1443
+ b_vec = _mm512_maskz_loadu_epi16(mask, b);
1444
+ n = 0;
1445
+ } else {
1446
+ a_vec = _mm512_loadu_epi16(a);
1447
+ b_vec = _mm512_loadu_epi16(b);
1448
+ a += 32, b += 32, n -= 32;
1449
+ }
1450
+ ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(_mm512_xor_si512(b_vec, sign_flip_vec)), (__m512bh)(a_vec));
1451
+ ab_imag_vec =
1452
+ _mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(_mm512_shuffle_epi8(b_vec, swap_adjacent_vec)), (__m512bh)(a_vec));
1453
+ if (n)
1454
+ goto simsimd_dot_bf16c_genoa_cycle;
1455
+
1456
+ // Reduce horizontal sums:
1457
+ results[0] = _mm512_reduce_add_ps(ab_real_vec);
1458
+ results[1] = _mm512_reduce_add_ps(ab_imag_vec);
1459
+ }
1460
+
1461
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1462
+ simsimd_distance_t* results) {
1463
+
1464
+ __m512 ab_real_vec = _mm512_setzero_ps();
1465
+ __m512 ab_imag_vec = _mm512_setzero_ps();
1466
+ __m512i a_vec;
1467
+ __m512i b_vec;
1468
+
1469
+ // We take into account, that FMS is the same as FMA with a negative multiplier.
1470
+ // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1471
+ // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1472
+ // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1473
+ // one of the vectors.
1474
+ __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1475
+ __m512i swap_adjacent_vec = _mm512_set_epi8( //
1476
+ 61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1477
+ 45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1478
+ 29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
1479
+ 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 // 1st 128-bit lane
1480
+ );
1481
+
1482
+ simsimd_dot_bf16c_genoa_cycle:
1483
+ if (n < 32) {
1484
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1485
+ a_vec = _mm512_maskz_loadu_epi16(mask, a);
1486
+ b_vec = _mm512_maskz_loadu_epi16(mask, b);
1487
+ n = 0;
1488
+ } else {
1489
+ a_vec = _mm512_loadu_epi16(a);
1490
+ b_vec = _mm512_loadu_epi16(b);
1491
+ a += 32, b += 32, n -= 32;
1492
+ }
1493
+ ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
1494
+ a_vec = _mm512_xor_si512(a_vec, sign_flip_vec);
1495
+ b_vec = _mm512_shuffle_epi8(b_vec, swap_adjacent_vec);
1496
+ ab_imag_vec = _mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
1497
+ if (n)
1498
+ goto simsimd_dot_bf16c_genoa_cycle;
1499
+
1500
+ // Reduce horizontal sums:
1501
+ results[0] = _mm512_reduce_add_ps(ab_real_vec);
1502
+ results[1] = _mm512_reduce_add_ps(ab_imag_vec);
1503
+ }
1504
+
1505
+ #pragma clang attribute pop
1506
+ #pragma GCC pop_options
1507
+ #endif // SIMSIMD_TARGET_GENOA
1508
+
1112
1509
  #if SIMSIMD_TARGET_SAPPHIRE
1113
1510
  #pragma GCC push_options
1114
1511
  #pragma GCC target("avx512f", "avx512vl", "bmi2", "avx512bw", "avx512fp16")
@@ -1228,6 +1625,43 @@ simsimd_dot_f16c_sapphire_cycle:
1228
1625
  #pragma clang attribute pop
1229
1626
  #pragma GCC pop_options
1230
1627
  #endif // SIMSIMD_TARGET_SAPPHIRE
1628
+
1629
+ #if SIMSIMD_TARGET_ICE
1630
+ #pragma GCC push_options
1631
+ #pragma GCC target("avx512f", "avx512vl", "bmi2", "avx512bw", "avx512vnni")
1632
+ #pragma clang attribute push(__attribute__((target("avx512f,avx512vl,bmi2,avx512bw,avx512vnni"))), apply_to = function)
1633
+
1634
+ SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1635
+ simsimd_distance_t* result) {
1636
+ __m512i ab_i32s_vec = _mm512_setzero_si512();
1637
+ __m512i a_vec, b_vec;
1638
+
1639
+ simsimd_dot_i8_ice_cycle:
1640
+ if (n < 32) {
1641
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1642
+ a_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a));
1643
+ b_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b));
1644
+ n = 0;
1645
+ } else {
1646
+ a_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(a));
1647
+ b_vec = _mm512_cvtepi8_epi16(_mm256_loadu_epi8(b));
1648
+ a += 32, b += 32, n -= 32;
1649
+ }
1650
+ // Unfortunately we can't use the `_mm512_dpbusd_epi32` intrinsics here either,
1651
+ // as it's asymmetric with respect to the sign of the input arguments:
1652
+ // Signed(ZeroExtend16(a.byte[4*j]) * SignExtend16(b.byte[4*j]))
1653
+ // So we have to use the `_mm512_dpwssd_epi32` intrinsics instead, upcasting
1654
+ // to 16-bit beforehand.
1655
+ ab_i32s_vec = _mm512_dpwssd_epi32(ab_i32s_vec, a_vec, b_vec);
1656
+ if (n)
1657
+ goto simsimd_dot_i8_ice_cycle;
1658
+
1659
+ *result = _mm512_reduce_add_epi32(ab_i32s_vec);
1660
+ }
1661
+
1662
+ #pragma clang attribute pop
1663
+ #pragma GCC pop_options
1664
+ #endif // SIMSIMD_TARGET_ICE
1231
1665
  #endif // SIMSIMD_TARGET_X86
1232
1666
 
1233
1667
  #ifdef __cplusplus