usearch 2.26.1 → 2.26.3

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 (146) hide show
  1. package/README.md +1 -1
  2. package/include/usearch/index.hpp +5 -1
  3. package/numkong/CITATION.cff +13 -9
  4. package/numkong/CONTRIBUTING.md +29 -24
  5. package/numkong/Cargo.toml +1 -1
  6. package/numkong/README.md +21 -7
  7. package/numkong/binding.gyp +47 -31
  8. package/numkong/build.rs +15 -5
  9. package/numkong/c/dispatch.h +0 -1
  10. package/numkong/c/parallel.c +139 -0
  11. package/numkong/c/parallel.h +47 -0
  12. package/numkong/cmake/toolchain-wasi.cmake +39 -5
  13. package/numkong/include/README.md +24 -17
  14. package/numkong/include/numkong/attention/sapphireamx.h +6 -6
  15. package/numkong/include/numkong/attention/sme.h +2 -2
  16. package/numkong/include/numkong/capabilities.h +2 -2
  17. package/numkong/include/numkong/cast/README.md +9 -7
  18. package/numkong/include/numkong/cast/loongsonasx.h +48 -40
  19. package/numkong/include/numkong/cast/serial.h +89 -11
  20. package/numkong/include/numkong/cast.h +3 -3
  21. package/numkong/include/numkong/curved/README.md +10 -10
  22. package/numkong/include/numkong/curved/neon.h +1 -0
  23. package/numkong/include/numkong/curved/neonbfdot.h +1 -0
  24. package/numkong/include/numkong/curved/smef64.h +1 -0
  25. package/numkong/include/numkong/dot/README.md +18 -18
  26. package/numkong/include/numkong/dot/alder.h +8 -8
  27. package/numkong/include/numkong/dot/haswell.h +8 -8
  28. package/numkong/include/numkong/dot/icelake.h +12 -12
  29. package/numkong/include/numkong/dot/loongsonasx.h +6 -6
  30. package/numkong/include/numkong/dot/neon.h +2 -1
  31. package/numkong/include/numkong/dot/neonbfdot.h +1 -0
  32. package/numkong/include/numkong/dot/neonfhm.h +1 -0
  33. package/numkong/include/numkong/dot/neonsdot.h +1 -0
  34. package/numkong/include/numkong/dot/powervsx.h +3 -3
  35. package/numkong/include/numkong/dot/serial.h +19 -6
  36. package/numkong/include/numkong/dot/skylake.h +4 -4
  37. package/numkong/include/numkong/dot/svehalf.h +1 -1
  38. package/numkong/include/numkong/dot/v128relaxed.h +24 -23
  39. package/numkong/include/numkong/dots/README.md +26 -26
  40. package/numkong/include/numkong/dots/genoa.h +3 -1
  41. package/numkong/include/numkong/dots/icelake.h +1 -0
  42. package/numkong/include/numkong/dots/powervsx.h +1 -1
  43. package/numkong/include/numkong/dots/serial.h +32 -33
  44. package/numkong/include/numkong/dots/sierra.h +4 -3
  45. package/numkong/include/numkong/each/README.md +5 -3
  46. package/numkong/include/numkong/each/sapphire.h +2 -0
  47. package/numkong/include/numkong/each/skylake.h +1 -0
  48. package/numkong/include/numkong/geospatial/README.md +6 -6
  49. package/numkong/include/numkong/geospatial/v128relaxed.h +1 -0
  50. package/numkong/include/numkong/maxsim/README.md +5 -5
  51. package/numkong/include/numkong/maxsim/genoa.h +4 -0
  52. package/numkong/include/numkong/maxsim/icelake.h +1 -0
  53. package/numkong/include/numkong/maxsim/serial.h +22 -9
  54. package/numkong/include/numkong/maxsim/sme.h +3 -2
  55. package/numkong/include/numkong/mesh/README.md +7 -4
  56. package/numkong/include/numkong/mesh/genoa.h +2 -2
  57. package/numkong/include/numkong/mesh/haswell.h +8 -8
  58. package/numkong/include/numkong/mesh/neon.h +6 -6
  59. package/numkong/include/numkong/mesh/neonbfdot.h +3 -2
  60. package/numkong/include/numkong/mesh/neonfhm.h +3 -2
  61. package/numkong/include/numkong/mesh/rvv.h +4 -4
  62. package/numkong/include/numkong/mesh/serial.h +17 -17
  63. package/numkong/include/numkong/mesh/skylake.h +4 -4
  64. package/numkong/include/numkong/mesh/v128relaxed.h +4 -4
  65. package/numkong/include/numkong/mesh.hpp +2 -2
  66. package/numkong/include/numkong/numkong.h +1 -1
  67. package/numkong/include/numkong/numkong.hpp +1 -1
  68. package/numkong/include/numkong/probability/README.md +17 -15
  69. package/numkong/include/numkong/probability/skylake.h +1 -1
  70. package/numkong/include/numkong/reduce/README.md +27 -21
  71. package/numkong/include/numkong/reduce/genoa.h +2 -1
  72. package/numkong/include/numkong/reduce/icelake.h +1 -0
  73. package/numkong/include/numkong/reduce/neon.h +1 -1
  74. package/numkong/include/numkong/reduce/serial.h +9 -9
  75. package/numkong/include/numkong/reduce/skylake.h +4 -4
  76. package/numkong/include/numkong/reduce.hpp +38 -0
  77. package/numkong/include/numkong/scalar/README.md +29 -28
  78. package/numkong/include/numkong/set/README.md +17 -14
  79. package/numkong/include/numkong/set/haswell.h +15 -16
  80. package/numkong/include/numkong/set/icelake.h +15 -16
  81. package/numkong/include/numkong/set/loongsonasx.h +12 -10
  82. package/numkong/include/numkong/set/neon.h +15 -19
  83. package/numkong/include/numkong/set/powervsx.h +15 -21
  84. package/numkong/include/numkong/set/serial.h +19 -20
  85. package/numkong/include/numkong/set/v128relaxed.h +10 -8
  86. package/numkong/include/numkong/sets/icelake.h +1 -0
  87. package/numkong/include/numkong/sparse/README.md +4 -4
  88. package/numkong/include/numkong/sparse/icelake.h +1 -0
  89. package/numkong/include/numkong/sparse/neon.h +1 -0
  90. package/numkong/include/numkong/sparse/serial.h +10 -3
  91. package/numkong/include/numkong/sparse/turin.h +1 -0
  92. package/numkong/include/numkong/spatial/README.md +25 -24
  93. package/numkong/include/numkong/spatial/alder.h +2 -1
  94. package/numkong/include/numkong/spatial/haswell.h +48 -40
  95. package/numkong/include/numkong/spatial/icelake.h +2 -1
  96. package/numkong/include/numkong/spatial/neon.h +54 -46
  97. package/numkong/include/numkong/spatial/neonbfdot.h +1 -1
  98. package/numkong/include/numkong/spatial/powervsx.h +54 -46
  99. package/numkong/include/numkong/spatial/serial.h +56 -46
  100. package/numkong/include/numkong/spatial/sierra.h +4 -3
  101. package/numkong/include/numkong/spatial/skylake.h +27 -22
  102. package/numkong/include/numkong/spatial/sve.h +6 -6
  103. package/numkong/include/numkong/spatial/svebfdot.h +1 -1
  104. package/numkong/include/numkong/spatial/svehalf.h +1 -1
  105. package/numkong/include/numkong/spatial/v128relaxed.h +36 -30
  106. package/numkong/include/numkong/spatials/README.md +15 -15
  107. package/numkong/include/numkong/spatials/genoa.h +3 -0
  108. package/numkong/include/numkong/spatials/graniteamx.h +2 -0
  109. package/numkong/include/numkong/spatials/icelake.h +1 -0
  110. package/numkong/include/numkong/spatials/sierra.h +1 -0
  111. package/numkong/include/numkong/tensor.hpp +236 -123
  112. package/numkong/include/numkong/trigonometry/README.md +4 -4
  113. package/numkong/include/numkong/trigonometry/serial.h +6 -6
  114. package/numkong/include/numkong/trigonometry/v128relaxed.h +1 -0
  115. package/numkong/include/numkong/types.hpp +46 -16
  116. package/numkong/include/numkong/vector.hpp +116 -23
  117. package/numkong/javascript/@numkong-darwin-arm64/package.json +1 -1
  118. package/numkong/javascript/@numkong-darwin-x64/package.json +1 -1
  119. package/numkong/javascript/@numkong-linux-arm64/package.json +1 -1
  120. package/numkong/javascript/@numkong-linux-x64/package.json +1 -1
  121. package/numkong/javascript/@numkong-win32-arm64/package.json +1 -1
  122. package/numkong/javascript/@numkong-win32-x64/package.json +1 -1
  123. package/numkong/javascript/README.md +18 -16
  124. package/numkong/javascript/numkong.c +70 -40
  125. package/numkong/package.json +8 -8
  126. package/numkong/rust/README.md +19 -9
  127. package/numkong/rust/cast.rs +52 -1
  128. package/numkong/rust/each.rs +480 -7
  129. package/numkong/rust/matrix.rs +877 -216
  130. package/numkong/rust/numkong.rs +12 -8
  131. package/numkong/rust/reduce.rs +433 -5
  132. package/numkong/rust/tensor.rs +450 -842
  133. package/numkong/rust/vector.rs +520 -101
  134. package/numkong/swift/README.md +5 -3
  135. package/numkong/test/README.md +4 -4
  136. package/numkong/test/test-wasi.mjs +17 -0
  137. package/numkong/test/test_each.cpp +30 -1
  138. package/numkong/test/test_each.py +74 -1
  139. package/numkong/test/test_reduce.cpp +51 -0
  140. package/numkong/test/test_tensor.cpp +2 -63
  141. package/numkong/test/test_tensor.py +93 -0
  142. package/package.json +1 -1
  143. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  144. package/prebuilds/linux-arm64/usearch.node +0 -0
  145. package/prebuilds/linux-x64/usearch.node +0 -0
  146. package/prebuilds/win32-x64/usearch.node +0 -0
@@ -25,8 +25,10 @@
25
25
  #if NK_TARGET_SKYLAKE
26
26
 
27
27
  #include "numkong/types.h"
28
- #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
29
- #include "numkong/dot/skylake.h" // `nk_dot_f64x8_state_skylake_t`
28
+ #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
29
+ #include "numkong/dot/skylake.h" // `nk_dot_f64x8_state_skylake_t`
30
+ #include "numkong/scalar/haswell.h" // `nk_f32_sqrt_haswell`, `nk_f64_sqrt_haswell`
31
+ #include "numkong/spatial/haswell.h" // `nk_angular_normalize_f32_haswell_`
30
32
 
31
33
  #if defined(__cplusplus)
32
34
  extern "C" {
@@ -224,61 +226,64 @@ nk_angular_f64_skylake_cycle:
224
226
  }
225
227
 
226
228
  /** @brief Angular from_dot for native f64: 1 − dot / √(query_sumsq × target_sumsq) for 4 pairs. */
227
- NK_INTERNAL void nk_angular_f64x4_from_dot_skylake_(nk_b256_vec_t dots, nk_f64_t query_sumsq,
228
- nk_b256_vec_t target_sumsqs, nk_b256_vec_t *results) {
229
- __m256d dots_f64x4 = dots.ymm_pd;
229
+ NK_INTERNAL void nk_angular_f64x4_from_dot_skylake_(nk_b256_vec_t const *dots_vec, nk_f64_t query_sumsq,
230
+ nk_b256_vec_t const *target_sumsqs_vec, nk_b256_vec_t *result_vec) {
231
+ __m256d dots_f64x4 = dots_vec->ymm_pd;
230
232
  __m256d query_sumsq_f64x4 = _mm256_set1_pd(query_sumsq);
231
- __m256d products_f64x4 = _mm256_mul_pd(query_sumsq_f64x4, target_sumsqs.ymm_pd);
233
+ __m256d products_f64x4 = _mm256_mul_pd(query_sumsq_f64x4, target_sumsqs_vec->ymm_pd);
232
234
  __m256d sqrt_products_f64x4 = _mm256_sqrt_pd(products_f64x4);
233
235
  __m256d normalized_f64x4 = _mm256_div_pd(dots_f64x4, sqrt_products_f64x4);
234
236
  __m256d ones_f64x4 = _mm256_set1_pd(1.0);
235
237
  __m256d angular_f64x4 = _mm256_sub_pd(ones_f64x4, normalized_f64x4);
236
- results->ymm_pd = _mm256_max_pd(angular_f64x4, _mm256_setzero_pd());
238
+ result_vec->ymm_pd = _mm256_max_pd(angular_f64x4, _mm256_setzero_pd());
237
239
  }
238
240
 
239
241
  /** @brief Euclidean from_dot for native f64: √(query_sumsq + target_sumsq − 2 × dot) for 4 pairs. */
240
- NK_INTERNAL void nk_euclidean_f64x4_from_dot_skylake_(nk_b256_vec_t dots, nk_f64_t query_sumsq,
241
- nk_b256_vec_t target_sumsqs, nk_b256_vec_t *results) {
242
- __m256d dots_f64x4 = dots.ymm_pd;
242
+ NK_INTERNAL void nk_euclidean_f64x4_from_dot_skylake_(nk_b256_vec_t const *dots_vec, nk_f64_t query_sumsq,
243
+ nk_b256_vec_t const *target_sumsqs_vec,
244
+ nk_b256_vec_t *result_vec) {
245
+ __m256d dots_f64x4 = dots_vec->ymm_pd;
243
246
  __m256d query_sumsq_f64x4 = _mm256_set1_pd(query_sumsq);
244
247
  __m256d two_f64x4 = _mm256_set1_pd(2.0);
245
- __m256d sum_sq_f64x4 = _mm256_add_pd(query_sumsq_f64x4, target_sumsqs.ymm_pd);
248
+ __m256d sum_sq_f64x4 = _mm256_add_pd(query_sumsq_f64x4, target_sumsqs_vec->ymm_pd);
246
249
  __m256d dist_sq_f64x4 = _mm256_fnmadd_pd(two_f64x4, dots_f64x4, sum_sq_f64x4);
247
250
  __m256d zeros_f64x4 = _mm256_setzero_pd();
248
251
  __m256d clamped_f64x4 = _mm256_max_pd(dist_sq_f64x4, zeros_f64x4);
249
- results->ymm_pd = _mm256_sqrt_pd(clamped_f64x4);
252
+ result_vec->ymm_pd = _mm256_sqrt_pd(clamped_f64x4);
250
253
  }
251
254
 
252
255
  /** @brief Angular from_dot: f32 dots upcast to f64 for precision. Output via nk_b128_vec_t (f32). */
253
- NK_INTERNAL void nk_angular_through_f64_from_dot_skylake_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
254
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
255
- __m128 dots_f32x4 = dots.xmm_ps;
256
+ NK_INTERNAL void nk_angular_through_f64_from_dot_skylake_(nk_b128_vec_t const *dots_vec, nk_f32_t query_sumsq,
257
+ nk_b128_vec_t const *target_sumsqs_vec,
258
+ nk_b128_vec_t *result_vec) {
259
+ __m128 dots_f32x4 = dots_vec->xmm_ps;
256
260
  __m256d dots_f64x4 = _mm256_cvtps_pd(dots_f32x4);
257
261
  __m256d query_sumsq_f64x4 = _mm256_set1_pd((nk_f64_t)query_sumsq);
258
- __m256d target_sumsqs_f64x4 = _mm256_cvtps_pd(target_sumsqs.xmm_ps);
262
+ __m256d target_sumsqs_f64x4 = _mm256_cvtps_pd(target_sumsqs_vec->xmm_ps);
259
263
  __m256d products_f64x4 = _mm256_mul_pd(query_sumsq_f64x4, target_sumsqs_f64x4);
260
264
  __m256d sqrt_products_f64x4 = _mm256_sqrt_pd(products_f64x4);
261
265
  __m256d normalized_f64x4 = _mm256_div_pd(dots_f64x4, sqrt_products_f64x4);
262
266
  __m256d ones_f64x4 = _mm256_set1_pd(1.0);
263
267
  __m256d angular_f64x4 = _mm256_sub_pd(ones_f64x4, normalized_f64x4);
264
268
  angular_f64x4 = _mm256_max_pd(angular_f64x4, _mm256_setzero_pd());
265
- results->xmm_ps = _mm256_cvtpd_ps(angular_f64x4);
269
+ result_vec->xmm_ps = _mm256_cvtpd_ps(angular_f64x4);
266
270
  }
267
271
 
268
272
  /** @brief Euclidean from_dot: f32 dots upcast to f64 for precision. Output via nk_b128_vec_t (f32). */
269
- NK_INTERNAL void nk_euclidean_through_f64_from_dot_skylake_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
270
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
271
- __m128 dots_f32x4 = dots.xmm_ps;
273
+ NK_INTERNAL void nk_euclidean_through_f64_from_dot_skylake_(nk_b128_vec_t const *dots_vec, nk_f32_t query_sumsq,
274
+ nk_b128_vec_t const *target_sumsqs_vec,
275
+ nk_b128_vec_t *result_vec) {
276
+ __m128 dots_f32x4 = dots_vec->xmm_ps;
272
277
  __m256d dots_f64x4 = _mm256_cvtps_pd(dots_f32x4);
273
278
  __m256d query_sumsq_f64x4 = _mm256_set1_pd((nk_f64_t)query_sumsq);
274
- __m256d target_sumsqs_f64x4 = _mm256_cvtps_pd(target_sumsqs.xmm_ps);
279
+ __m256d target_sumsqs_f64x4 = _mm256_cvtps_pd(target_sumsqs_vec->xmm_ps);
275
280
  __m256d two_f64x4 = _mm256_set1_pd(2.0);
276
281
  __m256d sum_sq_f64x4 = _mm256_add_pd(query_sumsq_f64x4, target_sumsqs_f64x4);
277
282
  __m256d dist_sq_f64x4 = _mm256_fnmadd_pd(two_f64x4, dots_f64x4, sum_sq_f64x4);
278
283
  __m256d zeros_f64x4 = _mm256_setzero_pd();
279
284
  __m256d clamped_f64x4 = _mm256_max_pd(dist_sq_f64x4, zeros_f64x4);
280
285
  __m256d dist_f64x4 = _mm256_sqrt_pd(clamped_f64x4);
281
- results->xmm_ps = _mm256_cvtpd_ps(dist_f64x4);
286
+ result_vec->xmm_ps = _mm256_cvtpd_ps(dist_f64x4);
282
287
  }
283
288
 
284
289
  #pragma endregion F32 and F64 Floats
@@ -60,9 +60,9 @@ extern "C" {
60
60
  * Marked `__arm_streaming_compatible` so the helper is callable from both streaming
61
61
  * (SME) and non-streaming (SVE) contexts without mode transitions.
62
62
  *
63
- * @param predicate Active-lane mask
64
- * @param x Input vector (must be positive for meaningful results)
65
- * @return Approximate 1/sqrt(x) with ~23-bit mantissa accuracy
63
+ * @param predicate_b32x Active-lane mask
64
+ * @param x Input vector (must be positive for meaningful results)
65
+ * @return Approximate 1/sqrt(x) with ~23-bit mantissa accuracy
66
66
  */
67
67
  NK_INTERNAL svfloat32_t nk_rsqrt_f32x_sve_(svbool_t predicate_b32x, svfloat32_t x) NK_STREAMING_COMPATIBLE_ {
68
68
  svfloat32_t r = svrsqrte_f32(x);
@@ -80,9 +80,9 @@ NK_INTERNAL svfloat32_t nk_rsqrt_f32x_sve_(svbool_t predicate_b32x, svfloat32_t
80
80
  * Marked `__arm_streaming_compatible` so the helper is callable from both streaming
81
81
  * (SME) and non-streaming (SVE) contexts without mode transitions.
82
82
  *
83
- * @param predicate_b32x Active-lane mask
84
- * @param x Input vector (must be positive for meaningful results)
85
- * @return Approximate 1/sqrt(x) with ~52-bit mantissa accuracy
83
+ * @param predicate_b64x Active-lane mask
84
+ * @param x Input vector (must be positive for meaningful results)
85
+ * @return Approximate 1/sqrt(x) with ~52-bit mantissa accuracy
86
86
  */
87
87
  NK_INTERNAL svfloat64_t nk_rsqrt_f64x_sve_(svbool_t predicate_b64x, svfloat64_t x) NK_STREAMING_COMPATIBLE_ {
88
88
  svfloat64_t r = svrsqrte_f64(x);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @brief SIMD-accelerated Spatial Similarity Measures for SVE BF16.
3
- * @file include/numkong/spatial/sve.h
3
+ * @file include/numkong/spatial/svebfdot.h
4
4
  * @author Ash Vardanian
5
5
  * @date December 27, 2025
6
6
  *
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @brief SIMD-accelerated Spatial Similarity Measures for SVE FP16.
3
- * @file include/numkong/spatial/sve.h
3
+ * @file include/numkong/spatial/svehalf.h
4
4
  * @author Ash Vardanian
5
5
  * @date December 27, 2025
6
6
  *
@@ -618,75 +618,81 @@ nk_angular_e3m2_v128relaxed_cycle:
618
618
  #pragma region Spatial From Dot Helpers
619
619
 
620
620
  /** @brief Angular from_dot: computes 1 − dot / √(query_sumsq × target_sumsq) for 4 pairs in f32. */
621
- NK_INTERNAL void nk_angular_through_f32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
622
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
623
- v128_t dots_f32x4 = dots.v128;
621
+ NK_INTERNAL void nk_angular_through_f32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_f32_t query_sumsq,
622
+ nk_b128_vec_t const *target_sumsqs_vec,
623
+ nk_b128_vec_t *result_vec) {
624
+ v128_t dots_f32x4 = dots_vec->v128;
624
625
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat(query_sumsq);
625
- v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, target_sumsqs.v128);
626
+ v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, target_sumsqs_vec->v128);
626
627
  v128_t sqrt_products_f32x4 = wasm_f32x4_sqrt(products_f32x4);
627
628
  v128_t normalized_f32x4 = wasm_f32x4_div(dots_f32x4, sqrt_products_f32x4);
628
629
  v128_t angular_f32x4 = wasm_f32x4_sub(wasm_f32x4_splat(1.0f), normalized_f32x4);
629
- results->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
630
+ result_vec->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
630
631
  }
631
632
 
632
633
  /** @brief Euclidean from_dot: computes √(query_sumsq + target_sumsq − 2 × dot) for 4 pairs in f32. */
633
- NK_INTERNAL void nk_euclidean_through_f32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
634
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
635
- v128_t dots_f32x4 = dots.v128;
634
+ NK_INTERNAL void nk_euclidean_through_f32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_f32_t query_sumsq,
635
+ nk_b128_vec_t const *target_sumsqs_vec,
636
+ nk_b128_vec_t *result_vec) {
637
+ v128_t dots_f32x4 = dots_vec->v128;
636
638
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat(query_sumsq);
637
639
  v128_t two_f32x4 = wasm_f32x4_splat(2.0f);
638
- v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, target_sumsqs.v128);
640
+ v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, target_sumsqs_vec->v128);
639
641
  v128_t dist_sq_f32x4 = wasm_f32x4_relaxed_nmadd(two_f32x4, dots_f32x4, sum_sq_f32x4);
640
642
  dist_sq_f32x4 = wasm_f32x4_max(dist_sq_f32x4, wasm_f32x4_splat(0.0f));
641
- results->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
643
+ result_vec->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
642
644
  }
643
645
 
644
646
  /** @brief Angular from_dot for i32 accumulators: cast to f32, then angular normalization. 4 pairs. */
645
- NK_INTERNAL void nk_angular_through_i32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_i32_t query_sumsq,
646
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
647
- v128_t dots_f32x4 = wasm_f32x4_convert_i32x4(dots.v128);
647
+ NK_INTERNAL void nk_angular_through_i32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_i32_t query_sumsq,
648
+ nk_b128_vec_t const *target_sumsqs_vec,
649
+ nk_b128_vec_t *result_vec) {
650
+ v128_t dots_f32x4 = wasm_f32x4_convert_i32x4(dots_vec->v128);
648
651
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat((nk_f32_t)query_sumsq);
649
- v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, wasm_f32x4_convert_i32x4(target_sumsqs.v128));
652
+ v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, wasm_f32x4_convert_i32x4(target_sumsqs_vec->v128));
650
653
  v128_t sqrt_products_f32x4 = wasm_f32x4_sqrt(products_f32x4);
651
654
  v128_t normalized_f32x4 = wasm_f32x4_div(dots_f32x4, sqrt_products_f32x4);
652
655
  v128_t angular_f32x4 = wasm_f32x4_sub(wasm_f32x4_splat(1.0f), normalized_f32x4);
653
- results->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
656
+ result_vec->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
654
657
  }
655
658
 
656
659
  /** @brief Euclidean from_dot for i32 accumulators: cast to f32, then √(a² + b² − 2ab). 4 pairs. */
657
- NK_INTERNAL void nk_euclidean_through_i32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_i32_t query_sumsq,
658
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
659
- v128_t dots_f32x4 = wasm_f32x4_convert_i32x4(dots.v128);
660
+ NK_INTERNAL void nk_euclidean_through_i32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_i32_t query_sumsq,
661
+ nk_b128_vec_t const *target_sumsqs_vec,
662
+ nk_b128_vec_t *result_vec) {
663
+ v128_t dots_f32x4 = wasm_f32x4_convert_i32x4(dots_vec->v128);
660
664
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat((nk_f32_t)query_sumsq);
661
665
  v128_t two_f32x4 = wasm_f32x4_splat(2.0f);
662
- v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, wasm_f32x4_convert_i32x4(target_sumsqs.v128));
666
+ v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, wasm_f32x4_convert_i32x4(target_sumsqs_vec->v128));
663
667
  v128_t dist_sq_f32x4 = wasm_f32x4_relaxed_nmadd(two_f32x4, dots_f32x4, sum_sq_f32x4);
664
668
  dist_sq_f32x4 = wasm_f32x4_max(dist_sq_f32x4, wasm_f32x4_splat(0.0f));
665
- results->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
669
+ result_vec->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
666
670
  }
667
671
 
668
672
  /** @brief Angular from_dot for u32 accumulators: cast to f32, then angular normalization. 4 pairs. */
669
- NK_INTERNAL void nk_angular_through_u32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_u32_t query_sumsq,
670
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
671
- v128_t dots_f32x4 = wasm_f32x4_convert_u32x4(dots.v128);
673
+ NK_INTERNAL void nk_angular_through_u32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_u32_t query_sumsq,
674
+ nk_b128_vec_t const *target_sumsqs_vec,
675
+ nk_b128_vec_t *result_vec) {
676
+ v128_t dots_f32x4 = wasm_f32x4_convert_u32x4(dots_vec->v128);
672
677
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat((nk_f32_t)query_sumsq);
673
- v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, wasm_f32x4_convert_u32x4(target_sumsqs.v128));
678
+ v128_t products_f32x4 = wasm_f32x4_mul(query_sumsq_f32x4, wasm_f32x4_convert_u32x4(target_sumsqs_vec->v128));
674
679
  v128_t sqrt_products_f32x4 = wasm_f32x4_sqrt(products_f32x4);
675
680
  v128_t normalized_f32x4 = wasm_f32x4_div(dots_f32x4, sqrt_products_f32x4);
676
681
  v128_t angular_f32x4 = wasm_f32x4_sub(wasm_f32x4_splat(1.0f), normalized_f32x4);
677
- results->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
682
+ result_vec->v128 = wasm_f32x4_max(angular_f32x4, wasm_f32x4_splat(0.0f));
678
683
  }
679
684
 
680
685
  /** @brief Euclidean from_dot for u32 accumulators: cast to f32, then √(a² + b² − 2ab). 4 pairs. */
681
- NK_INTERNAL void nk_euclidean_through_u32_from_dot_v128relaxed_(nk_b128_vec_t dots, nk_u32_t query_sumsq,
682
- nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
683
- v128_t dots_f32x4 = wasm_f32x4_convert_u32x4(dots.v128);
686
+ NK_INTERNAL void nk_euclidean_through_u32_from_dot_v128relaxed_(nk_b128_vec_t const *dots_vec, nk_u32_t query_sumsq,
687
+ nk_b128_vec_t const *target_sumsqs_vec,
688
+ nk_b128_vec_t *result_vec) {
689
+ v128_t dots_f32x4 = wasm_f32x4_convert_u32x4(dots_vec->v128);
684
690
  v128_t query_sumsq_f32x4 = wasm_f32x4_splat((nk_f32_t)query_sumsq);
685
691
  v128_t two_f32x4 = wasm_f32x4_splat(2.0f);
686
- v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, wasm_f32x4_convert_u32x4(target_sumsqs.v128));
692
+ v128_t sum_sq_f32x4 = wasm_f32x4_add(query_sumsq_f32x4, wasm_f32x4_convert_u32x4(target_sumsqs_vec->v128));
687
693
  v128_t dist_sq_f32x4 = wasm_f32x4_relaxed_nmadd(two_f32x4, dots_f32x4, sum_sq_f32x4);
688
694
  dist_sq_f32x4 = wasm_f32x4_max(dist_sq_f32x4, wasm_f32x4_splat(0.0f));
689
- results->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
695
+ result_vec->v128 = wasm_f32x4_sqrt(dist_sq_f32x4);
690
696
  }
691
697
 
692
698
  #pragma endregion Spatial From Dot Helpers
@@ -34,20 +34,20 @@ def euclideans_packed(a: np.ndarray, b: np.ndarray) -> np.ndarray:
34
34
 
35
35
  ## Input & Output Types
36
36
 
37
- | Input Type | Output Type | Description |
38
- | ---------- | ----------- | ---------------------------------------------- |
39
- | `f64` | `f64` | 64-bit IEEE 754 double precision |
40
- | `f32` | `f32` | 32-bit IEEE 754 single precision |
41
- | `f16` | `f32` | 16-bit IEEE 754 half precision, widened output |
42
- | `bf16` | `f32` | 16-bit brain float, widened output |
43
- | `e4m3` | `f32` | 8-bit Float8: 4 exponent, 3 mantissa bits |
44
- | `e5m2` | `f32` | 8-bit Float8: 5 exponent, 2 mantissa bits |
45
- | `e2m3` | `f32` | 8-bit MX format: 2 exponent, 3 mantissa bits |
46
- | `e3m2` | `f32` | 8-bit MX format: 3 exponent, 2 mantissa bits |
47
- | `i8` | `f32` | 8-bit signed integers, float output |
48
- | `u8` | `f32` | 8-bit unsigned integers, float output |
49
- | `i4` | `f32` | 4-bit signed integers, float output |
50
- | `u4` | `f32` | 4-bit unsigned integers, float output |
37
+ | Input Type | Output Type | Description |
38
+ | ---------- | ----------- | ------------------------------------------------ |
39
+ | `f64` | `f64` | 64-bit IEEE 754 double precision |
40
+ | `f32` | `f64` | 32-bit IEEE 754 single precision, widened output |
41
+ | `f16` | `f32` | 16-bit IEEE 754 half precision, widened output |
42
+ | `bf16` | `f32` | 16-bit brain float, widened output |
43
+ | `e4m3` | `f32` | 8-bit Float8: 4 exponent, 3 mantissa bits |
44
+ | `e5m2` | `f32` | 8-bit Float8: 5 exponent, 2 mantissa bits |
45
+ | `e2m3` | `f32` | 8-bit MX format: 2 exponent, 3 mantissa bits |
46
+ | `e3m2` | `f32` | 8-bit MX format: 3 exponent, 2 mantissa bits |
47
+ | `i8` | `f32` | 8-bit signed integers, float output |
48
+ | `u8` | `f32` | 8-bit unsigned integers, float output |
49
+ | `i4` | `f32` | 4-bit signed integers, float output |
50
+ | `u4` | `f32` | 4-bit unsigned integers, float output |
51
51
 
52
52
  ## Optimizations
53
53
 
@@ -65,7 +65,7 @@ The singular `spatial/` kernels compute these three sums ($\sum a_i b_i$, $\sum
65
65
  `nk_angular_through_f32_from_dot_haswell_` replaces this with hardware `_mm_rsqrt_ps` (~12-bit approximation, 5cy latency, 1/cy on port 0) plus one Newton-Raphson refinement step (~22–24 correct bits).
66
66
  `nk_euclidean_through_f32_from_dot_serial_` computes `sqrt(x)` as `x * rsqrt(x)` — reusing the same rsqrt path.
67
67
  `nk_euclidean_through_f32_from_dot_haswell_` uses exact `_mm_sqrt_ps` (11cy latency, 7cy throughput for XMM) instead of the rsqrt approximation — the subtraction $\|a\|^2 + \|b\|^2 - 2 \cdot \text{dot}$ can produce values near zero where rsqrt error would be amplified by the subsequent multiply.
68
- For Float64, all backends use exact division and sqrt — no fast rsqrt approximation, since reaching 52 mantissa bits of precision would need 4+ Newton-Raphson iterations, negating the speed advantage.
68
+ For Float64, the SIMD backends use exact division and sqrt — no fast rsqrt approximation, since reaching 52 mantissa bits of precision would need 4+ Newton-Raphson iterations, negating the speed advantage — while `nk_angular_through_f64_from_dot_serial_` still routes through `nk_f64_rsqrt_serial` and its four iterations.
69
69
  The 4-wide finalizer batching amortizes these costs: one rsqrt or sqrt call processes 4 output elements simultaneously, hiding the latency behind the GEMM tile's computation.
70
70
 
71
71
  ### Norm Precomputation in Packed Buffers
@@ -14,6 +14,9 @@
14
14
 
15
15
  #include "numkong/spatial/haswell.h"
16
16
  #include "numkong/dots/genoa.h"
17
+ #include "numkong/cast/icelake.h" // `nk_e5m2x32_to_bf16x32_icelake_`
18
+ #include "numkong/dot/skylake.h" // `nk_dot_through_f32_finalize_skylake_`
19
+ #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
17
20
 
18
21
  #if defined(__cplusplus)
19
22
  extern "C" {
@@ -15,6 +15,8 @@
15
15
  #include "numkong/spatial/skylake.h"
16
16
  #include "numkong/spatial/serial.h"
17
17
  #include "numkong/dots/graniteamx.h"
18
+ #include "numkong/dots/sapphireamx.h" // `nk_compiler_barrier_sapphireamx_`
19
+ #include "numkong/spatials/sapphireamx.h" // `nk_angulars_row_f32dots_sapphireamx_`
18
20
 
19
21
  #if defined(__cplusplus)
20
22
  extern "C" {
@@ -14,6 +14,7 @@
14
14
 
15
15
  #include "numkong/spatial/haswell.h"
16
16
  #include "numkong/dots/icelake.h"
17
+ #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
17
18
 
18
19
  #if defined(__cplusplus)
19
20
  extern "C" {
@@ -15,6 +15,7 @@
15
15
  #include "numkong/spatial/haswell.h"
16
16
  #include "numkong/spatial/serial.h"
17
17
  #include "numkong/dots/sierra.h"
18
+ #include "numkong/reduce/haswell.h" // `nk_reduce_add_i32x8_haswell_`
18
19
 
19
20
  #if defined(__cplusplus)
20
21
  extern "C" {