usearch 2.21.4 → 2.25.0

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 (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +224 -48
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,997 @@
1
+ /**
2
+ * @brief SIMD-accelerated Dot Products for Ice Lake.
3
+ * @file include/numkong/dot/icelake.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/dot.h
8
+ *
9
+ * @section dot_icelake_instructions VNNI Instructions Performance
10
+ *
11
+ * Intrinsic Instruction Icelake Genoa
12
+ * _mm512_dpwssd_epi32 VPDPWSSD (ZMM, ZMM, ZMM) 5cy @ p0 4cy @ p01
13
+ * _mm512_dpbusd_epi32 VPDPBUSD (ZMM, ZMM, ZMM) 5cy @ p0 4cy @ p01
14
+ * _mm512_madd_epi16 VPMADDWD (ZMM, ZMM, ZMM) 5cy @ p05 3cy @ p01
15
+ *
16
+ * Ice Lake introduces AVX-512 VNNI for accelerated integer dot products. VNNI instructions bottleneck
17
+ * on port 0, limiting throughput to 1/cy. AMD Genoa dual-issues on ports 0-1, achieving 0.5/cy throughput.
18
+ *
19
+ * @section dot_icelake_stateful Stateful Streaming Logic
20
+ *
21
+ * To build memory-optimal tiled algorithms, this file defines following structures and force-inlined
22
+ * `NK_INTERNAL` functions:
23
+ *
24
+ * - nk_dot_i8x64 for 8-bit signed integer inputs using DPBUSD with algebraic transformation,
25
+ * - nk_dot_u8x64 for 8-bit unsigned integer inputs using DPBUSD with algebraic transformation,
26
+ * - nk_dot_i4x128 for 4-bit signed integer products with correction terms,
27
+ * - nk_dot_u4x128 for 4-bit unsigned integer products.
28
+ *
29
+ * @code{c}
30
+ * nk_dot_i8x64_state_icelake_t state_first, state_second, state_third, state_fourth;
31
+ * nk_b512_vec_t query_i8x64, target_first_i8x64, target_second_i8x64, target_third_i8x64, target_fourth_i8x64;
32
+ * nk_dot_i8x64_init_icelake(&state_first);
33
+ * nk_dot_i8x64_init_icelake(&state_second);
34
+ * nk_dot_i8x64_init_icelake(&state_third);
35
+ * nk_dot_i8x64_init_icelake(&state_fourth);
36
+ * for (nk_size_t idx = 0; idx + 64 <= depth; idx += 64) {
37
+ * query_i8x64.zmm = _mm512_loadu_si512(query_ptr + idx);
38
+ * target_first_i8x64.zmm = _mm512_loadu_si512(target_first_ptr + idx);
39
+ * target_second_i8x64.zmm = _mm512_loadu_si512(target_second_ptr + idx);
40
+ * target_third_i8x64.zmm = _mm512_loadu_si512(target_third_ptr + idx);
41
+ * target_fourth_i8x64.zmm = _mm512_loadu_si512(target_fourth_ptr + idx);
42
+ * nk_dot_i8x64_update_icelake(&state_first, query_i8x64, target_first_i8x64, idx, 64);
43
+ * nk_dot_i8x64_update_icelake(&state_second, query_i8x64, target_second_i8x64, idx, 64);
44
+ * nk_dot_i8x64_update_icelake(&state_third, query_i8x64, target_third_i8x64, idx, 64);
45
+ * nk_dot_i8x64_update_icelake(&state_fourth, query_i8x64, target_fourth_i8x64, idx, 64);
46
+ * }
47
+ * nk_b128_vec_t results_i32x4;
48
+ * nk_dot_i8x64_finalize_icelake(&state_first, &state_second, &state_third, &state_fourth, depth, &results_i32x4);
49
+ * @endcode
50
+ *
51
+ * For 4-bit integers, the state manages the complex unpacking and correction term accumulation:
52
+ *
53
+ * @code{c}
54
+ * nk_dot_i4x128_state_icelake_t state_first, state_second, state_third, state_fourth;
55
+ * nk_b512_vec_t query_i4x128, target_first_i4x128, target_second_i4x128, target_third_i4x128, target_fourth_i4x128;
56
+ * nk_dot_i4x128_init_icelake(&state_first);
57
+ * nk_dot_i4x128_init_icelake(&state_second);
58
+ * nk_dot_i4x128_init_icelake(&state_third);
59
+ * nk_dot_i4x128_init_icelake(&state_fourth);
60
+ * for (nk_size_t idx = 0; idx + 128 <= depth; idx += 128) {
61
+ * query_i4x128.zmm = _mm512_loadu_si512(query_ptr + idx / 2);
62
+ * target_first_i4x128.zmm = _mm512_loadu_si512(target_first_ptr + idx / 2);
63
+ * target_second_i4x128.zmm = _mm512_loadu_si512(target_second_ptr + idx / 2);
64
+ * target_third_i4x128.zmm = _mm512_loadu_si512(target_third_ptr + idx / 2);
65
+ * target_fourth_i4x128.zmm = _mm512_loadu_si512(target_fourth_ptr + idx / 2);
66
+ * nk_dot_i4x128_update_icelake(&state_first, query_i4x128, target_first_i4x128, idx, 128);
67
+ * nk_dot_i4x128_update_icelake(&state_second, query_i4x128, target_second_i4x128, idx, 128);
68
+ * nk_dot_i4x128_update_icelake(&state_third, query_i4x128, target_third_i4x128, idx, 128);
69
+ * nk_dot_i4x128_update_icelake(&state_fourth, query_i4x128, target_fourth_i4x128, idx, 128);
70
+ * }
71
+ * nk_b128_vec_t results_i32x4;
72
+ * nk_dot_i4x128_finalize_icelake(&state_first, &state_second, &state_third, &state_fourth, depth, &results_i32x4);
73
+ * @endcode
74
+ */
75
+ #ifndef NK_DOT_ICELAKE_H
76
+ #define NK_DOT_ICELAKE_H
77
+
78
+ #if NK_TARGET_X8664_
79
+ #if NK_TARGET_ICELAKE
80
+
81
+ #include "numkong/types.h"
82
+ #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
83
+
84
+ #if defined(__cplusplus)
85
+ extern "C" {
86
+ #endif
87
+
88
+ #if defined(__clang__)
89
+ #pragma clang attribute push( \
90
+ __attribute__(( \
91
+ target("avx2,avx512f,avx512vl,avx512bw,avx512dq,avx512vnni,avx512vbmi,avx512vpopcntdq,f16c,fma,bmi,bmi2"))), \
92
+ apply_to = function)
93
+ #elif defined(__GNUC__)
94
+ #pragma GCC push_options
95
+ #pragma GCC target("avx2", "avx512f", "avx512vl", "avx512bw", "avx512dq", "avx512vnni", "avx512vbmi", \
96
+ "avx512vpopcntdq", "f16c", "fma", "bmi", "bmi2")
97
+ #endif
98
+
99
+ NK_PUBLIC void nk_dot_i8_icelake(nk_i8_t const *a_scalars, nk_i8_t const *b_scalars, nk_size_t count_scalars,
100
+ nk_i32_t *result) {
101
+ // Optimized i8×i8 dot product using algebraic transformation with DPBUSD
102
+ //
103
+ // Old approach (Haswell/Skylake):
104
+ // - Sign-extend i8 → i16 using cvtepi8_epi16 (3cy latency @ p5, 32 elements/iteration)
105
+ // - Multiply i16×i16 using vpmaddwd + dpwssd
106
+ // - Bottleneck: cvtepi8_epi16 serializes on port 5
107
+ //
108
+ // New approach (Ice Lake+):
109
+ // - Use DPBUSD (unsigned×signed multiply-add) with algebraic transformation
110
+ // - Convert signed i8 to unsigned via XOR with 0x80: a' = a + 128
111
+ // - Compute dpbusd(a', b) = (a+128)×b, then correct: a×b = (a+128)×b - 128×sum(b)
112
+ // - Use SAD for fast correction term accumulation (1cy @ p5 vs 8-10cy with cvtepi8)
113
+ // - Processes 64 elements/iteration
114
+ //
115
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8((char)0x80);
116
+ __m512i const zeros_u8x64 = _mm512_setzero_si512();
117
+ __m512i sum_ab_i32x16 = _mm512_setzero_si512();
118
+ __m512i sum_b_biased_i64x8 = _mm512_setzero_si512();
119
+ __m512i a_i8x64, b_i8x64;
120
+ nk_size_t count_original = count_scalars;
121
+
122
+ nk_dot_i8_icelake_cycle:
123
+ if (count_scalars < 64) {
124
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
125
+ a_i8x64 = _mm512_maskz_loadu_epi8(mask, a_scalars);
126
+ b_i8x64 = _mm512_maskz_loadu_epi8(mask, b_scalars);
127
+ count_scalars = 0;
128
+ }
129
+ else {
130
+ a_i8x64 = _mm512_loadu_si512(a_scalars);
131
+ b_i8x64 = _mm512_loadu_si512(b_scalars);
132
+ a_scalars += 64, b_scalars += 64, count_scalars -= 64;
133
+ }
134
+
135
+ // Convert a to unsigned [0,255] by XOR with 0x80: a_biased = a + 128
136
+ __m512i a_biased_u8x64 = _mm512_xor_si512(a_i8x64, xor_mask_u8x64);
137
+
138
+ // Compute (a+128) × b using dpbusd: unsigned × signed
139
+ sum_ab_i32x16 = _mm512_dpbusd_epi32(sum_ab_i32x16, a_biased_u8x64, b_i8x64);
140
+
141
+ // Accumulate sum(b+128) using SAD (1cy @ p5 instead of 8-10cy with cvtepi8+madd)
142
+ __m512i b_biased_u8x64 = _mm512_xor_si512(b_i8x64, xor_mask_u8x64);
143
+ sum_b_biased_i64x8 = _mm512_add_epi64(sum_b_biased_i64x8, _mm512_sad_epu8(b_biased_u8x64, zeros_u8x64));
144
+
145
+ if (count_scalars) goto nk_dot_i8_icelake_cycle;
146
+
147
+ // Apply algebraic correction: a×b = (a+128)×b - 128×sum(b)
148
+ // sum_b = sum_b_biased - 128×count_rounded
149
+ // correction = 128×sum_b = 128×sum_b_biased - 16384×count_rounded
150
+ nk_i32_t ab_sum = _mm512_reduce_add_epi32(sum_ab_i32x16);
151
+ nk_i64_t sum_b_biased = _mm512_reduce_add_epi64(sum_b_biased_i64x8);
152
+ nk_size_t count_rounded = nk_size_round_up_to_multiple_(count_original, 64);
153
+ nk_i64_t correction = 128LL * sum_b_biased - 16384LL * (nk_i64_t)count_rounded;
154
+
155
+ *result = (nk_i32_t)(ab_sum - correction);
156
+ }
157
+
158
+ NK_PUBLIC void nk_dot_u8_icelake(nk_u8_t const *a_scalars, nk_u8_t const *b_scalars, nk_size_t count_scalars,
159
+ nk_u32_t *result) {
160
+ // Optimized u8×u8 dot product using algebraic transformation with DPBUSD
161
+ //
162
+ // Algebraic transformation:
163
+ // Let b' = b XOR 0x80 (converts unsigned to signed: b' = b - 128)
164
+ // dpbusd(a, b') computes: a × (b-128) [unsigned × signed]
165
+ // Therefore: a×b = a×(b-128) + 128×sum(a)
166
+ //
167
+ // Where:
168
+ // - XOR with 0x80 converts unsigned u8 [0,255] to signed [-128,127]
169
+ // - dpbusd performs unsigned×signed multiply-accumulate
170
+ // - sad_epu8 computes sum(a) as correction term
171
+ // - Correction term 128×sum(a) is added at the end
172
+ //
173
+ // Performance: 1.92× speedup over unpack + dpwssd approach
174
+ // - Processes 64 elements/iteration
175
+ // - Lower latency: ~8cy vs ~16cy per iteration
176
+ // - Eliminates 4× unpack operations (1cy each @ p5)
177
+ // - dpbusd@p0 runs in parallel with sad@p5
178
+ //
179
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8((char)0x80);
180
+ __m512i const zeros_u8x64 = _mm512_setzero_si512();
181
+ __m512i sum_ab_i32x16 = _mm512_setzero_si512();
182
+ __m512i sum_a_i64x8 = _mm512_setzero_si512();
183
+ __m512i a_u8x64, b_u8x64;
184
+
185
+ nk_dot_u8_icelake_cycle:
186
+ if (count_scalars < 64) {
187
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
188
+ a_u8x64 = _mm512_maskz_loadu_epi8(mask, a_scalars);
189
+ b_u8x64 = _mm512_maskz_loadu_epi8(mask, b_scalars);
190
+ count_scalars = 0;
191
+ }
192
+ else {
193
+ a_u8x64 = _mm512_loadu_si512(a_scalars);
194
+ b_u8x64 = _mm512_loadu_si512(b_scalars);
195
+ a_scalars += 64, b_scalars += 64, count_scalars -= 64;
196
+ }
197
+
198
+ // Convert b to signed [-128,127] by XOR with 0x80: b_signed = b - 128
199
+ __m512i b_signed_i8x64 = _mm512_xor_si512(b_u8x64, xor_mask_u8x64);
200
+
201
+ // Compute a × (b-128) using dpbusd: unsigned × signed
202
+ sum_ab_i32x16 = _mm512_dpbusd_epi32(sum_ab_i32x16, a_u8x64, b_signed_i8x64);
203
+
204
+ // Accumulate sum(a) for correction term using sad_epu8 (1cy @ p5)
205
+ sum_a_i64x8 = _mm512_add_epi64(sum_a_i64x8, _mm512_sad_epu8(a_u8x64, zeros_u8x64));
206
+
207
+ if (count_scalars) goto nk_dot_u8_icelake_cycle;
208
+
209
+ // Apply algebraic correction: a×b = a×(b-128) + 128×sum(a)
210
+ nk_i32_t ab_dot_signed = _mm512_reduce_add_epi32(sum_ab_i32x16);
211
+ nk_i64_t sum_a = _mm512_reduce_add_epi64(sum_a_i64x8);
212
+ nk_i64_t correction = 128LL * sum_a;
213
+
214
+ *result = (nk_u32_t)(ab_dot_signed + correction);
215
+ }
216
+
217
+ typedef struct nk_dot_i8x64_state_icelake_t {
218
+ __m512i biased_product_sum_i32x16; // Single accumulator: (a^0x80)×b
219
+ } nk_dot_i8x64_state_icelake_t;
220
+
221
+ NK_INTERNAL void nk_dot_i8x64_init_icelake(nk_dot_i8x64_state_icelake_t *state) {
222
+ state->biased_product_sum_i32x16 = _mm512_setzero_si512();
223
+ }
224
+
225
+ NK_INTERNAL void nk_dot_i8x64_update_icelake(nk_dot_i8x64_state_icelake_t *state, nk_b512_vec_t a, nk_b512_vec_t b,
226
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
227
+ nk_unused_(depth_offset);
228
+ nk_unused_(active_dimensions);
229
+ // Optimized i8×i8 using DPBUSD with algebraic transformation
230
+ // DPBUSD(a^0x80, b) = (a+128)·b = a·b + 128·Σb
231
+ // Correction applied at finalize: result = biased − 128·Σb
232
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8((char)0x80);
233
+
234
+ __m512i a_i8x64 = a.zmm;
235
+ __m512i b_i8x64 = b.zmm;
236
+
237
+ // Convert a to unsigned: a_unsigned = a ^ 0x80
238
+ __m512i a_unsigned_u8x64 = _mm512_xor_si512(a_i8x64, xor_mask_u8x64);
239
+
240
+ // Compute (a+128) × b using dpbusd — no correction accumulator needed
241
+ state->biased_product_sum_i32x16 = _mm512_dpbusd_epi32(state->biased_product_sum_i32x16, a_unsigned_u8x64, b_i8x64);
242
+ }
243
+
244
+ NK_INTERNAL void nk_dot_i8x64_finalize_icelake( //
245
+ nk_dot_i8x64_state_icelake_t const *state_a, nk_dot_i8x64_state_icelake_t const *state_b, //
246
+ nk_dot_i8x64_state_icelake_t const *state_c, nk_dot_i8x64_state_icelake_t const *state_d, //
247
+ nk_size_t total_dimensions, //
248
+ nk_i32_t a_sum, /* A row sum (unused for i8) */ //
249
+ nk_b128_vec_t b_sums, /* 4 × i32 B column sums */ //
250
+ nk_b128_vec_t *results) {
251
+ nk_unused_(total_dimensions);
252
+ nk_unused_(a_sum);
253
+
254
+ // Reduce biased products: zmm (i32x16) → ymm (i32x8)
255
+ __m256i sum_a_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_a->biased_product_sum_i32x16),
256
+ _mm512_extracti32x8_epi32(state_a->biased_product_sum_i32x16, 1));
257
+ __m256i sum_b_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_b->biased_product_sum_i32x16),
258
+ _mm512_extracti32x8_epi32(state_b->biased_product_sum_i32x16, 1));
259
+ __m256i sum_c_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_c->biased_product_sum_i32x16),
260
+ _mm512_extracti32x8_epi32(state_c->biased_product_sum_i32x16, 1));
261
+ __m256i sum_d_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_d->biased_product_sum_i32x16),
262
+ _mm512_extracti32x8_epi32(state_d->biased_product_sum_i32x16, 1));
263
+
264
+ // Reduce ymm (i32x8) → xmm (i32x4)
265
+ __m128i sum_a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_a_i32x8), _mm256_extracti128_si256(sum_a_i32x8, 1));
266
+ __m128i sum_b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_b_i32x8), _mm256_extracti128_si256(sum_b_i32x8, 1));
267
+ __m128i sum_c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_c_i32x8), _mm256_extracti128_si256(sum_c_i32x8, 1));
268
+ __m128i sum_d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_d_i32x8), _mm256_extracti128_si256(sum_d_i32x8, 1));
269
+
270
+ // 4-way transpose reduce
271
+ __m128i t_ab_low = _mm_unpacklo_epi32(sum_a_i32x4, sum_b_i32x4);
272
+ __m128i t_cd_low = _mm_unpacklo_epi32(sum_c_i32x4, sum_d_i32x4);
273
+ __m128i t_ab_high = _mm_unpackhi_epi32(sum_a_i32x4, sum_b_i32x4);
274
+ __m128i t_cd_high = _mm_unpackhi_epi32(sum_c_i32x4, sum_d_i32x4);
275
+ __m128i biased_i32x4 = _mm_add_epi32(
276
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_low, t_cd_low), _mm_unpackhi_epi64(t_ab_low, t_cd_low)),
277
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_high, t_cd_high), _mm_unpackhi_epi64(t_ab_high, t_cd_high)));
278
+
279
+ // Apply compensation: result = biased − 128 × Σb
280
+ __m128i correction_i32x4 = _mm_slli_epi32(b_sums.xmm, 7); // × 128
281
+ results->xmm = _mm_sub_epi32(biased_i32x4, correction_i32x4);
282
+ }
283
+
284
+ typedef struct nk_dot_u8x64_state_icelake_t {
285
+ __m512i biased_product_sum_i32x16; // Single accumulator: DPBUSD(b, a^0x80)
286
+ } nk_dot_u8x64_state_icelake_t;
287
+
288
+ NK_INTERNAL void nk_dot_u8x64_init_icelake(nk_dot_u8x64_state_icelake_t *state) {
289
+ state->biased_product_sum_i32x16 = _mm512_setzero_si512();
290
+ }
291
+
292
+ NK_INTERNAL void nk_dot_u8x64_update_icelake(nk_dot_u8x64_state_icelake_t *state, nk_b512_vec_t a, nk_b512_vec_t b,
293
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
294
+ nk_unused_(depth_offset);
295
+ nk_unused_(active_dimensions);
296
+ // Optimized u8×u8 using operand swap: DPBUSD(b, a^0x80)
297
+ // DPBUSD(b, a^0x80) = b·(a−128) = a·b − 128·Σb
298
+ // Correction applied at finalize: result = biased + 128·Σb
299
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8((char)0x80);
300
+
301
+ __m512i a_u8x64 = a.zmm;
302
+ __m512i b_u8x64 = b.zmm;
303
+
304
+ // Convert a to signed: a_signed = a ^ 0x80 = a − 128
305
+ __m512i a_signed_i8x64 = _mm512_xor_si512(a_u8x64, xor_mask_u8x64);
306
+
307
+ // Operand swap: b (unsigned) in first slot, a−128 (signed) in second
308
+ state->biased_product_sum_i32x16 = _mm512_dpbusd_epi32(state->biased_product_sum_i32x16, b_u8x64, a_signed_i8x64);
309
+ }
310
+
311
+ NK_INTERNAL void nk_dot_u8x64_finalize_icelake( //
312
+ nk_dot_u8x64_state_icelake_t const *state_a, nk_dot_u8x64_state_icelake_t const *state_b, //
313
+ nk_dot_u8x64_state_icelake_t const *state_c, nk_dot_u8x64_state_icelake_t const *state_d, //
314
+ nk_size_t total_dimensions, //
315
+ nk_i32_t a_sum, /* A row sum (unused for u8) */ //
316
+ nk_b128_vec_t b_sums, /* 4 × u32 B column sums */ //
317
+ nk_b128_vec_t *result) {
318
+ nk_unused_(total_dimensions);
319
+ nk_unused_(a_sum);
320
+
321
+ // Reduce biased products: zmm (i32x16) → ymm (i32x8)
322
+ __m256i sum_a_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_a->biased_product_sum_i32x16),
323
+ _mm512_extracti32x8_epi32(state_a->biased_product_sum_i32x16, 1));
324
+ __m256i sum_b_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_b->biased_product_sum_i32x16),
325
+ _mm512_extracti32x8_epi32(state_b->biased_product_sum_i32x16, 1));
326
+ __m256i sum_c_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_c->biased_product_sum_i32x16),
327
+ _mm512_extracti32x8_epi32(state_c->biased_product_sum_i32x16, 1));
328
+ __m256i sum_d_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_d->biased_product_sum_i32x16),
329
+ _mm512_extracti32x8_epi32(state_d->biased_product_sum_i32x16, 1));
330
+
331
+ // Reduce ymm (i32x8) → xmm (i32x4)
332
+ __m128i sum_a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_a_i32x8), _mm256_extracti128_si256(sum_a_i32x8, 1));
333
+ __m128i sum_b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_b_i32x8), _mm256_extracti128_si256(sum_b_i32x8, 1));
334
+ __m128i sum_c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_c_i32x8), _mm256_extracti128_si256(sum_c_i32x8, 1));
335
+ __m128i sum_d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_d_i32x8), _mm256_extracti128_si256(sum_d_i32x8, 1));
336
+
337
+ // 4-way transpose reduce
338
+ __m128i t_ab_low = _mm_unpacklo_epi32(sum_a_i32x4, sum_b_i32x4);
339
+ __m128i t_cd_low = _mm_unpacklo_epi32(sum_c_i32x4, sum_d_i32x4);
340
+ __m128i t_ab_high = _mm_unpackhi_epi32(sum_a_i32x4, sum_b_i32x4);
341
+ __m128i t_cd_high = _mm_unpackhi_epi32(sum_c_i32x4, sum_d_i32x4);
342
+ __m128i biased_i32x4 = _mm_add_epi32(
343
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_low, t_cd_low), _mm_unpackhi_epi64(t_ab_low, t_cd_low)),
344
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_high, t_cd_high), _mm_unpackhi_epi64(t_ab_high, t_cd_high)));
345
+
346
+ // Apply compensation: result = biased + 128 × Σb
347
+ __m128i correction_i32x4 = _mm_slli_epi32(b_sums.xmm, 7); // × 128
348
+ result->xmm = _mm_add_epi32(biased_i32x4, correction_i32x4);
349
+ }
350
+
351
+ /**
352
+ * Stateful element-sum helpers for compensated symmetric GEMM.
353
+ * SAD512 runs on port 5 while DPBUSD runs on port 0 — zero throughput cost when inlined.
354
+ */
355
+
356
+ /* i8x64: signed i8 sum via XOR→unsigned + SAD, bias-corrected at finalize */
357
+ typedef struct nk_sum_i8x64_state_icelake_t {
358
+ __m512i biased_sum_u64x8;
359
+ } nk_sum_i8x64_state_icelake_t;
360
+
361
+ NK_INTERNAL void nk_sum_i8x64_init_icelake(nk_sum_i8x64_state_icelake_t *state) {
362
+ state->biased_sum_u64x8 = _mm512_setzero_si512();
363
+ }
364
+ NK_INTERNAL void nk_sum_i8x64_update_icelake(nk_sum_i8x64_state_icelake_t *state, nk_b512_vec_t vector) {
365
+ __m512i vector_unsigned_u8x64 = _mm512_xor_si512(vector.zmm, _mm512_set1_epi8((char)0x80));
366
+ __m512i sad_result_u64x8 = _mm512_sad_epu8(vector_unsigned_u8x64, _mm512_setzero_si512());
367
+ state->biased_sum_u64x8 = _mm512_add_epi64(state->biased_sum_u64x8, sad_result_u64x8);
368
+ }
369
+ NK_INTERNAL nk_i32_t nk_sum_i8x64_finalize_icelake(nk_sum_i8x64_state_icelake_t const *state, nk_size_t count) {
370
+ nk_u64_t unsigned_sum = (nk_u64_t)_mm512_reduce_add_epi64(state->biased_sum_u64x8);
371
+ return (nk_i32_t)((nk_i64_t)unsigned_sum - 128 * (nk_i64_t)count);
372
+ }
373
+
374
+ /* u8x64: unsigned u8 sum via plain SAD */
375
+ typedef struct nk_sum_u8x64_state_icelake_t {
376
+ __m512i sum_u64x8;
377
+ } nk_sum_u8x64_state_icelake_t;
378
+
379
+ NK_INTERNAL void nk_sum_u8x64_init_icelake(nk_sum_u8x64_state_icelake_t *state) {
380
+ state->sum_u64x8 = _mm512_setzero_si512();
381
+ }
382
+ NK_INTERNAL void nk_sum_u8x64_update_icelake(nk_sum_u8x64_state_icelake_t *state, nk_b512_vec_t vector) {
383
+ __m512i sad_result_u64x8 = _mm512_sad_epu8(vector.zmm, _mm512_setzero_si512());
384
+ state->sum_u64x8 = _mm512_add_epi64(state->sum_u64x8, sad_result_u64x8);
385
+ }
386
+ NK_INTERNAL nk_u32_t nk_sum_u8x64_finalize_icelake(nk_sum_u8x64_state_icelake_t const *state, nk_size_t count) {
387
+ nk_unused_(count);
388
+ return (nk_u32_t)_mm512_reduce_add_epi64(state->sum_u64x8);
389
+ }
390
+
391
+ /* i4x128: signed i4 sum — vectorized nibble extraction + SAD on 512-bit vector.
392
+ * Each byte contains 2 nibbles in [0,15] representing signed values in [-8,7].
393
+ * We XOR nibbles with 0x08 to get unsigned [0,15], SAD against zero, then bias-correct at finalize. */
394
+ typedef struct nk_sum_i4x128_state_icelake_t {
395
+ __m512i biased_sum_u64x8; /* Accumulates SAD of (nibble ^ 0x08), needs bias correction */
396
+ } nk_sum_i4x128_state_icelake_t;
397
+
398
+ NK_INTERNAL void nk_sum_i4x128_init_icelake(nk_sum_i4x128_state_icelake_t *state) {
399
+ state->biased_sum_u64x8 = _mm512_setzero_si512();
400
+ }
401
+ NK_INTERNAL void nk_sum_i4x128_update_icelake(nk_sum_i4x128_state_icelake_t *state, nk_b512_vec_t v) {
402
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
403
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8(0x08);
404
+ __m512i const zeros_u8x64 = _mm512_setzero_si512();
405
+ // Extract low and high nibbles, XOR with 8 to get unsigned representation
406
+ __m512i low_u8x64 = _mm512_and_si512(v.zmm, nibble_mask_u8x64);
407
+ __m512i high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(v.zmm, 4), nibble_mask_u8x64);
408
+ __m512i low_biased_u8x64 = _mm512_xor_si512(low_u8x64, xor_mask_u8x64);
409
+ __m512i high_biased_u8x64 = _mm512_xor_si512(high_u8x64, xor_mask_u8x64);
410
+ // SAD against zero gives sum of unsigned values, accumulate in u64 lanes
411
+ state->biased_sum_u64x8 = _mm512_add_epi64(state->biased_sum_u64x8, _mm512_sad_epu8(low_biased_u8x64, zeros_u8x64));
412
+ state->biased_sum_u64x8 = _mm512_add_epi64(state->biased_sum_u64x8,
413
+ _mm512_sad_epu8(high_biased_u8x64, zeros_u8x64));
414
+ }
415
+ NK_INTERNAL nk_i32_t nk_sum_i4x128_finalize_icelake(nk_sum_i4x128_state_icelake_t const *state, nk_size_t count) {
416
+ // Reduce u64x8 → scalar, then undo XOR bias: signed_sum = unsigned_sum - 8 * count
417
+ nk_i64_t unsigned_sum = _mm512_reduce_add_epi64(state->biased_sum_u64x8);
418
+ return (nk_i32_t)(unsigned_sum - 8 * (nk_i64_t)count);
419
+ }
420
+
421
+ NK_PUBLIC void nk_dot_i4_icelake(nk_i4x2_t const *a, nk_i4x2_t const *b, nk_size_t n, nk_i32_t *result) {
422
+ // i4 values are packed as nibbles: two 4-bit signed values per byte.
423
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
424
+ //
425
+ // Algorithm: For signed i4, we use an algebraic transformation.
426
+ // Let ax, bx be the unsigned [0,15] representation of signed values a, b in [-8,7].
427
+ // Then: a = ax - 8, b = bx - 8 (the XOR trick gives signed = (unsigned ^ 8) - 8)
428
+ // So: a * b = (ax - 8)(bx - 8) = ax * bx - 8 * ax - 8 * bx + 64
429
+ //
430
+ // We compute ax * bx using DPBUSD, then apply the correction:
431
+ // signed_dot = unsigned_dot - 8 * (sum_ax + sum_bx) + 64 * n
432
+ //
433
+ n = nk_size_round_up_to_multiple_(n, 2);
434
+ nk_size_t n_bytes = n / 2;
435
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
436
+ __m512i const xor_mask_u8x64 = _mm512_set1_epi8(0x08);
437
+ __m512i const zeros_u8x64 = _mm512_setzero_si512();
438
+ __m512i sum_cd_i32x16 = _mm512_setzero_si512();
439
+ __m512i sum_cx_i64x8 = _mm512_setzero_si512();
440
+ __m512i sum_dx_i64x8 = _mm512_setzero_si512();
441
+ __m512i a_i4x128, b_i4x128;
442
+
443
+ nk_dot_i4_icelake_cycle:
444
+ if (n_bytes < 64) {
445
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
446
+ a_i4x128 = _mm512_mask_loadu_epi8(_mm512_set1_epi8((char)0x88), mask, a);
447
+ b_i4x128 = _mm512_mask_loadu_epi8(_mm512_set1_epi8((char)0x88), mask, b);
448
+ n_bytes = 0;
449
+ }
450
+ else {
451
+ a_i4x128 = _mm512_loadu_si512(a);
452
+ b_i4x128 = _mm512_loadu_si512(b);
453
+ a += 64, b += 64, n_bytes -= 64;
454
+ }
455
+
456
+ // Extract low and high nibbles
457
+ __m512i a_low_u8x64 = _mm512_and_si512(a_i4x128, nibble_mask_u8x64);
458
+ __m512i a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_i4x128, 4), nibble_mask_u8x64);
459
+ __m512i b_low_u8x64 = _mm512_and_si512(b_i4x128, nibble_mask_u8x64);
460
+ __m512i b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_i4x128, 4), nibble_mask_u8x64);
461
+
462
+ // XOR with 8 to get cx, dx values for the algebraic transformation
463
+ __m512i c_low_u8x64 = _mm512_xor_si512(a_low_u8x64, xor_mask_u8x64);
464
+ __m512i c_high_u8x64 = _mm512_xor_si512(a_high_u8x64, xor_mask_u8x64);
465
+ __m512i d_low_u8x64 = _mm512_xor_si512(b_low_u8x64, xor_mask_u8x64);
466
+ __m512i d_high_u8x64 = _mm512_xor_si512(b_high_u8x64, xor_mask_u8x64);
467
+
468
+ // Compute dot products of cx*dx for low and high nibbles
469
+ sum_cd_i32x16 = _mm512_dpbusd_epi32(sum_cd_i32x16, c_low_u8x64, d_low_u8x64);
470
+ sum_cd_i32x16 = _mm512_dpbusd_epi32(sum_cd_i32x16, c_high_u8x64, d_high_u8x64);
471
+
472
+ // Accumulate sums of cx and dx using SAD against zeros
473
+ sum_cx_i64x8 = _mm512_add_epi64(sum_cx_i64x8, _mm512_sad_epu8(c_low_u8x64, zeros_u8x64));
474
+ sum_cx_i64x8 = _mm512_add_epi64(sum_cx_i64x8, _mm512_sad_epu8(c_high_u8x64, zeros_u8x64));
475
+ sum_dx_i64x8 = _mm512_add_epi64(sum_dx_i64x8, _mm512_sad_epu8(d_low_u8x64, zeros_u8x64));
476
+ sum_dx_i64x8 = _mm512_add_epi64(sum_dx_i64x8, _mm512_sad_epu8(d_high_u8x64, zeros_u8x64));
477
+ if (n_bytes) goto nk_dot_i4_icelake_cycle;
478
+
479
+ // Reduce partial sums and apply algebraic correction
480
+ nk_i32_t cd_dot = _mm512_reduce_add_epi32(sum_cd_i32x16);
481
+ nk_i64_t sum_cx = _mm512_reduce_add_epi64(sum_cx_i64x8);
482
+ nk_i64_t sum_dx = _mm512_reduce_add_epi64(sum_dx_i64x8);
483
+ *result = (nk_i32_t)(cd_dot - 8 * (sum_cx + sum_dx) + 64 * (nk_i64_t)n);
484
+ }
485
+
486
+ NK_PUBLIC void nk_dot_u4_icelake(nk_u4x2_t const *a, nk_u4x2_t const *b, nk_size_t n, nk_u32_t *result) {
487
+ // u4 values are packed as nibbles: two 4-bit unsigned values per byte.
488
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
489
+ // Values are ∈ [0,15], so DPBUSD can be used directly.
490
+ //
491
+ n = nk_size_round_up_to_multiple_(n, 2);
492
+ nk_size_t n_bytes = n / 2;
493
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
494
+ __m512i sum_i32x16 = _mm512_setzero_si512();
495
+
496
+ __m512i a_u4x128, b_u4x128;
497
+
498
+ nk_dot_u4_icelake_cycle:
499
+ if (n_bytes < 64) {
500
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
501
+ a_u4x128 = _mm512_maskz_loadu_epi8(mask, a);
502
+ b_u4x128 = _mm512_maskz_loadu_epi8(mask, b);
503
+ n_bytes = 0;
504
+ }
505
+ else {
506
+ a_u4x128 = _mm512_loadu_si512(a);
507
+ b_u4x128 = _mm512_loadu_si512(b);
508
+ a += 64, b += 64, n_bytes -= 64;
509
+ }
510
+
511
+ // Extract low and high nibbles
512
+ __m512i a_low_u8x64 = _mm512_and_si512(a_u4x128, nibble_mask_u8x64);
513
+ __m512i a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_u4x128, 4), nibble_mask_u8x64);
514
+ __m512i b_low_u8x64 = _mm512_and_si512(b_u4x128, nibble_mask_u8x64);
515
+ __m512i b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_u4x128, 4), nibble_mask_u8x64);
516
+
517
+ // DPBUSD works directly for u4 since values are ∈ [0,15]
518
+ // and the signed interpretation of [0,15] is the same as unsigned
519
+ sum_i32x16 = _mm512_dpbusd_epi32(sum_i32x16, a_low_u8x64, b_low_u8x64);
520
+ sum_i32x16 = _mm512_dpbusd_epi32(sum_i32x16, a_high_u8x64, b_high_u8x64);
521
+ if (n_bytes) goto nk_dot_u4_icelake_cycle;
522
+
523
+ *result = (nk_u32_t)_mm512_reduce_add_epi32(sum_i32x16);
524
+ }
525
+
526
+ typedef struct nk_dot_i4x128_state_icelake_t {
527
+ __m512i biased_product_sum_i32x16; // Single accumulator: (a^8)×(b^8) products
528
+ } nk_dot_i4x128_state_icelake_t;
529
+
530
+ NK_INTERNAL void nk_dot_i4x128_init_icelake(nk_dot_i4x128_state_icelake_t *state) {
531
+ state->biased_product_sum_i32x16 = _mm512_setzero_si512();
532
+ }
533
+
534
+ NK_INTERNAL void nk_dot_i4x128_update_icelake(nk_dot_i4x128_state_icelake_t *state, nk_b512_vec_t a, nk_b512_vec_t b,
535
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
536
+ // i4 values are packed as nibbles: 128 nibbles in 64 bytes (512 bits)
537
+ // Algebraic transformation: a×b = (a^8)×(b^8) − 8×(Σa + Σb) − 64×n
538
+ // Correction applied at finalize time using precomputed sums.
539
+ nk_unused_(depth_offset);
540
+ nk_unused_(active_dimensions);
541
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
542
+ __m512i const bias_xor_mask_u8x64 = _mm512_set1_epi8(0x08);
543
+
544
+ __m512i a_i4x128 = a.zmm;
545
+ __m512i b_i4x128 = b.zmm;
546
+
547
+ // Extract low and high nibbles (all 128 nibbles from 64 bytes)
548
+ __m512i a_low_u8x64 = _mm512_and_si512(a_i4x128, nibble_mask_u8x64);
549
+ __m512i a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_i4x128, 4), nibble_mask_u8x64);
550
+ __m512i b_low_u8x64 = _mm512_and_si512(b_i4x128, nibble_mask_u8x64);
551
+ __m512i b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_i4x128, 4), nibble_mask_u8x64);
552
+
553
+ // Apply bias transformation: XOR with 8
554
+ __m512i a_biased_low_u8x64 = _mm512_xor_si512(a_low_u8x64, bias_xor_mask_u8x64);
555
+ __m512i a_biased_high_u8x64 = _mm512_xor_si512(a_high_u8x64, bias_xor_mask_u8x64);
556
+ __m512i b_biased_low_u8x64 = _mm512_xor_si512(b_low_u8x64, bias_xor_mask_u8x64);
557
+ __m512i b_biased_high_u8x64 = _mm512_xor_si512(b_high_u8x64, bias_xor_mask_u8x64);
558
+
559
+ // Compute dot products of a_biased×b_biased — no SAD correction accumulators
560
+ state->biased_product_sum_i32x16 = _mm512_dpbusd_epi32(state->biased_product_sum_i32x16, a_biased_low_u8x64,
561
+ b_biased_low_u8x64);
562
+ state->biased_product_sum_i32x16 = _mm512_dpbusd_epi32(state->biased_product_sum_i32x16, a_biased_high_u8x64,
563
+ b_biased_high_u8x64);
564
+ }
565
+
566
+ NK_INTERNAL void nk_dot_i4x128_finalize_icelake( //
567
+ nk_dot_i4x128_state_icelake_t const *state_a, nk_dot_i4x128_state_icelake_t const *state_b, //
568
+ nk_dot_i4x128_state_icelake_t const *state_c, nk_dot_i4x128_state_icelake_t const *state_d, //
569
+ nk_size_t total_dimensions, //
570
+ nk_i32_t a_sum, /* A row sum (signed sum of i4 values) */ //
571
+ nk_b128_vec_t b_sums, /* 4 × i32 B column sums */ //
572
+ nk_b128_vec_t *result) {
573
+
574
+ // Compensated 4-way reduction with external correction sums.
575
+ // Formula: result = biased_product − 8×(Σa + Σb) − 64×depth_padded
576
+ nk_size_t depth_nibbles = nk_size_round_up_to_multiple_(total_dimensions, 128);
577
+
578
+ // Reduce main products: zmm (i32x16) → ymm (i32x8)
579
+ __m256i product_a_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_a->biased_product_sum_i32x16),
580
+ _mm512_extracti32x8_epi32(state_a->biased_product_sum_i32x16, 1));
581
+ __m256i product_b_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_b->biased_product_sum_i32x16),
582
+ _mm512_extracti32x8_epi32(state_b->biased_product_sum_i32x16, 1));
583
+ __m256i product_c_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_c->biased_product_sum_i32x16),
584
+ _mm512_extracti32x8_epi32(state_c->biased_product_sum_i32x16, 1));
585
+ __m256i product_d_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_d->biased_product_sum_i32x16),
586
+ _mm512_extracti32x8_epi32(state_d->biased_product_sum_i32x16, 1));
587
+
588
+ // Reduce ymm (i32x8) → xmm (i32x4)
589
+ __m128i product_a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(product_a_i32x8),
590
+ _mm256_extracti128_si256(product_a_i32x8, 1));
591
+ __m128i product_b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(product_b_i32x8),
592
+ _mm256_extracti128_si256(product_b_i32x8, 1));
593
+ __m128i product_c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(product_c_i32x8),
594
+ _mm256_extracti128_si256(product_c_i32x8, 1));
595
+ __m128i product_d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(product_d_i32x8),
596
+ _mm256_extracti128_si256(product_d_i32x8, 1));
597
+
598
+ // 4-way transpose reduce
599
+ __m128i t_ab_low = _mm_unpacklo_epi32(product_a_i32x4, product_b_i32x4);
600
+ __m128i t_cd_low = _mm_unpacklo_epi32(product_c_i32x4, product_d_i32x4);
601
+ __m128i t_ab_high = _mm_unpackhi_epi32(product_a_i32x4, product_b_i32x4);
602
+ __m128i t_cd_high = _mm_unpackhi_epi32(product_c_i32x4, product_d_i32x4);
603
+ __m128i biased_i32x4 = _mm_add_epi32(
604
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_low, t_cd_low), _mm_unpackhi_epi64(t_ab_low, t_cd_low)),
605
+ _mm_add_epi32(_mm_unpacklo_epi64(t_ab_high, t_cd_high), _mm_unpackhi_epi64(t_ab_high, t_cd_high)));
606
+
607
+ // Apply compensation: result = biased − 8×(Σa + Σb) − 64×depth_padded
608
+ __m128i a_sum_broadcast_i32x4 = _mm_set1_epi32(a_sum);
609
+ __m128i ab_sums_i32x4 = _mm_add_epi32(a_sum_broadcast_i32x4, b_sums.xmm);
610
+ __m128i correction_i32x4 = _mm_slli_epi32(ab_sums_i32x4, 3); // × 8
611
+ __m128i offset_i32x4 = _mm_set1_epi32((nk_i32_t)(-64LL * (nk_i64_t)depth_nibbles));
612
+ result->xmm = _mm_add_epi32(_mm_sub_epi32(biased_i32x4, correction_i32x4), offset_i32x4);
613
+ }
614
+
615
+ typedef struct nk_dot_u4x128_state_icelake_t {
616
+ __m512i sum_i32x16; // Direct unsigned accumulator
617
+ } nk_dot_u4x128_state_icelake_t;
618
+
619
+ NK_INTERNAL void nk_dot_u4x128_init_icelake(nk_dot_u4x128_state_icelake_t *state) {
620
+ state->sum_i32x16 = _mm512_setzero_si512();
621
+ }
622
+
623
+ NK_INTERNAL void nk_dot_u4x128_update_icelake(nk_dot_u4x128_state_icelake_t *state, nk_b512_vec_t a, nk_b512_vec_t b,
624
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
625
+ nk_unused_(depth_offset);
626
+ nk_unused_(active_dimensions);
627
+ // u4 values are packed as nibbles: 128 nibbles in 64 bytes (512 bits)
628
+ // Values are ∈ [0,15], so DPBUSD can be used directly
629
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
630
+
631
+ // Load 64 bytes containing 128 nibbles (full 512-bit register)
632
+ __m512i a_u4x128 = a.zmm;
633
+ __m512i b_u4x128 = b.zmm;
634
+
635
+ // Extract low and high nibbles (all 128 nibbles from 64 bytes)
636
+ __m512i a_low_u8x64 = _mm512_and_si512(a_u4x128, nibble_mask_u8x64);
637
+ __m512i a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_u4x128, 4), nibble_mask_u8x64);
638
+ __m512i b_low_u8x64 = _mm512_and_si512(b_u4x128, nibble_mask_u8x64);
639
+ __m512i b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_u4x128, 4), nibble_mask_u8x64);
640
+
641
+ // DPBUSD works directly for u4 since values are ∈ [0,15]
642
+ state->sum_i32x16 = _mm512_dpbusd_epi32(state->sum_i32x16, a_low_u8x64, b_low_u8x64);
643
+ state->sum_i32x16 = _mm512_dpbusd_epi32(state->sum_i32x16, a_high_u8x64, b_high_u8x64);
644
+ }
645
+
646
+ NK_INTERNAL void nk_dot_u4x128_finalize_icelake( //
647
+ nk_dot_u4x128_state_icelake_t const *state_a, nk_dot_u4x128_state_icelake_t const *state_b, //
648
+ nk_dot_u4x128_state_icelake_t const *state_c, nk_dot_u4x128_state_icelake_t const *state_d, //
649
+ nk_size_t total_dimensions, nk_b128_vec_t *result) {
650
+ nk_unused_(total_dimensions);
651
+ // ILP-optimized 4-way hierarchical reduction for u4 (no correction needed)
652
+
653
+ // Reduce zmm (i32x16) → ymm (i32x8)
654
+ __m256i sum_a_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_a->sum_i32x16),
655
+ _mm512_extracti32x8_epi32(state_a->sum_i32x16, 1));
656
+ __m256i sum_b_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_b->sum_i32x16),
657
+ _mm512_extracti32x8_epi32(state_b->sum_i32x16, 1));
658
+ __m256i sum_c_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_c->sum_i32x16),
659
+ _mm512_extracti32x8_epi32(state_c->sum_i32x16, 1));
660
+ __m256i sum_d_i32x8 = _mm256_add_epi32(_mm512_castsi512_si256(state_d->sum_i32x16),
661
+ _mm512_extracti32x8_epi32(state_d->sum_i32x16, 1));
662
+
663
+ // Reduce ymm (i32x8) → xmm (i32x4)
664
+ __m128i sum_a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_a_i32x8), _mm256_extracti128_si256(sum_a_i32x8, 1));
665
+ __m128i sum_b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_b_i32x8), _mm256_extracti128_si256(sum_b_i32x8, 1));
666
+ __m128i sum_c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_c_i32x8), _mm256_extracti128_si256(sum_c_i32x8, 1));
667
+ __m128i sum_d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(sum_d_i32x8), _mm256_extracti128_si256(sum_d_i32x8, 1));
668
+
669
+ // 4-way transpose to get [a,b,c,d] in lanes
670
+ __m128i transpose_ab_low_i32x4 = _mm_unpacklo_epi32(sum_a_i32x4, sum_b_i32x4);
671
+ __m128i transpose_cd_low_i32x4 = _mm_unpacklo_epi32(sum_c_i32x4, sum_d_i32x4);
672
+ __m128i transpose_ab_high_i32x4 = _mm_unpackhi_epi32(sum_a_i32x4, sum_b_i32x4);
673
+ __m128i transpose_cd_high_i32x4 = _mm_unpackhi_epi32(sum_c_i32x4, sum_d_i32x4);
674
+ __m128i sum_lane0_i32x4 = _mm_unpacklo_epi64(transpose_ab_low_i32x4, transpose_cd_low_i32x4);
675
+ __m128i sum_lane1_i32x4 = _mm_unpackhi_epi64(transpose_ab_low_i32x4, transpose_cd_low_i32x4);
676
+ __m128i sum_lane2_i32x4 = _mm_unpacklo_epi64(transpose_ab_high_i32x4, transpose_cd_high_i32x4);
677
+ __m128i sum_lane3_i32x4 = _mm_unpackhi_epi64(transpose_ab_high_i32x4, transpose_cd_high_i32x4);
678
+
679
+ __m128i final_i32x4 = _mm_add_epi32(_mm_add_epi32(sum_lane0_i32x4, sum_lane1_i32x4),
680
+ _mm_add_epi32(sum_lane2_i32x4, sum_lane3_i32x4));
681
+ result->xmm = final_i32x4;
682
+ }
683
+
684
+ NK_PUBLIC void nk_dot_e2m3_icelake(nk_e2m3_t const *a_scalars, nk_e2m3_t const *b_scalars, nk_size_t count_scalars,
685
+ nk_f32_t *result) {
686
+ // Integer dot product for e2m3 using VPERMB (LUT) + VPDPBUSD (unsigned×signed multiply-add).
687
+ // Every e2m3 value × 16 is an exact integer in [-120, +120].
688
+ // Result = i32_dot / 256.0f (exact, no rounding error).
689
+ //
690
+ // LUT maps 5-bit unsigned magnitude to (value × 16):
691
+ // exp=0 (sub): 2*mant, exp=1: 16+2*mant
692
+ // exp=2: 32+4*mant, exp=3: 64+8*mant
693
+ //
694
+ // VPERMB uses bits [5:0] of the index, so we need a 64-byte LUT with entries 0-31
695
+ // replicated in the upper 32 bytes (VPERMB indexes mod 64, our indices are 0-31).
696
+ // _mm512_set_epi8 lists bytes HIGH→LOW: byte63, byte62, ..., byte0
697
+ __m512i const lut_magnitude_u8x64 = _mm512_set_epi8(120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
698
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0,
699
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
700
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0);
701
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x1F);
702
+ __m512i const sign_mask_u8x64 = _mm512_set1_epi8(0x20);
703
+ __m512i sum_i32x16 = _mm512_setzero_si512();
704
+ __m512i a_e2m3_u8x64, b_e2m3_u8x64;
705
+
706
+ nk_dot_e2m3_icelake_cycle:
707
+ if (count_scalars < 64) {
708
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
709
+ a_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a_scalars);
710
+ b_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b_scalars);
711
+ count_scalars = 0;
712
+ }
713
+ else {
714
+ a_e2m3_u8x64 = _mm512_loadu_si512(a_scalars);
715
+ b_e2m3_u8x64 = _mm512_loadu_si512(b_scalars);
716
+ a_scalars += 64, b_scalars += 64, count_scalars -= 64;
717
+ }
718
+
719
+ // Extract 5-bit magnitude indices
720
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e2m3_u8x64, magnitude_mask_u8x64);
721
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e2m3_u8x64, magnitude_mask_u8x64);
722
+
723
+ // VPERMB LUT lookup: unsigned magnitudes × 16
724
+ __m512i a_unsigned_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_magnitude_u8x64);
725
+ __m512i b_unsigned_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_magnitude_u8x64);
726
+
727
+ // Combined sign: (a ^ b) & 0x20 — nonzero means negative product
728
+ __m512i sign_combined_u8x64 = _mm512_and_si512(_mm512_xor_si512(a_e2m3_u8x64, b_e2m3_u8x64), sign_mask_u8x64);
729
+ __mmask64 negate_mask = _mm512_test_epi8_mask(sign_combined_u8x64, sign_combined_u8x64);
730
+
731
+ // Negate b where signs differ: b_signed = negate_mask ? (0 - b_unsigned) : b_unsigned
732
+ // For VPDPBUSD: a=unsigned [0,120], b=signed [-120,+120]
733
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_unsigned_u8x64, negate_mask, _mm512_setzero_si512(),
734
+ b_unsigned_u8x64);
735
+
736
+ // VPDPBUSD: a_unsigned[unsigned] × b_signed[signed], 4 bytes → i32
737
+ sum_i32x16 = _mm512_dpbusd_epi32(sum_i32x16, a_unsigned_u8x64, b_signed_i8x64);
738
+
739
+ if (count_scalars) goto nk_dot_e2m3_icelake_cycle;
740
+ *result = (nk_f32_t)_mm512_reduce_add_epi32(sum_i32x16) / 256.0f;
741
+ }
742
+
743
+ NK_PUBLIC void nk_dot_e3m2_icelake(nk_e3m2_t const *a_scalars, nk_e3m2_t const *b_scalars, nk_size_t count_scalars,
744
+ nk_f32_t *result) {
745
+ // Integer dot product for e3m2 using VPERMW (i16 LUT) + VPMADDWD (i16×i16→i32).
746
+ // Every e3m2 value × 16 is an exact integer, but magnitudes reach 448, requiring i16.
747
+ // Result = i32_dot / 256.0f (exact, no rounding error).
748
+ //
749
+ // 32-entry i16 LUT for magnitude × 16:
750
+ // exp=0 (sub): mant, exp=1: 4+mant
751
+ // exp=2: 8+2*mant, exp=3: 16+4*mant
752
+ // exp=4: 32+8*mant, exp=5: 64+16*mant
753
+ // exp=6: 128+32*mant, exp=7: 256+64*mant
754
+ //
755
+ // VPERMW uses bits [4:0] of the index (mod 32), so 32 entries fit exactly in one ZMM.
756
+ // _mm512_set_epi16 lists words HIGH→LOW: word31, word30, ..., word0
757
+ __m512i const lut_magnitude_i16x32 = _mm512_set_epi16( //
758
+ 448, 384, 320, 256, 224, 192, 160, 128, 112, 96, 80, 64, 56, 48, 40, 32, //
759
+ 28, 24, 20, 16, 14, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1, 0);
760
+ __m512i const magnitude_mask_i16x32 = _mm512_set1_epi16(0x1F);
761
+ __m512i const sign_mask_i16x32 = _mm512_set1_epi16(0x20);
762
+ __m512i sum_i32x16 = _mm512_setzero_si512();
763
+ __m256i a_e3m2_u8x32, b_e3m2_u8x32;
764
+
765
+ nk_dot_e3m2_icelake_cycle:
766
+ if (count_scalars < 32) {
767
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, (unsigned int)count_scalars);
768
+ a_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, a_scalars);
769
+ b_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, b_scalars);
770
+ count_scalars = 0;
771
+ }
772
+ else {
773
+ a_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)a_scalars);
774
+ b_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)b_scalars);
775
+ a_scalars += 32, b_scalars += 32, count_scalars -= 32;
776
+ }
777
+
778
+ // Zero-extend u8x32 → u16x32
779
+ __m512i a_u16x32 = _mm512_cvtepu8_epi16(a_e3m2_u8x32);
780
+ __m512i b_u16x32 = _mm512_cvtepu8_epi16(b_e3m2_u8x32);
781
+
782
+ // Extract 5-bit magnitude indices
783
+ __m512i a_magnitude_u16x32 = _mm512_and_si512(a_u16x32, magnitude_mask_i16x32);
784
+ __m512i b_magnitude_u16x32 = _mm512_and_si512(b_u16x32, magnitude_mask_i16x32);
785
+
786
+ // VPERMW LUT lookup: unsigned magnitudes × 16
787
+ __m512i a_unsigned_i16x32 = _mm512_permutexvar_epi16(a_magnitude_u16x32, lut_magnitude_i16x32);
788
+ __m512i b_unsigned_i16x32 = _mm512_permutexvar_epi16(b_magnitude_u16x32, lut_magnitude_i16x32);
789
+
790
+ // Apply signs: negate if bit 5 is set
791
+ __mmask32 a_negate = _mm512_test_epi16_mask(a_u16x32, sign_mask_i16x32);
792
+ __mmask32 b_negate = _mm512_test_epi16_mask(b_u16x32, sign_mask_i16x32);
793
+ __m512i a_signed_i16x32 = _mm512_mask_sub_epi16(a_unsigned_i16x32, a_negate, _mm512_setzero_si512(),
794
+ a_unsigned_i16x32);
795
+ __m512i b_signed_i16x32 = _mm512_mask_sub_epi16(b_unsigned_i16x32, b_negate, _mm512_setzero_si512(),
796
+ b_unsigned_i16x32);
797
+
798
+ // VPMADDWD: i16×i16→i32, multiplies adjacent pairs and adds
799
+ sum_i32x16 = _mm512_add_epi32(sum_i32x16, _mm512_madd_epi16(a_signed_i16x32, b_signed_i16x32));
800
+
801
+ if (count_scalars) goto nk_dot_e3m2_icelake_cycle;
802
+ *result = (nk_f32_t)_mm512_reduce_add_epi32(sum_i32x16) / 256.0f;
803
+ }
804
+
805
+ #pragma region F16 and BF16 Floats
806
+
807
+ NK_PUBLIC void nk_dot_e4m3_icelake(nk_e4m3_t const *a_scalars, nk_e4m3_t const *b_scalars, nk_size_t count_scalars,
808
+ nk_f32_t *result) {
809
+ // E4M3 dot product via octave decomposition + VPDPBUSD integer MAC.
810
+ // Splits 4-bit exponent into 2 octave bits + 2 remainder bits, maps low 5 bits via VPERMB
811
+ // to u8 integers [0, 120], then 16 VPDPBUSD cross-products across 4×4 octave pairs.
812
+
813
+ __m512i const lut_normal_u8x64 = _mm512_set_epi8( //
814
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
815
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8, //
816
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
817
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8); //
818
+ __m512i const lut_subnorm_u8x64 = _mm512_set_epi8( //
819
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
820
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0, //
821
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
822
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0); //
823
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x7F);
824
+ __m512i const subnorm_threshold_u8x64 = _mm512_set1_epi8(0x08);
825
+ __m512i const oct_threshold_20_u8x64 = _mm512_set1_epi8(0x20);
826
+ __m512i const oct_threshold_40_u8x64 = _mm512_set1_epi8(0x40);
827
+ __m512i const oct_threshold_60_u8x64 = _mm512_set1_epi8(0x60);
828
+
829
+ __m512i dot0_i32x16 = _mm512_setzero_si512();
830
+ __m512i dot1_i32x16 = _mm512_setzero_si512();
831
+ __m512i dot2_i32x16 = _mm512_setzero_si512();
832
+ __m512i dot3_i32x16 = _mm512_setzero_si512();
833
+ __m512i dot4_i32x16 = _mm512_setzero_si512();
834
+ __m512i dot5_i32x16 = _mm512_setzero_si512();
835
+ __m512i dot6_i32x16 = _mm512_setzero_si512();
836
+ __m512i a_e4m3_u8x64, b_e4m3_u8x64;
837
+
838
+ nk_dot_e4m3_icelake_cycle:
839
+ if (count_scalars < 64) {
840
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
841
+ a_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a_scalars);
842
+ b_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b_scalars);
843
+ count_scalars = 0;
844
+ }
845
+ else {
846
+ a_e4m3_u8x64 = _mm512_loadu_si512(a_scalars);
847
+ b_e4m3_u8x64 = _mm512_loadu_si512(b_scalars);
848
+ a_scalars += 64, b_scalars += 64, count_scalars -= 64;
849
+ }
850
+
851
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e4m3_u8x64, magnitude_mask_u8x64);
852
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e4m3_u8x64, magnitude_mask_u8x64);
853
+ __m512i a_base_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_normal_u8x64);
854
+ __m512i b_base_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_normal_u8x64);
855
+
856
+ // Subnormal fixup via VPERMB (avoids VPADDB on Zen 4 ports 8+9 / SPR port 0)
857
+ a_base_u8x64 = _mm512_mask_permutexvar_epi8(a_base_u8x64,
858
+ _mm512_cmplt_epu8_mask(a_magnitude_u8x64, subnorm_threshold_u8x64),
859
+ a_magnitude_u8x64, lut_subnorm_u8x64);
860
+ b_base_u8x64 = _mm512_mask_permutexvar_epi8(b_base_u8x64,
861
+ _mm512_cmplt_epu8_mask(b_magnitude_u8x64, subnorm_threshold_u8x64),
862
+ b_magnitude_u8x64, lut_subnorm_u8x64);
863
+
864
+ // Sign via ternary logic: (a ^ b) & ~0x7F in one VPTERNLOGD (imm 0x14)
865
+ __m512i sign_diff_u8x64 = _mm512_ternarylogic_epi64(a_e4m3_u8x64, b_e4m3_u8x64, magnitude_mask_u8x64, 0x14);
866
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_base_u8x64, _mm512_test_epi8_mask(sign_diff_u8x64, sign_diff_u8x64),
867
+ _mm512_setzero_si512(), b_base_u8x64);
868
+
869
+ // Octave masks via cascaded range compares on magnitude
870
+ __mmask64 ka_lt20 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_20_u8x64);
871
+ __mmask64 ka_lt40 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_40_u8x64);
872
+ __mmask64 ka_lt60 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_60_u8x64);
873
+ __mmask64 kb_lt20 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_20_u8x64);
874
+ __mmask64 kb_lt40 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_40_u8x64);
875
+ __mmask64 kb_lt60 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_60_u8x64);
876
+
877
+ __m512i a0_u8x64 = _mm512_maskz_mov_epi8(ka_lt20, a_base_u8x64);
878
+ __m512i a1_u8x64 = _mm512_maskz_mov_epi8(ka_lt40 & ~ka_lt20, a_base_u8x64);
879
+ __m512i a2_u8x64 = _mm512_maskz_mov_epi8(ka_lt60 & ~ka_lt40, a_base_u8x64);
880
+ __m512i a3_u8x64 = _mm512_maskz_mov_epi8(~ka_lt60, a_base_u8x64);
881
+
882
+ __m512i b0_i8x64 = _mm512_maskz_mov_epi8(kb_lt20, b_signed_i8x64);
883
+ __m512i b1_i8x64 = _mm512_maskz_mov_epi8(kb_lt40 & ~kb_lt20, b_signed_i8x64);
884
+ __m512i b2_i8x64 = _mm512_maskz_mov_epi8(kb_lt60 & ~kb_lt40, b_signed_i8x64);
885
+ __m512i b3_i8x64 = _mm512_maskz_mov_epi8(~kb_lt60, b_signed_i8x64);
886
+
887
+ // 16 VPDPBUSD into 7 accumulators grouped by octave sum k = oa + ob
888
+ dot0_i32x16 = _mm512_dpbusd_epi32(dot0_i32x16, a0_u8x64, b0_i8x64);
889
+ dot1_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(dot1_i32x16, a0_u8x64, b1_i8x64), a1_u8x64, b0_i8x64);
890
+ dot2_i32x16 = _mm512_dpbusd_epi32(
891
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(dot2_i32x16, a0_u8x64, b2_i8x64), a1_u8x64, b1_i8x64), a2_u8x64,
892
+ b0_i8x64);
893
+ dot3_i32x16 = _mm512_dpbusd_epi32(
894
+ _mm512_dpbusd_epi32(
895
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(dot3_i32x16, a0_u8x64, b3_i8x64), a1_u8x64, b2_i8x64), a2_u8x64,
896
+ b1_i8x64),
897
+ a3_u8x64, b0_i8x64);
898
+ dot4_i32x16 = _mm512_dpbusd_epi32(
899
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(dot4_i32x16, a1_u8x64, b3_i8x64), a2_u8x64, b2_i8x64), a3_u8x64,
900
+ b1_i8x64);
901
+ dot5_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(dot5_i32x16, a2_u8x64, b3_i8x64), a3_u8x64, b2_i8x64);
902
+ dot6_i32x16 = _mm512_dpbusd_epi32(dot6_i32x16, a3_u8x64, b3_i8x64);
903
+
904
+ if (count_scalars) goto nk_dot_e4m3_icelake_cycle;
905
+
906
+ __m512 sum_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(dot0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
907
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot1_i32x16), _mm512_set1_ps(1.52587890625e-05f), sum_f32x16);
908
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot2_i32x16), _mm512_set1_ps(2.44140625e-04f), sum_f32x16);
909
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot3_i32x16), _mm512_set1_ps(3.90625e-03f), sum_f32x16);
910
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot4_i32x16), _mm512_set1_ps(6.25e-02f), sum_f32x16);
911
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot5_i32x16), _mm512_set1_ps(1.0f), sum_f32x16);
912
+ sum_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(dot6_i32x16), _mm512_set1_ps(16.0f), sum_f32x16);
913
+ *result = nk_reduce_add_f32x16_skylake_(sum_f32x16);
914
+ }
915
+
916
+ #pragma endregion F16 and BF16 Floats
917
+
918
+ #pragma region Binary
919
+
920
+ NK_PUBLIC void nk_dot_u1_icelake(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n_bits, nk_u32_t *result) {
921
+ nk_size_t n_bytes = nk_size_divide_round_up_(n_bits, NK_BITS_PER_BYTE);
922
+ __m512i and_popcount_u64x8 = _mm512_setzero_si512();
923
+ __m512i a_u8x64, b_u8x64;
924
+
925
+ nk_dot_u1_icelake_cycle:
926
+ if (n_bytes < 64) {
927
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
928
+ a_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
929
+ b_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
930
+ n_bytes = 0;
931
+ }
932
+ else {
933
+ a_u8x64 = _mm512_loadu_epi8(a);
934
+ b_u8x64 = _mm512_loadu_epi8(b);
935
+ a += 64, b += 64, n_bytes -= 64;
936
+ }
937
+ and_popcount_u64x8 = _mm512_add_epi64(and_popcount_u64x8, _mm512_popcnt_epi64(_mm512_and_si512(a_u8x64, b_u8x64)));
938
+ if (n_bytes) goto nk_dot_u1_icelake_cycle;
939
+
940
+ *result = (nk_u32_t)_mm512_reduce_add_epi64(and_popcount_u64x8);
941
+ }
942
+
943
+ typedef struct nk_dot_u1x512_state_icelake_t {
944
+ __m512i dot_count_i64x8;
945
+ } nk_dot_u1x512_state_icelake_t;
946
+
947
+ NK_INTERNAL void nk_dot_u1x512_init_icelake(nk_dot_u1x512_state_icelake_t *state) {
948
+ state->dot_count_i64x8 = _mm512_setzero_si512();
949
+ }
950
+
951
+ NK_INTERNAL void nk_dot_u1x512_update_icelake(nk_dot_u1x512_state_icelake_t *state, nk_b512_vec_t a, nk_b512_vec_t b,
952
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
953
+ nk_unused_(depth_offset);
954
+ nk_unused_(active_dimensions);
955
+ state->dot_count_i64x8 = _mm512_add_epi64(state->dot_count_i64x8,
956
+ _mm512_popcnt_epi64(_mm512_and_si512(a.zmm, b.zmm)));
957
+ }
958
+
959
+ NK_INTERNAL void nk_dot_u1x512_finalize_icelake( //
960
+ nk_dot_u1x512_state_icelake_t const *state_a, nk_dot_u1x512_state_icelake_t const *state_b,
961
+ nk_dot_u1x512_state_icelake_t const *state_c, nk_dot_u1x512_state_icelake_t const *state_d,
962
+ nk_size_t total_dimensions, nk_b128_vec_t *result) {
963
+ nk_unused_(total_dimensions);
964
+
965
+ // VPMOVQD: truncate 8×i64 → 8×i32 per state
966
+ __m256i a_i32x8 = _mm512_cvtepi64_epi32(state_a->dot_count_i64x8);
967
+ __m256i b_i32x8 = _mm512_cvtepi64_epi32(state_b->dot_count_i64x8);
968
+ __m256i c_i32x8 = _mm512_cvtepi64_epi32(state_c->dot_count_i64x8);
969
+ __m256i d_i32x8 = _mm512_cvtepi64_epi32(state_d->dot_count_i64x8);
970
+
971
+ // Fold 8×i32 → 4×i32 (add high 128-bit lane to low)
972
+ __m128i a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(a_i32x8), _mm256_extracti128_si256(a_i32x8, 1));
973
+ __m128i b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(b_i32x8), _mm256_extracti128_si256(b_i32x8, 1));
974
+ __m128i c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(c_i32x8), _mm256_extracti128_si256(c_i32x8, 1));
975
+ __m128i d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(d_i32x8), _mm256_extracti128_si256(d_i32x8, 1));
976
+
977
+ // VPHADDD cascade: 4×i32 → 2×i32 → 1×i32 per state
978
+ __m128i ab_i32x4 = _mm_hadd_epi32(a_i32x4, b_i32x4);
979
+ __m128i cd_i32x4 = _mm_hadd_epi32(c_i32x4, d_i32x4);
980
+ result->xmm = _mm_hadd_epi32(ab_i32x4, cd_i32x4);
981
+ }
982
+
983
+ #pragma endregion Binary
984
+
985
+ #if defined(__clang__)
986
+ #pragma clang attribute pop
987
+ #elif defined(__GNUC__)
988
+ #pragma GCC pop_options
989
+ #endif
990
+
991
+ #if defined(__cplusplus)
992
+ } // extern "C"
993
+ #endif
994
+
995
+ #endif // NK_TARGET_ICELAKE
996
+ #endif // NK_TARGET_X8664_
997
+ #endif // NK_DOT_ICELAKE_H