usearch 2.21.3 → 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 +235 -52
  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,1067 @@
1
+ /**
2
+ * @brief SIMD-accelerated Spatial Similarity Measures for Ice Lake.
3
+ * @file include/numkong/spatial/icelake.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/spatial.h
8
+ *
9
+ * @section spatial_icelake_instructions Key AVX-512 VNNI Spatial Instructions
10
+ *
11
+ * Intrinsic Instruction Icelake Genoa
12
+ * _mm512_dpwssd_epi32 VPDPWSSD (ZMM, ZMM, ZMM) 5cy @ p0 4cy @ p01
13
+ * _mm512_cvtepi8_epi16 VPMOVSXBW (ZMM, YMM) 3cy @ p5 3cy @ p12
14
+ * _mm512_sub_epi16 VPSUBW (ZMM, ZMM, ZMM) 1cy @ p05 1cy @ p0123
15
+ *
16
+ * Ice Lake's VNNI enables efficient i8 distance computations via VPDPWSSD for squared differences.
17
+ * After widening i8 to i16, the same instruction computes both multiply and horizontal pair addition.
18
+ */
19
+ #ifndef NK_SPATIAL_ICELAKE_H
20
+ #define NK_SPATIAL_ICELAKE_H
21
+
22
+ #if NK_TARGET_X8664_
23
+ #if NK_TARGET_ICELAKE
24
+
25
+ #include "numkong/types.h"
26
+ #include "numkong/spatial/haswell.h" // `nk_angular_normalize_f32_haswell_`, `nk_f32_sqrt_haswell`
27
+ #include "numkong/reduce/skylake.h" // `nk_reduce_add_f32x16_skylake_`
28
+
29
+ #if defined(__cplusplus)
30
+ extern "C" {
31
+ #endif
32
+
33
+ #if defined(__clang__)
34
+ #pragma clang attribute push( \
35
+ __attribute__((target("avx2,avx512f,avx512vl,avx512bw,avx512dq,avx512vnni,avx512vbmi,f16c,fma,bmi,bmi2"))), \
36
+ apply_to = function)
37
+ #elif defined(__GNUC__)
38
+ #pragma GCC push_options
39
+ #pragma GCC target("avx2", "avx512f", "avx512vl", "avx512bw", "avx512dq", "avx512vnni", "avx512vbmi", "f16c", "fma", \
40
+ "bmi", "bmi2")
41
+ #endif
42
+
43
+ NK_PUBLIC void nk_sqeuclidean_i8_icelake(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_u32_t *result) {
44
+ // Optimized i8 L2-squared using saturating subtract + DPWSSD
45
+ //
46
+ // Old approach (Haswell/Skylake):
47
+ // - Compute (a-b) as signed i8, then sign-extend i8→i16 using cvtepi8_epi16
48
+ // - Square using vpmaddwd on i16 values (32 elements/iteration)
49
+ // - Bottleneck: cvtepi8_epi16 (3cy latency @ p5) limits throughput
50
+ //
51
+ // New approach (Ice Lake+):
52
+ // - XOR with 0x80 to reinterpret signed i8 as unsigned u8
53
+ // - Compute |a-b| using unsigned saturating subtraction: diff = (a ⊖ b) | (b ⊖ a)
54
+ // - Zero-extend u8→u16 using unpacking (1cy latency @ p5)
55
+ // - Square using vpmaddwd on u16 values (64 elements/iteration)
56
+ // - Eliminates cvtepi8_epi16 bottleneck, doubles throughput
57
+ //
58
+ // Performance gain: 1.6-1.85× speedup
59
+ // - Processes 64 elements/iteration (2× improvement)
60
+ // - Faster zero-extension (unpack 1cy vs cvtepi8_epi16 3cy)
61
+ // - Correctness: |a-b|² = (a-b)², so unsigned absolute differences are valid
62
+ //
63
+ // The XOR bias is needed because subs_epu8 (unsigned) saturates to 0 when
64
+ // the result would be negative, so OR-ing both directions gives the true |a-b|.
65
+ // A naive subs_epi8 (signed) saturates to -128, corrupting the OR trick.
66
+ //
67
+ __m512i distance_sq_low_i32x16 = _mm512_setzero_si512();
68
+ __m512i distance_sq_high_i32x16 = _mm512_setzero_si512();
69
+ __m512i const zeros_i8x64 = _mm512_setzero_si512();
70
+ __m512i const bias_i8x64 = _mm512_set1_epi8((char)0x80);
71
+ __m512i diff_low_i16x32, diff_high_i16x32;
72
+ __m512i a_i8x64, b_i8x64, diff_u8x64;
73
+
74
+ nk_sqeuclidean_i8_icelake_cycle:
75
+ if (n < 64) {
76
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
77
+ a_i8x64 = _mm512_maskz_loadu_epi8(mask, a);
78
+ b_i8x64 = _mm512_maskz_loadu_epi8(mask, b);
79
+ n = 0;
80
+ }
81
+ else {
82
+ a_i8x64 = _mm512_loadu_si512(a);
83
+ b_i8x64 = _mm512_loadu_si512(b);
84
+ a += 64, b += 64, n -= 64;
85
+ }
86
+
87
+ // Reinterpret signed i8 as unsigned u8 by flipping the sign bit
88
+ a_i8x64 = _mm512_xor_si512(a_i8x64, bias_i8x64);
89
+ b_i8x64 = _mm512_xor_si512(b_i8x64, bias_i8x64);
90
+
91
+ // Compute |a-b| using unsigned saturating subtraction
92
+ // subs_epu8 saturates to 0 if result would be negative
93
+ // OR-ing both directions gives absolute difference as unsigned
94
+ diff_u8x64 = _mm512_or_si512(_mm512_subs_epu8(a_i8x64, b_i8x64), _mm512_subs_epu8(b_i8x64, a_i8x64));
95
+
96
+ // Zero-extend to i16 using unpack (1cy @ p5, much faster than cvtepi8_epi16)
97
+ diff_low_i16x32 = _mm512_unpacklo_epi8(diff_u8x64, zeros_i8x64);
98
+ diff_high_i16x32 = _mm512_unpackhi_epi8(diff_u8x64, zeros_i8x64);
99
+
100
+ // Multiply and accumulate at i16 level, accumulate at i32 level
101
+ distance_sq_low_i32x16 = _mm512_dpwssd_epi32(distance_sq_low_i32x16, diff_low_i16x32, diff_low_i16x32);
102
+ distance_sq_high_i32x16 = _mm512_dpwssd_epi32(distance_sq_high_i32x16, diff_high_i16x32, diff_high_i16x32);
103
+ if (n) goto nk_sqeuclidean_i8_icelake_cycle;
104
+
105
+ *result = _mm512_reduce_add_epi32(_mm512_add_epi32(distance_sq_low_i32x16, distance_sq_high_i32x16));
106
+ }
107
+
108
+ NK_PUBLIC void nk_euclidean_i8_icelake(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_f32_t *result) {
109
+ nk_u32_t d2;
110
+ nk_sqeuclidean_i8_icelake(a, b, n, &d2);
111
+ *result = nk_f32_sqrt_haswell((nk_f32_t)d2);
112
+ }
113
+
114
+ NK_PUBLIC void nk_angular_i8_icelake(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_f32_t *result) {
115
+
116
+ __m512i dot_product_i32x16 = _mm512_setzero_si512();
117
+ __m512i a_norm_sq_i32x16 = _mm512_setzero_si512();
118
+ __m512i b_norm_sq_i32x16 = _mm512_setzero_si512();
119
+ __m512i a_i16x32, b_i16x32;
120
+ nk_angular_i8_icelake_cycle:
121
+ if (n < 32) {
122
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
123
+ a_i16x32 = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a));
124
+ b_i16x32 = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b));
125
+ n = 0;
126
+ }
127
+ else {
128
+ a_i16x32 = _mm512_cvtepi8_epi16(_mm256_loadu_si256((__m256i const *)a));
129
+ b_i16x32 = _mm512_cvtepi8_epi16(_mm256_loadu_si256((__m256i const *)b));
130
+ a += 32, b += 32, n -= 32;
131
+ }
132
+
133
+ // We can't directly use the `_mm512_dpbusd_epi32` intrinsic everywhere,
134
+ // as it's asymmetric with respect to the sign of the input arguments:
135
+ //
136
+ // Signed(ZeroExtend16(a.byte[4 × j]) × SignExtend16(b.byte[4 × j]))
137
+ //
138
+ // To compute the squares, we could just drop the sign bit of the second argument.
139
+ // But this would lead to big-big problems on values like `-128`!
140
+ // For dot-products we don't have the luxury of optimizing the sign bit away.
141
+ // Assuming this is an approximate kernel (with reciprocal square root approximations)
142
+ // in the end, we can allow clamping the value to [-127, 127] range.
143
+ //
144
+ // VNNI instruction performance (Ice Lake vs Zen4 Genoa):
145
+ //
146
+ // Instruction Icelake Genoa
147
+ // VPDPBUSDS (ZMM, ZMM, ZMM) 5cy @ p0 4cy @ p01
148
+ // VPDPWSSDS (ZMM, ZMM, ZMM) 5cy @ p0 4cy @ p01
149
+ // VPMADDWD (ZMM, ZMM, ZMM) 5cy @ p05 3cy @ p01
150
+ //
151
+ // On Ice Lake, VNNI bottlenecks on port 0. On Genoa, dual-issue on p01 is faster.
152
+ //
153
+ // The old solution was complex replied on 1. and 2.:
154
+ //
155
+ // a_i8_abs_vec = _mm512_abs_epi8(a_i8_vec);
156
+ // b_i8_abs_vec = _mm512_abs_epi8(b_i8_vec);
157
+ // a2_i32_vec = _mm512_dpbusds_epi32(a2_i32_vec, a_i8_abs_vec, a_i8_abs_vec);
158
+ // b2_i32_vec = _mm512_dpbusds_epi32(b2_i32_vec, b_i8_abs_vec, b_i8_abs_vec);
159
+ // ab_i32_low_vec = _mm512_dpwssds_epi32( //
160
+ // ab_i32_low_vec, //
161
+ // _mm512_cvtepi8_epi16(_mm512_castsi512_si256(a_i8_vec)), //
162
+ // _mm512_cvtepi8_epi16(_mm512_castsi512_si256(b_i8_vec)));
163
+ // ab_i32_high_vec = _mm512_dpwssds_epi32( //
164
+ // ab_i32_high_vec, //
165
+ // _mm512_cvtepi8_epi16(_mm512_extracti64x4_epi64(a_i8_vec, 1)), //
166
+ // _mm512_cvtepi8_epi16(_mm512_extracti64x4_epi64(b_i8_vec, 1)));
167
+ //
168
+ // The new solution is simpler and relies on 3.:
169
+ dot_product_i32x16 = _mm512_add_epi32(dot_product_i32x16, _mm512_madd_epi16(a_i16x32, b_i16x32));
170
+ a_norm_sq_i32x16 = _mm512_add_epi32(a_norm_sq_i32x16, _mm512_madd_epi16(a_i16x32, a_i16x32));
171
+ b_norm_sq_i32x16 = _mm512_add_epi32(b_norm_sq_i32x16, _mm512_madd_epi16(b_i16x32, b_i16x32));
172
+ if (n) goto nk_angular_i8_icelake_cycle;
173
+
174
+ nk_i32_t dot_product_i32 = _mm512_reduce_add_epi32(dot_product_i32x16);
175
+ nk_i32_t a_norm_sq_i32 = _mm512_reduce_add_epi32(a_norm_sq_i32x16);
176
+ nk_i32_t b_norm_sq_i32 = _mm512_reduce_add_epi32(b_norm_sq_i32x16);
177
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)dot_product_i32, (nk_f32_t)a_norm_sq_i32,
178
+ (nk_f32_t)b_norm_sq_i32);
179
+ }
180
+ NK_PUBLIC void nk_sqeuclidean_u8_icelake(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_u32_t *result) {
181
+ __m512i distance_sq_low_i32x16 = _mm512_setzero_si512();
182
+ __m512i distance_sq_high_i32x16 = _mm512_setzero_si512();
183
+ __m512i const zeros_i8x64 = _mm512_setzero_si512();
184
+ __m512i diff_low_i16x32, diff_high_i16x32;
185
+ __m512i a_u8x64, b_u8x64, diff_u8x64;
186
+
187
+ nk_sqeuclidean_u8_icelake_cycle:
188
+ if (n < 64) {
189
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
190
+ a_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
191
+ b_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
192
+ n = 0;
193
+ }
194
+ else {
195
+ a_u8x64 = _mm512_loadu_si512(a);
196
+ b_u8x64 = _mm512_loadu_si512(b);
197
+ a += 64, b += 64, n -= 64;
198
+ }
199
+
200
+ // Substracting unsigned vectors in AVX-512 is done by saturating subtraction:
201
+ diff_u8x64 = _mm512_or_si512(_mm512_subs_epu8(a_u8x64, b_u8x64), _mm512_subs_epu8(b_u8x64, a_u8x64));
202
+ diff_low_i16x32 = _mm512_unpacklo_epi8(diff_u8x64, zeros_i8x64);
203
+ diff_high_i16x32 = _mm512_unpackhi_epi8(diff_u8x64, zeros_i8x64);
204
+
205
+ // Multiply and accumulate at `int16` level, accumulate at `int32` level:
206
+ distance_sq_low_i32x16 = _mm512_dpwssd_epi32(distance_sq_low_i32x16, diff_low_i16x32, diff_low_i16x32);
207
+ distance_sq_high_i32x16 = _mm512_dpwssd_epi32(distance_sq_high_i32x16, diff_high_i16x32, diff_high_i16x32);
208
+ if (n) goto nk_sqeuclidean_u8_icelake_cycle;
209
+
210
+ *result = _mm512_reduce_add_epi32(_mm512_add_epi32(distance_sq_low_i32x16, distance_sq_high_i32x16));
211
+ }
212
+ NK_PUBLIC void nk_euclidean_u8_icelake(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_f32_t *result) {
213
+ nk_u32_t d2;
214
+ nk_sqeuclidean_u8_icelake(a, b, n, &d2);
215
+ *result = nk_f32_sqrt_haswell((nk_f32_t)d2);
216
+ }
217
+
218
+ NK_PUBLIC void nk_angular_u8_icelake(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_f32_t *result) {
219
+
220
+ __m512i dot_product_low_i32x16 = _mm512_setzero_si512();
221
+ __m512i dot_product_high_i32x16 = _mm512_setzero_si512();
222
+ __m512i a_norm_sq_low_i32x16 = _mm512_setzero_si512();
223
+ __m512i a_norm_sq_high_i32x16 = _mm512_setzero_si512();
224
+ __m512i b_norm_sq_low_i32x16 = _mm512_setzero_si512();
225
+ __m512i b_norm_sq_high_i32x16 = _mm512_setzero_si512();
226
+ __m512i const zeros_i8x64 = _mm512_setzero_si512();
227
+ __m512i a_low_i16x32, a_high_i16x32, b_low_i16x32, b_high_i16x32;
228
+ __m512i a_u8x64, b_u8x64;
229
+
230
+ nk_angular_u8_icelake_cycle:
231
+ if (n < 64) {
232
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
233
+ a_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
234
+ b_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
235
+ n = 0;
236
+ }
237
+ else {
238
+ a_u8x64 = _mm512_loadu_si512(a);
239
+ b_u8x64 = _mm512_loadu_si512(b);
240
+ a += 64, b += 64, n -= 64;
241
+ }
242
+
243
+ // Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
244
+ // instructions instead of extracts, as they are much faster and more efficient.
245
+ a_low_i16x32 = _mm512_unpacklo_epi8(a_u8x64, zeros_i8x64);
246
+ a_high_i16x32 = _mm512_unpackhi_epi8(a_u8x64, zeros_i8x64);
247
+ b_low_i16x32 = _mm512_unpacklo_epi8(b_u8x64, zeros_i8x64);
248
+ b_high_i16x32 = _mm512_unpackhi_epi8(b_u8x64, zeros_i8x64);
249
+
250
+ // Multiply and accumulate as `int16`, accumulate products as `int32`:
251
+ dot_product_low_i32x16 = _mm512_dpwssds_epi32(dot_product_low_i32x16, a_low_i16x32, b_low_i16x32);
252
+ dot_product_high_i32x16 = _mm512_dpwssds_epi32(dot_product_high_i32x16, a_high_i16x32, b_high_i16x32);
253
+ a_norm_sq_low_i32x16 = _mm512_dpwssds_epi32(a_norm_sq_low_i32x16, a_low_i16x32, a_low_i16x32);
254
+ a_norm_sq_high_i32x16 = _mm512_dpwssds_epi32(a_norm_sq_high_i32x16, a_high_i16x32, a_high_i16x32);
255
+ b_norm_sq_low_i32x16 = _mm512_dpwssds_epi32(b_norm_sq_low_i32x16, b_low_i16x32, b_low_i16x32);
256
+ b_norm_sq_high_i32x16 = _mm512_dpwssds_epi32(b_norm_sq_high_i32x16, b_high_i16x32, b_high_i16x32);
257
+ if (n) goto nk_angular_u8_icelake_cycle;
258
+
259
+ nk_i32_t dot_product_i32 = _mm512_reduce_add_epi32(
260
+ _mm512_add_epi32(dot_product_low_i32x16, dot_product_high_i32x16));
261
+ nk_i32_t a_norm_sq_i32 = _mm512_reduce_add_epi32(_mm512_add_epi32(a_norm_sq_low_i32x16, a_norm_sq_high_i32x16));
262
+ nk_i32_t b_norm_sq_i32 = _mm512_reduce_add_epi32(_mm512_add_epi32(b_norm_sq_low_i32x16, b_norm_sq_high_i32x16));
263
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)dot_product_i32, (nk_f32_t)a_norm_sq_i32,
264
+ (nk_f32_t)b_norm_sq_i32);
265
+ }
266
+
267
+ NK_PUBLIC void nk_sqeuclidean_i4_icelake(nk_i4x2_t const *a, nk_i4x2_t const *b, nk_size_t n, nk_u32_t *result) {
268
+ // i4 values are packed as nibbles: two 4-bit signed values per byte.
269
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
270
+ n = nk_size_round_up_to_multiple_(n, 2);
271
+ nk_size_t n_bytes = n / 2;
272
+
273
+ // While `int8_t` covers the range [-128, 127], `int4_t` covers only [-8, 7].
274
+ // The absolute difference between two 4-bit integers is at most 15 and fits in `uint4_t`.
275
+ // Moreover, its square is at most 225, which fits into `uint8_t`.
276
+ //
277
+ // Instead of using lookup tables for sign extension and squaring, we use arithmetic:
278
+ //
279
+ // 1. XOR trick for sign extension: `signed = (nibble ^ 8) - 8`
280
+ // Maps [0,7] → [0,7] (positive) and [8,15] → [-8,-1] (negative).
281
+ //
282
+ // 2. For L2 squared: |a-b|² = diff * diff, using `_mm512_dpbusd_epi32`.
283
+ // After computing signed difference and taking abs, the result fits ∈ [0,15].
284
+ // We can then use DPBUSD to compute diff² efficiently without lookup tables.
285
+ //
286
+ // This approach avoids 8x VPSHUFB operations per iteration, replacing them with
287
+ // arithmetic operations that distribute better across execution ports.
288
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
289
+ __m512i const eight_i8x64 = _mm512_set1_epi8(8);
290
+
291
+ __m512i a_i4_u8x64, b_i4_u8x64;
292
+ __m512i a_low_u8x64, a_high_u8x64, b_low_u8x64, b_high_u8x64;
293
+ __m512i a_low_i8x64, a_high_i8x64, b_low_i8x64, b_high_i8x64;
294
+ __m512i diff_low_u8x64, diff_high_u8x64;
295
+ __m512i d2_i32x16 = _mm512_setzero_si512();
296
+
297
+ nk_sqeuclidean_i4_icelake_cycle:
298
+ if (n_bytes < 64) {
299
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
300
+ a_i4_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
301
+ b_i4_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
302
+ n_bytes = 0;
303
+ }
304
+ else {
305
+ a_i4_u8x64 = _mm512_loadu_epi8(a);
306
+ b_i4_u8x64 = _mm512_loadu_epi8(b);
307
+ a += 64, b += 64, n_bytes -= 64;
308
+ }
309
+
310
+ // Extract nibbles as unsigned [0,15]. VPSHUFB ignores high 4 bits of index,
311
+ // so no AND needed for low nibbles when used with lookup, but we need it here.
312
+ a_low_u8x64 = _mm512_and_si512(a_i4_u8x64, nibble_mask_u8x64);
313
+ a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_i4_u8x64, 4), nibble_mask_u8x64);
314
+ b_low_u8x64 = _mm512_and_si512(b_i4_u8x64, nibble_mask_u8x64);
315
+ b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_i4_u8x64, 4), nibble_mask_u8x64);
316
+
317
+ // Sign extend using XOR trick: signed = (nibble ^ 8) - 8
318
+ a_low_i8x64 = _mm512_sub_epi8(_mm512_xor_si512(a_low_u8x64, eight_i8x64), eight_i8x64);
319
+ a_high_i8x64 = _mm512_sub_epi8(_mm512_xor_si512(a_high_u8x64, eight_i8x64), eight_i8x64);
320
+ b_low_i8x64 = _mm512_sub_epi8(_mm512_xor_si512(b_low_u8x64, eight_i8x64), eight_i8x64);
321
+ b_high_i8x64 = _mm512_sub_epi8(_mm512_xor_si512(b_high_u8x64, eight_i8x64), eight_i8x64);
322
+
323
+ // Compute |a - b| for each nibble pair. Result is unsigned ∈ [0, 15].
324
+ diff_low_u8x64 = _mm512_abs_epi8(_mm512_sub_epi8(a_low_i8x64, b_low_i8x64));
325
+ diff_high_u8x64 = _mm512_abs_epi8(_mm512_sub_epi8(a_high_i8x64, b_high_i8x64));
326
+
327
+ // Square and accumulate using DPBUSD: diff² = diff * diff.
328
+ // DPBUSD computes u8*i8 products and sums groups of 4 into i32.
329
+ // Since diff is ∈ [0,15], it's safe for both u8 and i8 interpretation.
330
+ d2_i32x16 = _mm512_dpbusd_epi32(d2_i32x16, diff_low_u8x64, diff_low_u8x64);
331
+ d2_i32x16 = _mm512_dpbusd_epi32(d2_i32x16, diff_high_u8x64, diff_high_u8x64);
332
+ if (n_bytes) goto nk_sqeuclidean_i4_icelake_cycle;
333
+
334
+ *result = (nk_u32_t)_mm512_reduce_add_epi32(d2_i32x16);
335
+ }
336
+ NK_PUBLIC void nk_euclidean_i4_icelake(nk_i4x2_t const *a, nk_i4x2_t const *b, nk_size_t n, nk_f32_t *result) {
337
+ nk_u32_t d2;
338
+ nk_sqeuclidean_i4_icelake(a, b, n, &d2);
339
+ *result = nk_f32_sqrt_haswell((nk_f32_t)d2);
340
+ }
341
+ NK_PUBLIC void nk_angular_i4_icelake(nk_i4x2_t const *a, nk_i4x2_t const *b, nk_size_t n, nk_f32_t *result) {
342
+ // i4 values are packed as nibbles: two 4-bit signed values per byte.
343
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
344
+ n = nk_size_round_up_to_multiple_(n, 2);
345
+ nk_size_t n_bytes = n / 2;
346
+
347
+ // Angular distance for signed 4-bit integers requires computing:
348
+ // 1. Dot product: ∑(aᵢ × bᵢ)
349
+ // 2. Squared norms: ∑(aᵢ²) and ∑(bᵢ²)
350
+ //
351
+ // For signed i4 values in [-8, 7], we use DPBUSD for everything by leveraging
352
+ // an algebraic identity. Define x = a ^ 8 (XOR with 8), which maps:
353
+ // [0,7] → [8,15] and [8,15] → [0,7]
354
+ //
355
+ // The signed value is: a_signed = x - 8
356
+ //
357
+ // For two signed values:
358
+ // a_signed × b_signed = (ax - 8)(bx - 8) = ax × bx - 8 × ax - 8 × bx + 64
359
+ //
360
+ // Therefore:
361
+ // dot(a_signed, b_signed) = DPBUSD(ax, bx) - 8 × (∑(ax) + ∑(bx)) + 64 × n
362
+ //
363
+ // This avoids all i8 → i16 upcasts and uses DPBUSD directly on byte values!
364
+ // For norms, we use |x|² = x², computing abs then squaring with DPBUSD.
365
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
366
+ __m512i const eight_i8x64 = _mm512_set1_epi8(8);
367
+ __m512i const zeros_i8x64 = _mm512_setzero_si512();
368
+
369
+ __m512i a_i4_u8x64, b_i4_u8x64;
370
+ __m512i a_low_u8x64, a_high_u8x64, b_low_u8x64, b_high_u8x64;
371
+ __m512i ax_low_u8x64, ax_high_u8x64, bx_low_u8x64, bx_high_u8x64;
372
+ __m512i a_low_i8x64, a_high_i8x64, b_low_i8x64, b_high_i8x64;
373
+
374
+ // Accumulators for dot product (using biased values) and correction sums
375
+ __m512i ab_i32x16 = zeros_i8x64;
376
+ __m512i ax_sum_i64x8 = zeros_i8x64;
377
+ __m512i bx_sum_i64x8 = zeros_i8x64;
378
+ // Accumulators for squared norms
379
+ __m512i a2_i32x16 = zeros_i8x64;
380
+ __m512i b2_i32x16 = zeros_i8x64;
381
+
382
+ nk_angular_i4_icelake_cycle:
383
+ if (n_bytes < 64) {
384
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
385
+ a_i4_u8x64 = _mm512_mask_loadu_epi8(_mm512_set1_epi8((char)0x88), mask, a);
386
+ b_i4_u8x64 = _mm512_mask_loadu_epi8(_mm512_set1_epi8((char)0x88), mask, b);
387
+ n_bytes = 0;
388
+ }
389
+ else {
390
+ a_i4_u8x64 = _mm512_loadu_epi8(a);
391
+ b_i4_u8x64 = _mm512_loadu_epi8(b);
392
+ a += 64, b += 64, n_bytes -= 64;
393
+ }
394
+
395
+ // Extract nibbles as unsigned [0,15]
396
+ a_low_u8x64 = _mm512_and_si512(a_i4_u8x64, nibble_mask_u8x64);
397
+ a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_i4_u8x64, 4), nibble_mask_u8x64);
398
+ b_low_u8x64 = _mm512_and_si512(b_i4_u8x64, nibble_mask_u8x64);
399
+ b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_i4_u8x64, 4), nibble_mask_u8x64);
400
+
401
+ // Compute biased values: ax = a ^ 8 (still ∈ [0,15], just reordered)
402
+ ax_low_u8x64 = _mm512_xor_si512(a_low_u8x64, eight_i8x64);
403
+ ax_high_u8x64 = _mm512_xor_si512(a_high_u8x64, eight_i8x64);
404
+ bx_low_u8x64 = _mm512_xor_si512(b_low_u8x64, eight_i8x64);
405
+ bx_high_u8x64 = _mm512_xor_si512(b_high_u8x64, eight_i8x64);
406
+
407
+ // Dot product using DPBUSD on biased values (correction applied at end)
408
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, ax_low_u8x64, bx_low_u8x64);
409
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, ax_high_u8x64, bx_high_u8x64);
410
+
411
+ // Track sums for correction using SAD (sum of absolute differences with zero)
412
+ ax_sum_i64x8 = _mm512_add_epi64(ax_sum_i64x8, _mm512_sad_epu8(ax_low_u8x64, zeros_i8x64));
413
+ ax_sum_i64x8 = _mm512_add_epi64(ax_sum_i64x8, _mm512_sad_epu8(ax_high_u8x64, zeros_i8x64));
414
+ bx_sum_i64x8 = _mm512_add_epi64(bx_sum_i64x8, _mm512_sad_epu8(bx_low_u8x64, zeros_i8x64));
415
+ bx_sum_i64x8 = _mm512_add_epi64(bx_sum_i64x8, _mm512_sad_epu8(bx_high_u8x64, zeros_i8x64));
416
+
417
+ // For norms: convert to signed, take abs, then square with DPBUSD
418
+ a_low_i8x64 = _mm512_sub_epi8(ax_low_u8x64, eight_i8x64);
419
+ a_high_i8x64 = _mm512_sub_epi8(ax_high_u8x64, eight_i8x64);
420
+ b_low_i8x64 = _mm512_sub_epi8(bx_low_u8x64, eight_i8x64);
421
+ b_high_i8x64 = _mm512_sub_epi8(bx_high_u8x64, eight_i8x64);
422
+
423
+ __m512i a_low_abs_u8x64 = _mm512_abs_epi8(a_low_i8x64);
424
+ __m512i a_high_abs_u8x64 = _mm512_abs_epi8(a_high_i8x64);
425
+ __m512i b_low_abs_u8x64 = _mm512_abs_epi8(b_low_i8x64);
426
+ __m512i b_high_abs_u8x64 = _mm512_abs_epi8(b_high_i8x64);
427
+
428
+ // Squared norms: ‖x‖² = x², use DPBUSD for efficient squaring
429
+ a2_i32x16 = _mm512_dpbusd_epi32(a2_i32x16, a_low_abs_u8x64, a_low_abs_u8x64);
430
+ a2_i32x16 = _mm512_dpbusd_epi32(a2_i32x16, a_high_abs_u8x64, a_high_abs_u8x64);
431
+ b2_i32x16 = _mm512_dpbusd_epi32(b2_i32x16, b_low_abs_u8x64, b_low_abs_u8x64);
432
+ b2_i32x16 = _mm512_dpbusd_epi32(b2_i32x16, b_high_abs_u8x64, b_high_abs_u8x64);
433
+ if (n_bytes) goto nk_angular_i4_icelake_cycle;
434
+
435
+ // Apply algebraic correction for signed dot product:
436
+ // signed_dot = DPBUSD(ax, bx) - 8 × (∑(ax) + ∑(bx)) + 64 × n
437
+ nk_i64_t ax_sum = _mm512_reduce_add_epi64(ax_sum_i64x8);
438
+ nk_i64_t bx_sum = _mm512_reduce_add_epi64(bx_sum_i64x8);
439
+ nk_i32_t ab_raw = _mm512_reduce_add_epi32(ab_i32x16);
440
+ nk_i32_t ab = ab_raw - 8 * (nk_i32_t)(ax_sum + bx_sum) + 64 * (nk_i32_t)n;
441
+
442
+ nk_size_t n_bytes_total = nk_size_divide_round_up_(n, 2);
443
+ nk_i32_t norm_excess = 128 * (nk_i32_t)(nk_size_round_up_to_multiple_(n_bytes_total, 64) - n_bytes_total);
444
+ nk_i32_t a2 = _mm512_reduce_add_epi32(a2_i32x16) - norm_excess;
445
+ nk_i32_t b2 = _mm512_reduce_add_epi32(b2_i32x16) - norm_excess;
446
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)ab, (nk_f32_t)a2, (nk_f32_t)b2);
447
+ }
448
+
449
+ NK_PUBLIC void nk_sqeuclidean_u4_icelake(nk_u4x2_t const *a, nk_u4x2_t const *b, nk_size_t n, nk_u32_t *result) {
450
+ // u4 values are packed as nibbles: two 4-bit unsigned values per byte.
451
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
452
+ n = nk_size_round_up_to_multiple_(n, 2);
453
+ nk_size_t n_bytes = n / 2;
454
+
455
+ // For unsigned 4-bit integers ∈ [0, 15], the L2 squared distance is straightforward:
456
+ // 1. Extract nibbles as u8 values
457
+ // 2. Compute |a - b| using saturating subtraction: max(a,b) - min(a,b) = (a ⊖ b) | (b ⊖ a)
458
+ // 3. Square with DPBUSD: diff * diff
459
+ //
460
+ // No sign extension needed since values are unsigned.
461
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
462
+
463
+ __m512i a_u4_u8x64, b_u4_u8x64;
464
+ __m512i a_low_u8x64, a_high_u8x64, b_low_u8x64, b_high_u8x64;
465
+ __m512i diff_low_u8x64, diff_high_u8x64;
466
+ __m512i d2_i32x16 = _mm512_setzero_si512();
467
+
468
+ nk_sqeuclidean_u4_icelake_cycle:
469
+ if (n_bytes < 64) {
470
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
471
+ a_u4_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
472
+ b_u4_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
473
+ n_bytes = 0;
474
+ }
475
+ else {
476
+ a_u4_u8x64 = _mm512_loadu_epi8(a);
477
+ b_u4_u8x64 = _mm512_loadu_epi8(b);
478
+ a += 64, b += 64, n_bytes -= 64;
479
+ }
480
+
481
+ // Extract nibbles as unsigned [0,15]
482
+ a_low_u8x64 = _mm512_and_si512(a_u4_u8x64, nibble_mask_u8x64);
483
+ a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_u4_u8x64, 4), nibble_mask_u8x64);
484
+ b_low_u8x64 = _mm512_and_si512(b_u4_u8x64, nibble_mask_u8x64);
485
+ b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_u4_u8x64, 4), nibble_mask_u8x64);
486
+
487
+ // Absolute difference for unsigned: |a-b| = (a ⊖ b) | (b ⊖ a) where ⊖ is saturating sub
488
+ diff_low_u8x64 = _mm512_or_si512(_mm512_subs_epu8(a_low_u8x64, b_low_u8x64),
489
+ _mm512_subs_epu8(b_low_u8x64, a_low_u8x64));
490
+ diff_high_u8x64 = _mm512_or_si512(_mm512_subs_epu8(a_high_u8x64, b_high_u8x64),
491
+ _mm512_subs_epu8(b_high_u8x64, a_high_u8x64));
492
+
493
+ // Square and accumulate using DPBUSD
494
+ d2_i32x16 = _mm512_dpbusd_epi32(d2_i32x16, diff_low_u8x64, diff_low_u8x64);
495
+ d2_i32x16 = _mm512_dpbusd_epi32(d2_i32x16, diff_high_u8x64, diff_high_u8x64);
496
+ if (n_bytes) goto nk_sqeuclidean_u4_icelake_cycle;
497
+
498
+ *result = (nk_u32_t)_mm512_reduce_add_epi32(d2_i32x16);
499
+ }
500
+ NK_PUBLIC void nk_euclidean_u4_icelake(nk_u4x2_t const *a, nk_u4x2_t const *b, nk_size_t n, nk_f32_t *result) {
501
+ nk_u32_t d2;
502
+ nk_sqeuclidean_u4_icelake(a, b, n, &d2);
503
+ *result = nk_f32_sqrt_haswell((nk_f32_t)d2);
504
+ }
505
+
506
+ NK_PUBLIC void nk_angular_u4_icelake(nk_u4x2_t const *a, nk_u4x2_t const *b, nk_size_t n, nk_f32_t *result) {
507
+ // u4 values are packed as nibbles: two 4-bit unsigned values per byte.
508
+ // Parameter `n` is the number of 4-bit values (dimensions), not bytes.
509
+ n = nk_size_round_up_to_multiple_(n, 2);
510
+ nk_size_t n_bytes = n / 2;
511
+
512
+ // Angular distance for unsigned 4-bit integers ∈ [0, 15].
513
+ // Since values are unsigned and small, we can use DPBUSD directly for both
514
+ // dot product and norms without any sign handling.
515
+ //
516
+ // DPBUSD computes: ZeroExtend(a) * SignExtend(b), but for values ∈ [0, 15],
517
+ // sign extension is identity (no high bit set), so it works correctly.
518
+ __m512i const nibble_mask_u8x64 = _mm512_set1_epi8(0x0F);
519
+ __m512i const zeros_i8x64 = _mm512_setzero_si512();
520
+
521
+ __m512i a_u4_u8x64, b_u4_u8x64;
522
+ __m512i a_low_u8x64, a_high_u8x64, b_low_u8x64, b_high_u8x64;
523
+
524
+ __m512i ab_i32x16 = zeros_i8x64;
525
+ __m512i a2_i64x8 = zeros_i8x64;
526
+ __m512i b2_i64x8 = zeros_i8x64;
527
+
528
+ nk_angular_u4_icelake_cycle:
529
+ if (n_bytes < 64) {
530
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
531
+ a_u4_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
532
+ b_u4_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
533
+ n_bytes = 0;
534
+ }
535
+ else {
536
+ a_u4_u8x64 = _mm512_loadu_epi8(a);
537
+ b_u4_u8x64 = _mm512_loadu_epi8(b);
538
+ a += 64, b += 64, n_bytes -= 64;
539
+ }
540
+
541
+ // Extract nibbles as unsigned [0,15]
542
+ a_low_u8x64 = _mm512_and_si512(a_u4_u8x64, nibble_mask_u8x64);
543
+ a_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(a_u4_u8x64, 4), nibble_mask_u8x64);
544
+ b_low_u8x64 = _mm512_and_si512(b_u4_u8x64, nibble_mask_u8x64);
545
+ b_high_u8x64 = _mm512_and_si512(_mm512_srli_epi16(b_u4_u8x64, 4), nibble_mask_u8x64);
546
+
547
+ // Dot product with DPBUSD (safe for unsigned [0,15])
548
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, a_low_u8x64, b_low_u8x64);
549
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, a_high_u8x64, b_high_u8x64);
550
+
551
+ // Squared norms: compute a² per nibble using lookup table for efficiency
552
+ // Squares lookup: 0 → 0, 1 → 1, 2 → 4, ..., 15 → 225
553
+ __m512i const u4_squares_lookup_u8x64 = _mm512_set_epi8(
554
+ (char)225, (char)196, (char)169, (char)144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, //
555
+ (char)225, (char)196, (char)169, (char)144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, //
556
+ (char)225, (char)196, (char)169, (char)144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, //
557
+ (char)225, (char)196, (char)169, (char)144, 121, 100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0);
558
+
559
+ __m512i a2_low_u8x64 = _mm512_shuffle_epi8(u4_squares_lookup_u8x64, a_low_u8x64);
560
+ __m512i a2_high_u8x64 = _mm512_shuffle_epi8(u4_squares_lookup_u8x64, a_high_u8x64);
561
+ __m512i b2_low_u8x64 = _mm512_shuffle_epi8(u4_squares_lookup_u8x64, b_low_u8x64);
562
+ __m512i b2_high_u8x64 = _mm512_shuffle_epi8(u4_squares_lookup_u8x64, b_high_u8x64);
563
+
564
+ // Accumulate low and high squares separately using SAD to avoid u8 overflow
565
+ a2_i64x8 = _mm512_add_epi64(a2_i64x8, _mm512_sad_epu8(a2_low_u8x64, zeros_i8x64));
566
+ a2_i64x8 = _mm512_add_epi64(a2_i64x8, _mm512_sad_epu8(a2_high_u8x64, zeros_i8x64));
567
+ b2_i64x8 = _mm512_add_epi64(b2_i64x8, _mm512_sad_epu8(b2_low_u8x64, zeros_i8x64));
568
+ b2_i64x8 = _mm512_add_epi64(b2_i64x8, _mm512_sad_epu8(b2_high_u8x64, zeros_i8x64));
569
+ if (n_bytes) goto nk_angular_u4_icelake_cycle;
570
+
571
+ nk_i32_t ab = _mm512_reduce_add_epi32(ab_i32x16);
572
+ nk_i64_t a2 = _mm512_reduce_add_epi64(a2_i64x8);
573
+ nk_i64_t b2 = _mm512_reduce_add_epi64(b2_i64x8);
574
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)ab, (nk_f32_t)a2, (nk_f32_t)b2);
575
+ }
576
+
577
+ NK_PUBLIC void nk_sqeuclidean_e4m3_icelake(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
578
+ // E4M3 squared Euclidean distance via octave VNNI.
579
+
580
+ __m512i const lut_normal_u8x64 = _mm512_set_epi8( //
581
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
582
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8, //
583
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
584
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8); //
585
+ __m512i const lut_subnorm_u8x64 = _mm512_set_epi8( //
586
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
587
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0, //
588
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
589
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0); //
590
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x7F);
591
+ __m512i const subnorm_threshold_u8x64 = _mm512_set1_epi8(0x08);
592
+ __m512i const oct_threshold_20_u8x64 = _mm512_set1_epi8(0x20);
593
+ __m512i const oct_threshold_40_u8x64 = _mm512_set1_epi8(0x40);
594
+ __m512i const oct_threshold_60_u8x64 = _mm512_set1_epi8(0x60);
595
+
596
+ __m512i ab0_i32x16 = _mm512_setzero_si512(), ab1_i32x16 = _mm512_setzero_si512();
597
+ __m512i ab2_i32x16 = _mm512_setzero_si512(), ab3_i32x16 = _mm512_setzero_si512();
598
+ __m512i ab4_i32x16 = _mm512_setzero_si512(), ab5_i32x16 = _mm512_setzero_si512();
599
+ __m512i ab6_i32x16 = _mm512_setzero_si512();
600
+ __m512i a2_0_i32x16 = _mm512_setzero_si512(), a2_2_i32x16 = _mm512_setzero_si512();
601
+ __m512i a2_4_i32x16 = _mm512_setzero_si512(), a2_6_i32x16 = _mm512_setzero_si512();
602
+ __m512i b2_0_i32x16 = _mm512_setzero_si512(), b2_2_i32x16 = _mm512_setzero_si512();
603
+ __m512i b2_4_i32x16 = _mm512_setzero_si512(), b2_6_i32x16 = _mm512_setzero_si512();
604
+ __m512i a_e4m3_u8x64, b_e4m3_u8x64;
605
+
606
+ nk_sqeuclidean_e4m3_icelake_cycle:
607
+ if (n < 64) {
608
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
609
+ a_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
610
+ b_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
611
+ n = 0;
612
+ }
613
+ else {
614
+ a_e4m3_u8x64 = _mm512_loadu_si512(a);
615
+ b_e4m3_u8x64 = _mm512_loadu_si512(b);
616
+ a += 64, b += 64, n -= 64;
617
+ }
618
+
619
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e4m3_u8x64, magnitude_mask_u8x64);
620
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e4m3_u8x64, magnitude_mask_u8x64);
621
+ __m512i a_base_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_normal_u8x64);
622
+ __m512i b_base_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_normal_u8x64);
623
+ a_base_u8x64 = _mm512_mask_permutexvar_epi8(a_base_u8x64,
624
+ _mm512_cmplt_epu8_mask(a_magnitude_u8x64, subnorm_threshold_u8x64),
625
+ a_magnitude_u8x64, lut_subnorm_u8x64);
626
+ b_base_u8x64 = _mm512_mask_permutexvar_epi8(b_base_u8x64,
627
+ _mm512_cmplt_epu8_mask(b_magnitude_u8x64, subnorm_threshold_u8x64),
628
+ b_magnitude_u8x64, lut_subnorm_u8x64);
629
+
630
+ __m512i sign_diff_u8x64 = _mm512_ternarylogic_epi64(a_e4m3_u8x64, b_e4m3_u8x64, magnitude_mask_u8x64, 0x14);
631
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_base_u8x64, _mm512_test_epi8_mask(sign_diff_u8x64, sign_diff_u8x64),
632
+ _mm512_setzero_si512(), b_base_u8x64);
633
+
634
+ __mmask64 ka_lt20 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_20_u8x64);
635
+ __mmask64 ka_lt40 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_40_u8x64);
636
+ __mmask64 ka_lt60 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_60_u8x64);
637
+ __mmask64 kb_lt20 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_20_u8x64);
638
+ __mmask64 kb_lt40 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_40_u8x64);
639
+ __mmask64 kb_lt60 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_60_u8x64);
640
+
641
+ __m512i a0_u8x64 = _mm512_maskz_mov_epi8(ka_lt20, a_base_u8x64);
642
+ __m512i a1_u8x64 = _mm512_maskz_mov_epi8(ka_lt40 & ~ka_lt20, a_base_u8x64);
643
+ __m512i a2_u8x64 = _mm512_maskz_mov_epi8(ka_lt60 & ~ka_lt40, a_base_u8x64);
644
+ __m512i a3_u8x64 = _mm512_maskz_mov_epi8(~ka_lt60, a_base_u8x64);
645
+
646
+ __m512i b0_i8x64 = _mm512_maskz_mov_epi8(kb_lt20, b_signed_i8x64);
647
+ __m512i b1_i8x64 = _mm512_maskz_mov_epi8(kb_lt40 & ~kb_lt20, b_signed_i8x64);
648
+ __m512i b2_i8x64 = _mm512_maskz_mov_epi8(kb_lt60 & ~kb_lt40, b_signed_i8x64);
649
+ __m512i b3_i8x64 = _mm512_maskz_mov_epi8(~kb_lt60, b_signed_i8x64);
650
+
651
+ // dot(a,b): 16 VPDPBUSD
652
+ ab0_i32x16 = _mm512_dpbusd_epi32(ab0_i32x16, a0_u8x64, b0_i8x64);
653
+ ab1_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab1_i32x16, a0_u8x64, b1_i8x64), a1_u8x64, b0_i8x64);
654
+ ab2_i32x16 = _mm512_dpbusd_epi32(
655
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab2_i32x16, a0_u8x64, b2_i8x64), a1_u8x64, b1_i8x64), a2_u8x64,
656
+ b0_i8x64);
657
+ ab3_i32x16 = _mm512_dpbusd_epi32(
658
+ _mm512_dpbusd_epi32(
659
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab3_i32x16, a0_u8x64, b3_i8x64), a1_u8x64, b2_i8x64), a2_u8x64,
660
+ b1_i8x64),
661
+ a3_u8x64, b0_i8x64);
662
+ ab4_i32x16 = _mm512_dpbusd_epi32(
663
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab4_i32x16, a1_u8x64, b3_i8x64), a2_u8x64, b2_i8x64), a3_u8x64,
664
+ b1_i8x64);
665
+ ab5_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab5_i32x16, a2_u8x64, b3_i8x64), a3_u8x64, b2_i8x64);
666
+ ab6_i32x16 = _mm512_dpbusd_epi32(ab6_i32x16, a3_u8x64, b3_i8x64);
667
+
668
+ // ||a||²: 4 VPDPBUSD (self-dot, same-octave only)
669
+ a2_0_i32x16 = _mm512_dpbusd_epi32(a2_0_i32x16, a0_u8x64, a0_u8x64);
670
+ a2_2_i32x16 = _mm512_dpbusd_epi32(a2_2_i32x16, a1_u8x64, a1_u8x64);
671
+ a2_4_i32x16 = _mm512_dpbusd_epi32(a2_4_i32x16, a2_u8x64, a2_u8x64);
672
+ a2_6_i32x16 = _mm512_dpbusd_epi32(a2_6_i32x16, a3_u8x64, a3_u8x64);
673
+
674
+ // ||b||²: 4 VPDPBUSD (unsigned b, not signed)
675
+ __m512i b0_u8x64 = _mm512_maskz_mov_epi8(kb_lt20, b_base_u8x64);
676
+ __m512i b1_u8x64 = _mm512_maskz_mov_epi8(kb_lt40 & ~kb_lt20, b_base_u8x64);
677
+ __m512i b2_u8x64 = _mm512_maskz_mov_epi8(kb_lt60 & ~kb_lt40, b_base_u8x64);
678
+ __m512i b3_u8x64 = _mm512_maskz_mov_epi8(~kb_lt60, b_base_u8x64);
679
+ b2_0_i32x16 = _mm512_dpbusd_epi32(b2_0_i32x16, b0_u8x64, b0_u8x64);
680
+ b2_2_i32x16 = _mm512_dpbusd_epi32(b2_2_i32x16, b1_u8x64, b1_u8x64);
681
+ b2_4_i32x16 = _mm512_dpbusd_epi32(b2_4_i32x16, b2_u8x64, b2_u8x64);
682
+ b2_6_i32x16 = _mm512_dpbusd_epi32(b2_6_i32x16, b3_u8x64, b3_u8x64);
683
+
684
+ if (n) goto nk_sqeuclidean_e4m3_icelake_cycle;
685
+
686
+ // Reduce dot(a,b)
687
+ __m512 ab_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(ab0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
688
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab1_i32x16), _mm512_set1_ps(1.52587890625e-05f), ab_f32x16);
689
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab2_i32x16), _mm512_set1_ps(2.44140625e-04f), ab_f32x16);
690
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab3_i32x16), _mm512_set1_ps(3.90625e-03f), ab_f32x16);
691
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab4_i32x16), _mm512_set1_ps(6.25e-02f), ab_f32x16);
692
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab5_i32x16), _mm512_set1_ps(1.0f), ab_f32x16);
693
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab6_i32x16), _mm512_set1_ps(16.0f), ab_f32x16);
694
+
695
+ // Reduce ||a||² and ||b||² (even-k only: scale = 2^(8·oct − 20))
696
+ __m512 a2_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(a2_0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
697
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_2_i32x16), _mm512_set1_ps(2.44140625e-04f), a2_f32x16);
698
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_4_i32x16), _mm512_set1_ps(6.25e-02f), a2_f32x16);
699
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_6_i32x16), _mm512_set1_ps(16.0f), a2_f32x16);
700
+
701
+ __m512 b2_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(b2_0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
702
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_2_i32x16), _mm512_set1_ps(2.44140625e-04f), b2_f32x16);
703
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_4_i32x16), _mm512_set1_ps(6.25e-02f), b2_f32x16);
704
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_6_i32x16), _mm512_set1_ps(16.0f), b2_f32x16);
705
+
706
+ // (a-b)² = ||a||² + ||b||² - 2·dot(a,b)
707
+ __m512 sum_sq_f32x16 = _mm512_add_ps(a2_f32x16, b2_f32x16);
708
+ *result = nk_reduce_add_f32x16_skylake_(_mm512_fnmadd_ps(_mm512_set1_ps(2.0f), ab_f32x16, sum_sq_f32x16));
709
+ }
710
+
711
+ NK_PUBLIC void nk_euclidean_e4m3_icelake(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
712
+ nk_sqeuclidean_e4m3_icelake(a, b, n, result);
713
+ *result = nk_f32_sqrt_haswell(*result);
714
+ }
715
+
716
+ NK_PUBLIC void nk_angular_e4m3_icelake(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
717
+ // E4M3 angular distance via octave VNNI.
718
+
719
+ __m512i const lut_normal_u8x64 = _mm512_set_epi8( //
720
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
721
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8, //
722
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36, 32, //
723
+ 30, 28, 26, 24, 22, 20, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8); //
724
+ __m512i const lut_subnorm_u8x64 = _mm512_set_epi8( //
725
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
726
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0, //
727
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
728
+ 0, 0, 0, 0, 0, 0, 0, 0, 14, 12, 10, 8, 6, 4, 2, 0); //
729
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x7F);
730
+ __m512i const subnorm_threshold_u8x64 = _mm512_set1_epi8(0x08);
731
+ __m512i const oct_threshold_20_u8x64 = _mm512_set1_epi8(0x20);
732
+ __m512i const oct_threshold_40_u8x64 = _mm512_set1_epi8(0x40);
733
+ __m512i const oct_threshold_60_u8x64 = _mm512_set1_epi8(0x60);
734
+
735
+ __m512i ab0_i32x16 = _mm512_setzero_si512(), ab1_i32x16 = _mm512_setzero_si512();
736
+ __m512i ab2_i32x16 = _mm512_setzero_si512(), ab3_i32x16 = _mm512_setzero_si512();
737
+ __m512i ab4_i32x16 = _mm512_setzero_si512(), ab5_i32x16 = _mm512_setzero_si512();
738
+ __m512i ab6_i32x16 = _mm512_setzero_si512();
739
+ __m512i a2_0_i32x16 = _mm512_setzero_si512(), a2_2_i32x16 = _mm512_setzero_si512();
740
+ __m512i a2_4_i32x16 = _mm512_setzero_si512(), a2_6_i32x16 = _mm512_setzero_si512();
741
+ __m512i b2_0_i32x16 = _mm512_setzero_si512(), b2_2_i32x16 = _mm512_setzero_si512();
742
+ __m512i b2_4_i32x16 = _mm512_setzero_si512(), b2_6_i32x16 = _mm512_setzero_si512();
743
+ __m512i a_e4m3_u8x64, b_e4m3_u8x64;
744
+
745
+ nk_angular_e4m3_icelake_cycle:
746
+ if (n < 64) {
747
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
748
+ a_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
749
+ b_e4m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
750
+ n = 0;
751
+ }
752
+ else {
753
+ a_e4m3_u8x64 = _mm512_loadu_si512(a);
754
+ b_e4m3_u8x64 = _mm512_loadu_si512(b);
755
+ a += 64, b += 64, n -= 64;
756
+ }
757
+
758
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e4m3_u8x64, magnitude_mask_u8x64);
759
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e4m3_u8x64, magnitude_mask_u8x64);
760
+ __m512i a_base_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_normal_u8x64);
761
+ __m512i b_base_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_normal_u8x64);
762
+ a_base_u8x64 = _mm512_mask_permutexvar_epi8(a_base_u8x64,
763
+ _mm512_cmplt_epu8_mask(a_magnitude_u8x64, subnorm_threshold_u8x64),
764
+ a_magnitude_u8x64, lut_subnorm_u8x64);
765
+ b_base_u8x64 = _mm512_mask_permutexvar_epi8(b_base_u8x64,
766
+ _mm512_cmplt_epu8_mask(b_magnitude_u8x64, subnorm_threshold_u8x64),
767
+ b_magnitude_u8x64, lut_subnorm_u8x64);
768
+
769
+ __m512i sign_diff_u8x64 = _mm512_ternarylogic_epi64(a_e4m3_u8x64, b_e4m3_u8x64, magnitude_mask_u8x64, 0x14);
770
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_base_u8x64, _mm512_test_epi8_mask(sign_diff_u8x64, sign_diff_u8x64),
771
+ _mm512_setzero_si512(), b_base_u8x64);
772
+
773
+ __mmask64 ka_lt20 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_20_u8x64);
774
+ __mmask64 ka_lt40 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_40_u8x64);
775
+ __mmask64 ka_lt60 = _mm512_cmplt_epu8_mask(a_magnitude_u8x64, oct_threshold_60_u8x64);
776
+ __mmask64 kb_lt20 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_20_u8x64);
777
+ __mmask64 kb_lt40 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_40_u8x64);
778
+ __mmask64 kb_lt60 = _mm512_cmplt_epu8_mask(b_magnitude_u8x64, oct_threshold_60_u8x64);
779
+
780
+ __m512i a0_u8x64 = _mm512_maskz_mov_epi8(ka_lt20, a_base_u8x64);
781
+ __m512i a1_u8x64 = _mm512_maskz_mov_epi8(ka_lt40 & ~ka_lt20, a_base_u8x64);
782
+ __m512i a2_u8x64 = _mm512_maskz_mov_epi8(ka_lt60 & ~ka_lt40, a_base_u8x64);
783
+ __m512i a3_u8x64 = _mm512_maskz_mov_epi8(~ka_lt60, a_base_u8x64);
784
+
785
+ __m512i b0_i8x64 = _mm512_maskz_mov_epi8(kb_lt20, b_signed_i8x64);
786
+ __m512i b1_i8x64 = _mm512_maskz_mov_epi8(kb_lt40 & ~kb_lt20, b_signed_i8x64);
787
+ __m512i b2_i8x64 = _mm512_maskz_mov_epi8(kb_lt60 & ~kb_lt40, b_signed_i8x64);
788
+ __m512i b3_i8x64 = _mm512_maskz_mov_epi8(~kb_lt60, b_signed_i8x64);
789
+
790
+ // dot(a,b): 16 VPDPBUSD
791
+ ab0_i32x16 = _mm512_dpbusd_epi32(ab0_i32x16, a0_u8x64, b0_i8x64);
792
+ ab1_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab1_i32x16, a0_u8x64, b1_i8x64), a1_u8x64, b0_i8x64);
793
+ ab2_i32x16 = _mm512_dpbusd_epi32(
794
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab2_i32x16, a0_u8x64, b2_i8x64), a1_u8x64, b1_i8x64), a2_u8x64,
795
+ b0_i8x64);
796
+ ab3_i32x16 = _mm512_dpbusd_epi32(
797
+ _mm512_dpbusd_epi32(
798
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab3_i32x16, a0_u8x64, b3_i8x64), a1_u8x64, b2_i8x64), a2_u8x64,
799
+ b1_i8x64),
800
+ a3_u8x64, b0_i8x64);
801
+ ab4_i32x16 = _mm512_dpbusd_epi32(
802
+ _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab4_i32x16, a1_u8x64, b3_i8x64), a2_u8x64, b2_i8x64), a3_u8x64,
803
+ b1_i8x64);
804
+ ab5_i32x16 = _mm512_dpbusd_epi32(_mm512_dpbusd_epi32(ab5_i32x16, a2_u8x64, b3_i8x64), a3_u8x64, b2_i8x64);
805
+ ab6_i32x16 = _mm512_dpbusd_epi32(ab6_i32x16, a3_u8x64, b3_i8x64);
806
+
807
+ // ||a||²: 4 VPDPBUSD
808
+ a2_0_i32x16 = _mm512_dpbusd_epi32(a2_0_i32x16, a0_u8x64, a0_u8x64);
809
+ a2_2_i32x16 = _mm512_dpbusd_epi32(a2_2_i32x16, a1_u8x64, a1_u8x64);
810
+ a2_4_i32x16 = _mm512_dpbusd_epi32(a2_4_i32x16, a2_u8x64, a2_u8x64);
811
+ a2_6_i32x16 = _mm512_dpbusd_epi32(a2_6_i32x16, a3_u8x64, a3_u8x64);
812
+
813
+ // ||b||²: 4 VPDPBUSD (unsigned b)
814
+ __m512i b0_u8x64 = _mm512_maskz_mov_epi8(kb_lt20, b_base_u8x64);
815
+ __m512i b1_u8x64 = _mm512_maskz_mov_epi8(kb_lt40 & ~kb_lt20, b_base_u8x64);
816
+ __m512i b2_u8x64 = _mm512_maskz_mov_epi8(kb_lt60 & ~kb_lt40, b_base_u8x64);
817
+ __m512i b3_u8x64 = _mm512_maskz_mov_epi8(~kb_lt60, b_base_u8x64);
818
+ b2_0_i32x16 = _mm512_dpbusd_epi32(b2_0_i32x16, b0_u8x64, b0_u8x64);
819
+ b2_2_i32x16 = _mm512_dpbusd_epi32(b2_2_i32x16, b1_u8x64, b1_u8x64);
820
+ b2_4_i32x16 = _mm512_dpbusd_epi32(b2_4_i32x16, b2_u8x64, b2_u8x64);
821
+ b2_6_i32x16 = _mm512_dpbusd_epi32(b2_6_i32x16, b3_u8x64, b3_u8x64);
822
+
823
+ if (n) goto nk_angular_e4m3_icelake_cycle;
824
+
825
+ // Reduce dot(a,b)
826
+ __m512 ab_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(ab0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
827
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab1_i32x16), _mm512_set1_ps(1.52587890625e-05f), ab_f32x16);
828
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab2_i32x16), _mm512_set1_ps(2.44140625e-04f), ab_f32x16);
829
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab3_i32x16), _mm512_set1_ps(3.90625e-03f), ab_f32x16);
830
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab4_i32x16), _mm512_set1_ps(6.25e-02f), ab_f32x16);
831
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab5_i32x16), _mm512_set1_ps(1.0f), ab_f32x16);
832
+ ab_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(ab6_i32x16), _mm512_set1_ps(16.0f), ab_f32x16);
833
+
834
+ __m512 a2_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(a2_0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
835
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_2_i32x16), _mm512_set1_ps(2.44140625e-04f), a2_f32x16);
836
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_4_i32x16), _mm512_set1_ps(6.25e-02f), a2_f32x16);
837
+ a2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(a2_6_i32x16), _mm512_set1_ps(16.0f), a2_f32x16);
838
+
839
+ __m512 b2_f32x16 = _mm512_mul_ps(_mm512_cvtepi32_ps(b2_0_i32x16), _mm512_set1_ps(9.5367431640625e-07f));
840
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_2_i32x16), _mm512_set1_ps(2.44140625e-04f), b2_f32x16);
841
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_4_i32x16), _mm512_set1_ps(6.25e-02f), b2_f32x16);
842
+ b2_f32x16 = _mm512_fmadd_ps(_mm512_cvtepi32_ps(b2_6_i32x16), _mm512_set1_ps(16.0f), b2_f32x16);
843
+
844
+ nk_f32_t ab_f32 = nk_reduce_add_f32x16_skylake_(ab_f32x16);
845
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x16_skylake_(a2_f32x16);
846
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x16_skylake_(b2_f32x16);
847
+ *result = nk_angular_normalize_f32_haswell_(ab_f32, a_norm_sq_f32, b_norm_sq_f32);
848
+ }
849
+
850
+ NK_PUBLIC void nk_sqeuclidean_e2m3_icelake(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
851
+ // E2M3 squared Euclidean distance via VPDPBUSD integer MAC.
852
+ __m512i const lut_magnitude_u8x64 = _mm512_set_epi8(120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
853
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0,
854
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
855
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0);
856
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x1F);
857
+ __m512i const sign_mask_u8x64 = _mm512_set1_epi8(0x20);
858
+ __m512i ab_i32x16 = _mm512_setzero_si512();
859
+ __m512i a2_i32x16 = _mm512_setzero_si512();
860
+ __m512i b2_i32x16 = _mm512_setzero_si512();
861
+ __m512i a_e2m3_u8x64, b_e2m3_u8x64;
862
+
863
+ nk_sqeuclidean_e2m3_icelake_cycle:
864
+ if (n < 64) {
865
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
866
+ a_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
867
+ b_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
868
+ n = 0;
869
+ }
870
+ else {
871
+ a_e2m3_u8x64 = _mm512_loadu_si512(a);
872
+ b_e2m3_u8x64 = _mm512_loadu_si512(b);
873
+ a += 64, b += 64, n -= 64;
874
+ }
875
+
876
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e2m3_u8x64, magnitude_mask_u8x64);
877
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e2m3_u8x64, magnitude_mask_u8x64);
878
+ __m512i a_unsigned_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_magnitude_u8x64);
879
+ __m512i b_unsigned_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_magnitude_u8x64);
880
+
881
+ __m512i sign_combined_u8x64 = _mm512_and_si512(_mm512_xor_si512(a_e2m3_u8x64, b_e2m3_u8x64), sign_mask_u8x64);
882
+ __mmask64 negate_mask = _mm512_test_epi8_mask(sign_combined_u8x64, sign_combined_u8x64);
883
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_unsigned_u8x64, negate_mask, _mm512_setzero_si512(),
884
+ b_unsigned_u8x64);
885
+
886
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, a_unsigned_u8x64, b_signed_i8x64);
887
+ a2_i32x16 = _mm512_dpbusd_epi32(a2_i32x16, a_unsigned_u8x64, a_unsigned_u8x64);
888
+ b2_i32x16 = _mm512_dpbusd_epi32(b2_i32x16, b_unsigned_u8x64, b_unsigned_u8x64);
889
+
890
+ if (n) goto nk_sqeuclidean_e2m3_icelake_cycle;
891
+
892
+ // (a-b)² = a² + b² − 2·ab, scaled by 256 (16² from LUT)
893
+ __m512 a2_f32x16 = _mm512_cvtepi32_ps(a2_i32x16);
894
+ __m512 b2_f32x16 = _mm512_cvtepi32_ps(b2_i32x16);
895
+ __m512 ab_f32x16 = _mm512_cvtepi32_ps(ab_i32x16);
896
+ __m512 sum_sq_f32x16 = _mm512_add_ps(a2_f32x16, b2_f32x16);
897
+ *result = nk_reduce_add_f32x16_skylake_(_mm512_fnmadd_ps(_mm512_set1_ps(2.0f), ab_f32x16, sum_sq_f32x16)) / 256.0f;
898
+ }
899
+
900
+ NK_PUBLIC void nk_euclidean_e2m3_icelake(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
901
+ nk_sqeuclidean_e2m3_icelake(a, b, n, result);
902
+ *result = nk_f32_sqrt_haswell(*result);
903
+ }
904
+
905
+ NK_PUBLIC void nk_angular_e2m3_icelake(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
906
+ // E2M3 angular distance via VPDPBUSD integer MAC.
907
+ __m512i const lut_magnitude_u8x64 = _mm512_set_epi8(120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
908
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0,
909
+ 120, 112, 104, 96, 88, 80, 72, 64, 60, 56, 52, 48, 44, 40, 36,
910
+ 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0);
911
+ __m512i const magnitude_mask_u8x64 = _mm512_set1_epi8(0x1F);
912
+ __m512i const sign_mask_u8x64 = _mm512_set1_epi8(0x20);
913
+ __m512i ab_i32x16 = _mm512_setzero_si512();
914
+ __m512i a2_i32x16 = _mm512_setzero_si512();
915
+ __m512i b2_i32x16 = _mm512_setzero_si512();
916
+ __m512i a_e2m3_u8x64, b_e2m3_u8x64;
917
+
918
+ nk_angular_e2m3_icelake_cycle:
919
+ if (n < 64) {
920
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
921
+ a_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
922
+ b_e2m3_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
923
+ n = 0;
924
+ }
925
+ else {
926
+ a_e2m3_u8x64 = _mm512_loadu_si512(a);
927
+ b_e2m3_u8x64 = _mm512_loadu_si512(b);
928
+ a += 64, b += 64, n -= 64;
929
+ }
930
+
931
+ __m512i a_magnitude_u8x64 = _mm512_and_si512(a_e2m3_u8x64, magnitude_mask_u8x64);
932
+ __m512i b_magnitude_u8x64 = _mm512_and_si512(b_e2m3_u8x64, magnitude_mask_u8x64);
933
+ __m512i a_unsigned_u8x64 = _mm512_permutexvar_epi8(a_magnitude_u8x64, lut_magnitude_u8x64);
934
+ __m512i b_unsigned_u8x64 = _mm512_permutexvar_epi8(b_magnitude_u8x64, lut_magnitude_u8x64);
935
+
936
+ __m512i sign_combined_u8x64 = _mm512_and_si512(_mm512_xor_si512(a_e2m3_u8x64, b_e2m3_u8x64), sign_mask_u8x64);
937
+ __mmask64 negate_mask = _mm512_test_epi8_mask(sign_combined_u8x64, sign_combined_u8x64);
938
+ __m512i b_signed_i8x64 = _mm512_mask_sub_epi8(b_unsigned_u8x64, negate_mask, _mm512_setzero_si512(),
939
+ b_unsigned_u8x64);
940
+
941
+ ab_i32x16 = _mm512_dpbusd_epi32(ab_i32x16, a_unsigned_u8x64, b_signed_i8x64);
942
+ a2_i32x16 = _mm512_dpbusd_epi32(a2_i32x16, a_unsigned_u8x64, a_unsigned_u8x64);
943
+ b2_i32x16 = _mm512_dpbusd_epi32(b2_i32x16, b_unsigned_u8x64, b_unsigned_u8x64);
944
+
945
+ if (n) goto nk_angular_e2m3_icelake_cycle;
946
+
947
+ nk_f32_t ab_f32 = (nk_f32_t)_mm512_reduce_add_epi32(ab_i32x16) / 256.0f;
948
+ nk_f32_t a_norm_sq_f32 = (nk_f32_t)_mm512_reduce_add_epi32(a2_i32x16) / 256.0f;
949
+ nk_f32_t b_norm_sq_f32 = (nk_f32_t)_mm512_reduce_add_epi32(b2_i32x16) / 256.0f;
950
+ *result = nk_angular_normalize_f32_haswell_(ab_f32, a_norm_sq_f32, b_norm_sq_f32);
951
+ }
952
+
953
+ NK_PUBLIC void nk_sqeuclidean_e3m2_icelake(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
954
+ // E3M2 squared Euclidean distance via direct difference squaring.
955
+ __m512i const lut_magnitude_i16x32 = _mm512_set_epi16( //
956
+ 448, 384, 320, 256, 224, 192, 160, 128, 112, 96, 80, 64, 56, 48, 40, 32, //
957
+ 28, 24, 20, 16, 14, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1, 0);
958
+ __m512i const magnitude_mask_i16x32 = _mm512_set1_epi16(0x1F);
959
+ __m512i const sign_mask_i16x32 = _mm512_set1_epi16(0x20);
960
+ __m512i sum_i32x16 = _mm512_setzero_si512();
961
+ __m256i a_e3m2_u8x32, b_e3m2_u8x32;
962
+
963
+ nk_sqeuclidean_e3m2_icelake_cycle:
964
+ if (n < 32) {
965
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, (unsigned int)n);
966
+ a_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, a);
967
+ b_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, b);
968
+ n = 0;
969
+ }
970
+ else {
971
+ a_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)a);
972
+ b_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)b);
973
+ a += 32, b += 32, n -= 32;
974
+ }
975
+
976
+ __m512i a_u16x32 = _mm512_cvtepu8_epi16(a_e3m2_u8x32);
977
+ __m512i b_u16x32 = _mm512_cvtepu8_epi16(b_e3m2_u8x32);
978
+ __m512i a_unsigned_i16x32 = _mm512_permutexvar_epi16(_mm512_and_si512(a_u16x32, magnitude_mask_i16x32),
979
+ lut_magnitude_i16x32);
980
+ __m512i b_unsigned_i16x32 = _mm512_permutexvar_epi16(_mm512_and_si512(b_u16x32, magnitude_mask_i16x32),
981
+ lut_magnitude_i16x32);
982
+
983
+ // Apply signs individually
984
+ __mmask32 a_negative_mask = _mm512_test_epi16_mask(a_u16x32, sign_mask_i16x32);
985
+ __mmask32 b_negative_mask = _mm512_test_epi16_mask(b_u16x32, sign_mask_i16x32);
986
+ __m512i a_signed_i16x32 = _mm512_mask_sub_epi16(a_unsigned_i16x32, a_negative_mask, _mm512_setzero_si512(),
987
+ a_unsigned_i16x32);
988
+ __m512i b_signed_i16x32 = _mm512_mask_sub_epi16(b_unsigned_i16x32, b_negative_mask, _mm512_setzero_si512(),
989
+ b_unsigned_i16x32);
990
+
991
+ // Direct difference squaring: (a-b)² via VPMADDWD
992
+ __m512i diff_i16x32 = _mm512_sub_epi16(a_signed_i16x32, b_signed_i16x32);
993
+ sum_i32x16 = _mm512_add_epi32(sum_i32x16, _mm512_madd_epi16(diff_i16x32, diff_i16x32));
994
+
995
+ if (n) goto nk_sqeuclidean_e3m2_icelake_cycle;
996
+ *result = (nk_f32_t)_mm512_reduce_add_epi32(sum_i32x16) / 256.0f;
997
+ }
998
+
999
+ NK_PUBLIC void nk_euclidean_e3m2_icelake(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
1000
+ nk_sqeuclidean_e3m2_icelake(a, b, n, result);
1001
+ *result = nk_f32_sqrt_haswell(*result);
1002
+ }
1003
+
1004
+ NK_PUBLIC void nk_angular_e3m2_icelake(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
1005
+ // E3M2 angular distance via VPMADDWD integer MAC.
1006
+ __m512i const lut_magnitude_i16x32 = _mm512_set_epi16( //
1007
+ 448, 384, 320, 256, 224, 192, 160, 128, 112, 96, 80, 64, 56, 48, 40, 32, //
1008
+ 28, 24, 20, 16, 14, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1, 0);
1009
+ __m512i const magnitude_mask_i16x32 = _mm512_set1_epi16(0x1F);
1010
+ __m512i const sign_mask_i16x32 = _mm512_set1_epi16(0x20);
1011
+ __m512i ab_i32x16 = _mm512_setzero_si512();
1012
+ __m512i a2_i32x16 = _mm512_setzero_si512();
1013
+ __m512i b2_i32x16 = _mm512_setzero_si512();
1014
+ __m256i a_e3m2_u8x32, b_e3m2_u8x32;
1015
+
1016
+ nk_angular_e3m2_icelake_cycle:
1017
+ if (n < 32) {
1018
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, (unsigned int)n);
1019
+ a_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, a);
1020
+ b_e3m2_u8x32 = _mm256_maskz_loadu_epi8(mask, b);
1021
+ n = 0;
1022
+ }
1023
+ else {
1024
+ a_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)a);
1025
+ b_e3m2_u8x32 = _mm256_loadu_si256((__m256i const *)b);
1026
+ a += 32, b += 32, n -= 32;
1027
+ }
1028
+
1029
+ __m512i a_u16x32 = _mm512_cvtepu8_epi16(a_e3m2_u8x32);
1030
+ __m512i b_u16x32 = _mm512_cvtepu8_epi16(b_e3m2_u8x32);
1031
+ __m512i a_unsigned_i16x32 = _mm512_permutexvar_epi16(_mm512_and_si512(a_u16x32, magnitude_mask_i16x32),
1032
+ lut_magnitude_i16x32);
1033
+ __m512i b_unsigned_i16x32 = _mm512_permutexvar_epi16(_mm512_and_si512(b_u16x32, magnitude_mask_i16x32),
1034
+ lut_magnitude_i16x32);
1035
+
1036
+ __mmask32 a_negative_mask = _mm512_test_epi16_mask(a_u16x32, sign_mask_i16x32);
1037
+ __mmask32 b_negative_mask = _mm512_test_epi16_mask(b_u16x32, sign_mask_i16x32);
1038
+ __m512i a_signed_i16x32 = _mm512_mask_sub_epi16(a_unsigned_i16x32, a_negative_mask, _mm512_setzero_si512(),
1039
+ a_unsigned_i16x32);
1040
+ __m512i b_signed_i16x32 = _mm512_mask_sub_epi16(b_unsigned_i16x32, b_negative_mask, _mm512_setzero_si512(),
1041
+ b_unsigned_i16x32);
1042
+
1043
+ ab_i32x16 = _mm512_add_epi32(ab_i32x16, _mm512_madd_epi16(a_signed_i16x32, b_signed_i16x32));
1044
+ a2_i32x16 = _mm512_add_epi32(a2_i32x16, _mm512_madd_epi16(a_unsigned_i16x32, a_unsigned_i16x32));
1045
+ b2_i32x16 = _mm512_add_epi32(b2_i32x16, _mm512_madd_epi16(b_unsigned_i16x32, b_unsigned_i16x32));
1046
+
1047
+ if (n) goto nk_angular_e3m2_icelake_cycle;
1048
+
1049
+ nk_f32_t ab_f32 = (nk_f32_t)_mm512_reduce_add_epi32(ab_i32x16) / 256.0f;
1050
+ nk_f32_t a_norm_sq_f32 = (nk_f32_t)_mm512_reduce_add_epi32(a2_i32x16) / 256.0f;
1051
+ nk_f32_t b_norm_sq_f32 = (nk_f32_t)_mm512_reduce_add_epi32(b2_i32x16) / 256.0f;
1052
+ *result = nk_angular_normalize_f32_haswell_(ab_f32, a_norm_sq_f32, b_norm_sq_f32);
1053
+ }
1054
+
1055
+ #if defined(__clang__)
1056
+ #pragma clang attribute pop
1057
+ #elif defined(__GNUC__)
1058
+ #pragma GCC pop_options
1059
+ #endif
1060
+
1061
+ #if defined(__cplusplus)
1062
+ } // extern "C"
1063
+ #endif
1064
+
1065
+ #endif // NK_TARGET_ICELAKE
1066
+ #endif // NK_TARGET_X8664_
1067
+ #endif // NK_SPATIAL_ICELAKE_H