usearch 2.21.4 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +224 -48
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,979 @@
1
+ /**
2
+ * @brief SIMD-accelerated Spatial Similarity Measures for Haswell.
3
+ * @file include/numkong/spatial/haswell.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/spatial.h
8
+ *
9
+ * @section spatial_haswell_instructions Key AVX2 Spatial Instructions
10
+ *
11
+ * Intrinsic Instruction Haswell Genoa
12
+ * _mm256_fmadd_ps VFMADD (YMM, YMM, YMM) 5cy @ p01 4cy @ p01
13
+ * _mm256_mul_ps VMULPS (YMM, YMM, YMM) 5cy @ p01 3cy @ p01
14
+ * _mm256_add_ps VADDPS (YMM, YMM, YMM) 3cy @ p01 3cy @ p23
15
+ * _mm256_sub_ps VSUBPS (YMM, YMM, YMM) 3cy @ p01 3cy @ p23
16
+ * _mm_rsqrt_ps VRSQRTPS (XMM, XMM) 5cy @ p0 4cy @ p01
17
+ * _mm_sqrt_ps VSQRTPS (XMM, XMM) 11cy @ p0 15cy @ p01
18
+ * _mm256_sqrt_ps VSQRTPS (YMM, YMM) 19cy @ p0 15cy @ p01
19
+ *
20
+ * For angular distance normalization, `_mm_rsqrt_ps` provides ~12-bit precision (1.5 x 2⁻¹² error).
21
+ * Newton-Raphson refinement doubles precision to ~22-24 bits, sufficient for f32. For f64 we use
22
+ * the exact `_mm_sqrt_pd` instruction since fast rsqrt approximations lack f64 precision.
23
+ */
24
+ #ifndef NK_SPATIAL_HASWELL_H
25
+ #define NK_SPATIAL_HASWELL_H
26
+
27
+ #if NK_TARGET_X8664_
28
+ #if NK_TARGET_HASWELL
29
+
30
+ #include "numkong/types.h"
31
+ #include "numkong/scalar/haswell.h" // `nk_f32_sqrt_haswell`
32
+ #include "numkong/dot/haswell.h" // `nk_dot_f32x4_state_haswell_t`
33
+ #include "numkong/reduce/haswell.h" // `nk_reduce_add_f32x8_haswell_`
34
+
35
+ #if defined(__cplusplus)
36
+ extern "C" {
37
+ #endif
38
+
39
+ #if defined(__clang__)
40
+ #pragma clang attribute push(__attribute__((target("avx2,f16c,fma,bmi,bmi2"))), apply_to = function)
41
+ #elif defined(__GNUC__)
42
+ #pragma GCC push_options
43
+ #pragma GCC target("avx2", "f16c", "fma", "bmi", "bmi2")
44
+ #endif
45
+
46
+ /** @brief Reciprocal square root of 4 floats with Newton-Raphson refinement. */
47
+ NK_INTERNAL __m128 nk_rsqrt_f32x4_haswell_(__m128 x) {
48
+ __m128 rsqrt_f32x4 = _mm_rsqrt_ps(x);
49
+ __m128 nr_f32x4 = _mm_mul_ps(_mm_mul_ps(x, rsqrt_f32x4), rsqrt_f32x4);
50
+ nr_f32x4 = _mm_sub_ps(_mm_set1_ps(3.0f), nr_f32x4);
51
+ return _mm_mul_ps(_mm_mul_ps(_mm_set1_ps(0.5f), rsqrt_f32x4), nr_f32x4);
52
+ }
53
+
54
+ /** @brief Safe square root of 4 floats with zero-clamping for numerical stability. */
55
+ NK_INTERNAL __m128 nk_sqrt_f32x4_haswell_(__m128 x) { return _mm_sqrt_ps(_mm_max_ps(x, _mm_setzero_ps())); }
56
+
57
+ /** @brief Angular from_dot: computes 1 − dot × rsqrt(query_sumsq × target_sumsq) for 4 pairs. */
58
+ NK_INTERNAL void nk_angular_through_f32_from_dot_haswell_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
59
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
60
+ __m128 dots_f32x4 = dots.xmm_ps;
61
+ __m128 query_sumsq_f32x4 = _mm_set1_ps(query_sumsq);
62
+ __m128 products_f32x4 = _mm_mul_ps(query_sumsq_f32x4, target_sumsqs.xmm_ps);
63
+ __m128 rsqrt_f32x4 = nk_rsqrt_f32x4_haswell_(products_f32x4);
64
+ __m128 normalized_f32x4 = _mm_mul_ps(dots_f32x4, rsqrt_f32x4);
65
+ __m128 angular_f32x4 = _mm_sub_ps(_mm_set1_ps(1.0f), normalized_f32x4);
66
+ results->xmm_ps = _mm_max_ps(angular_f32x4, _mm_setzero_ps());
67
+ }
68
+
69
+ /** @brief Euclidean from_dot: computes √(query_sumsq + target_sumsq − 2 × dot) for 4 pairs. */
70
+ NK_INTERNAL void nk_euclidean_through_f32_from_dot_haswell_(nk_b128_vec_t dots, nk_f32_t query_sumsq,
71
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
72
+ __m128 dots_f32x4 = dots.xmm_ps;
73
+ __m128 query_sumsq_f32x4 = _mm_set1_ps(query_sumsq);
74
+ __m128 sum_sq_f32x4 = _mm_add_ps(query_sumsq_f32x4, target_sumsqs.xmm_ps);
75
+ __m128 dist_sq_f32x4 = _mm_fnmadd_ps(_mm_set1_ps(2.0f), dots_f32x4, sum_sq_f32x4);
76
+ results->xmm_ps = nk_sqrt_f32x4_haswell_(dist_sq_f32x4);
77
+ }
78
+
79
+ /** @brief Angular from_dot for native f64: 1 − dot / √(query_sumsq × target_sumsq) for 4 pairs. */
80
+ NK_INTERNAL void nk_angular_through_f64_from_dot_haswell_(nk_b256_vec_t dots, nk_f64_t query_sumsq,
81
+ nk_b256_vec_t target_sumsqs, nk_b256_vec_t *results) {
82
+ __m256d dots_f64x4 = dots.ymm_pd;
83
+ __m256d query_sumsq_f64x4 = _mm256_set1_pd(query_sumsq);
84
+ __m256d products_f64x4 = _mm256_mul_pd(query_sumsq_f64x4, target_sumsqs.ymm_pd);
85
+ __m256d sqrt_products_f64x4 = _mm256_sqrt_pd(products_f64x4);
86
+ __m256d normalized_f64x4 = _mm256_div_pd(dots_f64x4, sqrt_products_f64x4);
87
+ __m256d angular_f64x4 = _mm256_sub_pd(_mm256_set1_pd(1.0), normalized_f64x4);
88
+ results->ymm_pd = _mm256_max_pd(angular_f64x4, _mm256_setzero_pd());
89
+ }
90
+
91
+ /** @brief Euclidean from_dot for native f64: √(query_sumsq + target_sumsq − 2 × dot) for 4 pairs. */
92
+ NK_INTERNAL void nk_euclidean_through_f64_from_dot_haswell_(nk_b256_vec_t dots, nk_f64_t query_sumsq,
93
+ nk_b256_vec_t target_sumsqs, nk_b256_vec_t *results) {
94
+ __m256d dots_f64x4 = dots.ymm_pd;
95
+ __m256d query_sumsq_f64x4 = _mm256_set1_pd(query_sumsq);
96
+ __m256d sum_sq_f64x4 = _mm256_add_pd(query_sumsq_f64x4, target_sumsqs.ymm_pd);
97
+ __m256d dist_sq_f64x4 = _mm256_fnmadd_pd(_mm256_set1_pd(2.0), dots_f64x4, sum_sq_f64x4);
98
+ results->ymm_pd = _mm256_sqrt_pd(_mm256_max_pd(dist_sq_f64x4, _mm256_setzero_pd()));
99
+ }
100
+
101
+ /** @brief Angular from_dot for i32 accumulators: cast to f32, rsqrt+NR, clamp. 4 pairs. */
102
+ NK_INTERNAL void nk_angular_through_i32_from_dot_haswell_(nk_b128_vec_t dots, nk_i32_t query_sumsq,
103
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
104
+ __m128 dots_f32x4 = _mm_cvtepi32_ps(dots.xmm);
105
+ __m128 query_sumsq_f32x4 = _mm_set1_ps((nk_f32_t)query_sumsq);
106
+ __m128 products_f32x4 = _mm_mul_ps(query_sumsq_f32x4, _mm_cvtepi32_ps(target_sumsqs.xmm));
107
+ __m128 rsqrt_f32x4 = nk_rsqrt_f32x4_haswell_(products_f32x4);
108
+ __m128 normalized_f32x4 = _mm_mul_ps(dots_f32x4, rsqrt_f32x4);
109
+ __m128 angular_f32x4 = _mm_sub_ps(_mm_set1_ps(1.0f), normalized_f32x4);
110
+ results->xmm_ps = _mm_max_ps(angular_f32x4, _mm_setzero_ps());
111
+ }
112
+
113
+ /** @brief Euclidean from_dot for i32 accumulators: cast to f32, then √(a² + b² − 2ab). 4 pairs. */
114
+ NK_INTERNAL void nk_euclidean_through_i32_from_dot_haswell_(nk_b128_vec_t dots, nk_i32_t query_sumsq,
115
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
116
+ __m128 dots_f32x4 = _mm_cvtepi32_ps(dots.xmm);
117
+ __m128 query_sumsq_f32x4 = _mm_set1_ps((nk_f32_t)query_sumsq);
118
+ __m128 sum_sq_f32x4 = _mm_add_ps(query_sumsq_f32x4, _mm_cvtepi32_ps(target_sumsqs.xmm));
119
+ __m128 dist_sq_f32x4 = _mm_fnmadd_ps(_mm_set1_ps(2.0f), dots_f32x4, sum_sq_f32x4);
120
+ results->xmm_ps = nk_sqrt_f32x4_haswell_(dist_sq_f32x4);
121
+ }
122
+
123
+ /** @brief Angular from_dot for u32 accumulators: cast to f32, rsqrt+NR, clamp. 4 pairs. */
124
+ NK_INTERNAL void nk_angular_through_u32_from_dot_haswell_(nk_b128_vec_t dots, nk_u32_t query_sumsq,
125
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
126
+ __m128 dots_f32x4 = _mm_cvtepi32_ps(dots.xmm);
127
+ __m128 query_sumsq_f32x4 = _mm_set1_ps((nk_f32_t)query_sumsq);
128
+ __m128 products_f32x4 = _mm_mul_ps(query_sumsq_f32x4, _mm_cvtepi32_ps(target_sumsqs.xmm));
129
+ __m128 rsqrt_f32x4 = nk_rsqrt_f32x4_haswell_(products_f32x4);
130
+ __m128 normalized_f32x4 = _mm_mul_ps(dots_f32x4, rsqrt_f32x4);
131
+ __m128 angular_f32x4 = _mm_sub_ps(_mm_set1_ps(1.0f), normalized_f32x4);
132
+ results->xmm_ps = _mm_max_ps(angular_f32x4, _mm_setzero_ps());
133
+ }
134
+
135
+ /** @brief Euclidean from_dot for u32 accumulators: cast to f32, then √(a² + b² − 2ab). 4 pairs. */
136
+ NK_INTERNAL void nk_euclidean_through_u32_from_dot_haswell_(nk_b128_vec_t dots, nk_u32_t query_sumsq,
137
+ nk_b128_vec_t target_sumsqs, nk_b128_vec_t *results) {
138
+ __m128 dots_f32x4 = _mm_cvtepi32_ps(dots.xmm);
139
+ __m128 query_sumsq_f32x4 = _mm_set1_ps((nk_f32_t)query_sumsq);
140
+ __m128 sum_sq_f32x4 = _mm_add_ps(query_sumsq_f32x4, _mm_cvtepi32_ps(target_sumsqs.xmm));
141
+ __m128 dist_sq_f32x4 = _mm_fnmadd_ps(_mm_set1_ps(2.0f), dots_f32x4, sum_sq_f32x4);
142
+ results->xmm_ps = nk_sqrt_f32x4_haswell_(dist_sq_f32x4);
143
+ }
144
+
145
+ NK_INTERNAL nk_f64_t nk_angular_normalize_f64_haswell_(nk_f64_t ab, nk_f64_t a2, nk_f64_t b2) {
146
+
147
+ // If both vectors have magnitude 0, the distance is 0.
148
+ if (a2 == 0 && b2 == 0) return 0;
149
+ // If any one of the vectors is 0, the square root of the product is 0,
150
+ // the division is illformed, and the result is 1.
151
+ else if (ab == 0) return 1;
152
+
153
+ // Design note: We use exact `_mm_sqrt_pd` instead of fast rsqrt approximation.
154
+ // The f32 `_mm_rsqrt_ps` has max relative error of 1.5 × 2⁻¹² (~11 bits precision).
155
+ // Even with Newton-Raphson refinement (doubles precision to ~22-24 bits), this is
156
+ // insufficient for f64's 52-bit mantissa, causing ULP errors in the hundreds of millions.
157
+ // The `_mm_sqrt_pd` instruction has ~13 cycle latency but provides full f64 precision.
158
+ // https://web.archive.org/web/20210208132927/http://assemblyrequired.crashworks.org/timing-square-root/
159
+ __m128d squares_f64x2 = _mm_set_pd(a2, b2);
160
+ __m128d sqrts_f64x2 = _mm_sqrt_pd(squares_f64x2);
161
+ nk_f64_t a_sqrt = _mm_cvtsd_f64(_mm_unpackhi_pd(sqrts_f64x2, sqrts_f64x2));
162
+ nk_f64_t b_sqrt = _mm_cvtsd_f64(sqrts_f64x2);
163
+ nk_f64_t result = 1 - ab / (a_sqrt * b_sqrt);
164
+ return result > 0 ? result : 0;
165
+ }
166
+
167
+ NK_INTERNAL nk_f32_t nk_angular_normalize_f32_haswell_(nk_f32_t ab, nk_f32_t a2, nk_f32_t b2) {
168
+
169
+ // If both vectors have magnitude 0, the distance is 0.
170
+ if (a2 == 0.0f && b2 == 0.0f) return 0.0f;
171
+ // If any one of the vectors is 0, the square root of the product is 0,
172
+ // the division is illformed, and the result is 1.
173
+ else if (ab == 0.0f) return 1.0f;
174
+
175
+ // Load the squares into an __m128 register for single-precision floating-point operations
176
+ __m128 squares_f32x4 = _mm_set_ps(a2, b2, a2, b2); // We replicate to make use of full register
177
+
178
+ // Compute the reciprocal square root of the squares using `_mm_rsqrt_ps` (single-precision)
179
+ __m128 rsqrts_f32x4 = _mm_rsqrt_ps(squares_f32x4);
180
+
181
+ // Perform one iteration of Newton-Raphson refinement to improve the precision of rsqrt:
182
+ // Formula: y' = y × (1.5 - 0.5 × x × y × y)
183
+ __m128 half_f32x4 = _mm_set1_ps(0.5f);
184
+ __m128 three_halves_f32x4 = _mm_set1_ps(1.5f);
185
+ rsqrts_f32x4 = _mm_mul_ps(
186
+ rsqrts_f32x4,
187
+ _mm_sub_ps(three_halves_f32x4,
188
+ _mm_mul_ps(half_f32x4, _mm_mul_ps(squares_f32x4, _mm_mul_ps(rsqrts_f32x4, rsqrts_f32x4)))));
189
+
190
+ // Extract the reciprocal square roots of a2 and b2 from the __m128 register
191
+ nk_f32_t a2_reciprocal = _mm_cvtss_f32(_mm_shuffle_ps(rsqrts_f32x4, rsqrts_f32x4, _MM_SHUFFLE(0, 0, 0, 1)));
192
+ nk_f32_t b2_reciprocal = _mm_cvtss_f32(rsqrts_f32x4);
193
+
194
+ // Calculate the angular distance: 1 - dot_product × a2_reciprocal × b2_reciprocal
195
+ nk_f32_t result = 1.0f - ab * a2_reciprocal * b2_reciprocal;
196
+ return result > 0 ? result : 0;
197
+ }
198
+
199
+ #pragma region F16 and BF16 Floats
200
+
201
+ NK_PUBLIC void nk_sqeuclidean_f16_haswell(nk_f16_t const *a, nk_f16_t const *b, nk_size_t n, nk_f32_t *result) {
202
+ __m256 a_f32x8, b_f32x8;
203
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
204
+
205
+ nk_sqeuclidean_f16_haswell_cycle:
206
+ if (n < 8) {
207
+ nk_b256_vec_t a_vec, b_vec;
208
+ nk_partial_load_f16x8_to_f32x8_haswell_(a, &a_vec, n);
209
+ nk_partial_load_f16x8_to_f32x8_haswell_(b, &b_vec, n);
210
+ a_f32x8 = a_vec.ymm_ps;
211
+ b_f32x8 = b_vec.ymm_ps;
212
+ n = 0;
213
+ }
214
+ else {
215
+ a_f32x8 = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const *)a));
216
+ b_f32x8 = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const *)b));
217
+ n -= 8, a += 8, b += 8;
218
+ }
219
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
220
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
221
+ if (n) goto nk_sqeuclidean_f16_haswell_cycle;
222
+
223
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
224
+ }
225
+
226
+ NK_PUBLIC void nk_euclidean_f16_haswell(nk_f16_t const *a, nk_f16_t const *b, nk_size_t n, nk_f32_t *result) {
227
+ nk_sqeuclidean_f16_haswell(a, b, n, result);
228
+ *result = nk_f32_sqrt_haswell(*result);
229
+ }
230
+
231
+ NK_PUBLIC void nk_angular_f16_haswell(nk_f16_t const *a, nk_f16_t const *b, nk_size_t n, nk_f32_t *result) {
232
+ __m256 a_f32x8, b_f32x8;
233
+ __m256 dot_product_f32x8 = _mm256_setzero_ps(), a_norm_sq_f32x8 = _mm256_setzero_ps(),
234
+ b_norm_sq_f32x8 = _mm256_setzero_ps();
235
+
236
+ nk_angular_f16_haswell_cycle:
237
+ if (n < 8) {
238
+ nk_b256_vec_t a_vec, b_vec;
239
+ nk_partial_load_f16x8_to_f32x8_haswell_(a, &a_vec, n);
240
+ nk_partial_load_f16x8_to_f32x8_haswell_(b, &b_vec, n);
241
+ a_f32x8 = a_vec.ymm_ps;
242
+ b_f32x8 = b_vec.ymm_ps;
243
+ n = 0;
244
+ }
245
+ else {
246
+ a_f32x8 = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const *)a));
247
+ b_f32x8 = _mm256_cvtph_ps(_mm_loadu_si128((__m128i const *)b));
248
+ n -= 8, a += 8, b += 8;
249
+ }
250
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
251
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
252
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
253
+ if (n) goto nk_angular_f16_haswell_cycle;
254
+
255
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
256
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
257
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
258
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
259
+ }
260
+
261
+ NK_PUBLIC void nk_sqeuclidean_bf16_haswell(nk_bf16_t const *a, nk_bf16_t const *b, nk_size_t n, nk_f32_t *result) {
262
+ __m256i a_bf16_i16x16, b_bf16_i16x16;
263
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
264
+ __m256i mask_high_u32x8 = _mm256_set1_epi32((int)0xFFFF0000);
265
+
266
+ nk_sqeuclidean_bf16_haswell_cycle:
267
+ if (n < 16) {
268
+ nk_b256_vec_t a_vec, b_vec;
269
+ nk_partial_load_b16x16_serial_(a, &a_vec, n);
270
+ nk_partial_load_b16x16_serial_(b, &b_vec, n);
271
+ a_bf16_i16x16 = a_vec.ymm;
272
+ b_bf16_i16x16 = b_vec.ymm;
273
+ n = 0;
274
+ }
275
+ else {
276
+ a_bf16_i16x16 = _mm256_loadu_si256((__m256i const *)a);
277
+ b_bf16_i16x16 = _mm256_loadu_si256((__m256i const *)b);
278
+ n -= 16, a += 16, b += 16;
279
+ }
280
+ __m256 a_even_f32x8 = _mm256_castsi256_ps(_mm256_slli_epi32(a_bf16_i16x16, 16));
281
+ __m256 b_even_f32x8 = _mm256_castsi256_ps(_mm256_slli_epi32(b_bf16_i16x16, 16));
282
+ __m256 diff_even_f32x8 = _mm256_sub_ps(a_even_f32x8, b_even_f32x8);
283
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_even_f32x8, diff_even_f32x8, distance_sq_f32x8);
284
+ __m256 a_odd_f32x8 = _mm256_castsi256_ps(_mm256_and_si256(a_bf16_i16x16, mask_high_u32x8));
285
+ __m256 b_odd_f32x8 = _mm256_castsi256_ps(_mm256_and_si256(b_bf16_i16x16, mask_high_u32x8));
286
+ __m256 diff_odd_f32x8 = _mm256_sub_ps(a_odd_f32x8, b_odd_f32x8);
287
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_odd_f32x8, diff_odd_f32x8, distance_sq_f32x8);
288
+ if (n) goto nk_sqeuclidean_bf16_haswell_cycle;
289
+
290
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
291
+ }
292
+
293
+ NK_PUBLIC void nk_euclidean_bf16_haswell(nk_bf16_t const *a, nk_bf16_t const *b, nk_size_t n, nk_f32_t *result) {
294
+ nk_sqeuclidean_bf16_haswell(a, b, n, result);
295
+ *result = nk_f32_sqrt_haswell(*result);
296
+ }
297
+
298
+ NK_PUBLIC void nk_angular_bf16_haswell(nk_bf16_t const *a, nk_bf16_t const *b, nk_size_t n, nk_f32_t *result) {
299
+ __m256i a_bf16_i16x16, b_bf16_i16x16;
300
+ __m256 dot_product_f32x8 = _mm256_setzero_ps(), a_norm_sq_f32x8 = _mm256_setzero_ps(),
301
+ b_norm_sq_f32x8 = _mm256_setzero_ps();
302
+ __m256i mask_high_u32x8 = _mm256_set1_epi32((int)0xFFFF0000);
303
+
304
+ nk_angular_bf16_haswell_cycle:
305
+ if (n < 16) {
306
+ nk_b256_vec_t a_vec, b_vec;
307
+ nk_partial_load_b16x16_serial_(a, &a_vec, n);
308
+ nk_partial_load_b16x16_serial_(b, &b_vec, n);
309
+ a_bf16_i16x16 = a_vec.ymm;
310
+ b_bf16_i16x16 = b_vec.ymm;
311
+ n = 0;
312
+ }
313
+ else {
314
+ a_bf16_i16x16 = _mm256_loadu_si256((__m256i const *)a);
315
+ b_bf16_i16x16 = _mm256_loadu_si256((__m256i const *)b);
316
+ n -= 16, a += 16, b += 16;
317
+ }
318
+ __m256 a_even_f32x8 = _mm256_castsi256_ps(_mm256_slli_epi32(a_bf16_i16x16, 16));
319
+ __m256 b_even_f32x8 = _mm256_castsi256_ps(_mm256_slli_epi32(b_bf16_i16x16, 16));
320
+ dot_product_f32x8 = _mm256_fmadd_ps(a_even_f32x8, b_even_f32x8, dot_product_f32x8);
321
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_even_f32x8, a_even_f32x8, a_norm_sq_f32x8);
322
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_even_f32x8, b_even_f32x8, b_norm_sq_f32x8);
323
+ __m256 a_odd_f32x8 = _mm256_castsi256_ps(_mm256_and_si256(a_bf16_i16x16, mask_high_u32x8));
324
+ __m256 b_odd_f32x8 = _mm256_castsi256_ps(_mm256_and_si256(b_bf16_i16x16, mask_high_u32x8));
325
+ dot_product_f32x8 = _mm256_fmadd_ps(a_odd_f32x8, b_odd_f32x8, dot_product_f32x8);
326
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_odd_f32x8, a_odd_f32x8, a_norm_sq_f32x8);
327
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_odd_f32x8, b_odd_f32x8, b_norm_sq_f32x8);
328
+ if (n) goto nk_angular_bf16_haswell_cycle;
329
+
330
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
331
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
332
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
333
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
334
+ }
335
+
336
+ #pragma endregion F16 and BF16 Floats
337
+ #pragma region I8 and U8 Integers
338
+
339
+ NK_PUBLIC void nk_sqeuclidean_i8_haswell(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_u32_t *result) {
340
+ // Optimized i8 L2-squared using saturating subtract + VPMADDWD
341
+ //
342
+ // Approach:
343
+ // - XOR with 0x80 to reinterpret signed i8 as unsigned u8
344
+ // - Compute |a-b| using unsigned saturating subtraction: diff = (a ⊖ b) | (b ⊖ a)
345
+ // - Zero-extend u8→u16 using unpacking (1cy latency @ p5)
346
+ // - Square using vpmaddwd on u16 values (32 elements/iteration)
347
+ //
348
+ // The XOR bias is needed because subs_epu8 (unsigned) saturates to 0 when the result
349
+ // would be negative, so OR-ing both directions gives the true |a-b|.
350
+ // A naive subs_epi8 (signed) would saturate to -128, corrupting the OR trick.
351
+ //
352
+ // Correctness: For squared distance, |a-b|² = (a-b)², so unsigned absolute differences are valid.
353
+ // The XOR preserves distances: |a-b| = |(a^0x80) - (b^0x80)|.
354
+ //
355
+ __m256i distance_sq_low_i32x8 = _mm256_setzero_si256();
356
+ __m256i distance_sq_high_i32x8 = _mm256_setzero_si256();
357
+ __m256i const zeros_i8x32 = _mm256_setzero_si256();
358
+ __m256i const bias_i8x32 = _mm256_set1_epi8((char)0x80);
359
+ __m256i diff_low_i16x16, diff_high_i16x16;
360
+ __m256i a_i8x32, b_i8x32, diff_u8x32;
361
+
362
+ // Process 32 elements per iteration with 256-bit loads
363
+ nk_size_t i = 0;
364
+ for (; i + 32 <= n; i += 32) {
365
+ a_i8x32 = _mm256_loadu_si256((__m256i const *)(a + i));
366
+ b_i8x32 = _mm256_loadu_si256((__m256i const *)(b + i));
367
+
368
+ // Reinterpret signed i8 as unsigned u8 by flipping the sign bit
369
+ a_i8x32 = _mm256_xor_si256(a_i8x32, bias_i8x32);
370
+ b_i8x32 = _mm256_xor_si256(b_i8x32, bias_i8x32);
371
+
372
+ // Compute |a-b| using unsigned saturating subtraction
373
+ // subs_epu8 saturates to 0 if result would be negative
374
+ // OR-ing both directions gives absolute difference as unsigned
375
+ diff_u8x32 = _mm256_or_si256(_mm256_subs_epu8(a_i8x32, b_i8x32), _mm256_subs_epu8(b_i8x32, a_i8x32));
376
+
377
+ // Zero-extend to i16 using unpack (1cy @ p5, much faster than cvtepi8_epi16)
378
+ diff_low_i16x16 = _mm256_unpacklo_epi8(diff_u8x32, zeros_i8x32);
379
+ diff_high_i16x16 = _mm256_unpackhi_epi8(diff_u8x32, zeros_i8x32);
380
+
381
+ // Multiply and accumulate at i16 level, accumulate at i32 level
382
+ distance_sq_low_i32x8 = _mm256_add_epi32(distance_sq_low_i32x8,
383
+ _mm256_madd_epi16(diff_low_i16x16, diff_low_i16x16));
384
+ distance_sq_high_i32x8 = _mm256_add_epi32(distance_sq_high_i32x8,
385
+ _mm256_madd_epi16(diff_high_i16x16, diff_high_i16x16));
386
+ }
387
+
388
+ // Reduce to scalar
389
+ nk_i32_t distance_sq_i32 = nk_reduce_add_i32x8_haswell_(
390
+ _mm256_add_epi32(distance_sq_low_i32x8, distance_sq_high_i32x8));
391
+
392
+ // Take care of the tail:
393
+ for (; i < n; ++i) {
394
+ nk_i32_t diff_i32 = (nk_i32_t)(a[i]) - b[i];
395
+ distance_sq_i32 += diff_i32 * diff_i32;
396
+ }
397
+
398
+ *result = (nk_u32_t)distance_sq_i32;
399
+ }
400
+
401
+ NK_PUBLIC void nk_euclidean_i8_haswell(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_f32_t *result) {
402
+ nk_u32_t distance_sq_u32;
403
+ nk_sqeuclidean_i8_haswell(a, b, n, &distance_sq_u32);
404
+ *result = nk_f32_sqrt_haswell((nk_f32_t)distance_sq_u32);
405
+ }
406
+
407
+ NK_PUBLIC void nk_angular_i8_haswell(nk_i8_t const *a, nk_i8_t const *b, nk_size_t n, nk_f32_t *result) {
408
+
409
+ __m256i dot_product_i32x8 = _mm256_setzero_si256();
410
+ __m256i a_norm_sq_i32x8 = _mm256_setzero_si256();
411
+ __m256i b_norm_sq_i32x8 = _mm256_setzero_si256();
412
+
413
+ // AVX2 has no instructions for 8-bit signed integer dot products,
414
+ // but it has a weird instruction for mixed signed-unsigned 8-bit dot product.
415
+ // So we need to normalize the first vector to its absolute value,
416
+ // and shift the product sign into the second vector.
417
+ //
418
+ // __m256i a_i8_abs_vec = _mm256_abs_epi8(a_i8_vec);
419
+ // __m256i b_i8_flipped_vec = _mm256_sign_epi8(b_i8_vec, a_i8_vec);
420
+ // __m256i ab_i16_vec = _mm256_maddubs_epi16(a_i8_abs_vec, b_i8_flipped_vec);
421
+ //
422
+ // The problem with this approach, however, is the `-128` value in the second vector.
423
+ // Flipping its sign will do nothing, and the result will be incorrect.
424
+ // This can easily lead to noticeable numerical errors in the final result.
425
+ nk_size_t i = 0;
426
+ for (; i + 16 <= n; i += 16) {
427
+ __m128i a_i8x16 = _mm_loadu_si128((__m128i const *)(a + i));
428
+ __m128i b_i8x16 = _mm_loadu_si128((__m128i const *)(b + i));
429
+
430
+ // Sign extend i8 → i16 directly (128-bit → 256-bit, no port 5 pressure)
431
+ __m256i a_i16x16 = _mm256_cvtepi8_epi16(a_i8x16);
432
+ __m256i b_i16x16 = _mm256_cvtepi8_epi16(b_i8x16);
433
+
434
+ // Multiply and accumulate as i16 pairs, accumulate products as i32:
435
+ dot_product_i32x8 = _mm256_add_epi32(dot_product_i32x8, _mm256_madd_epi16(a_i16x16, b_i16x16));
436
+ a_norm_sq_i32x8 = _mm256_add_epi32(a_norm_sq_i32x8, _mm256_madd_epi16(a_i16x16, a_i16x16));
437
+ b_norm_sq_i32x8 = _mm256_add_epi32(b_norm_sq_i32x8, _mm256_madd_epi16(b_i16x16, b_i16x16));
438
+ }
439
+
440
+ // Reduce to scalar
441
+ nk_i32_t dot_product_i32 = nk_reduce_add_i32x8_haswell_(dot_product_i32x8);
442
+ nk_i32_t a_norm_sq_i32 = nk_reduce_add_i32x8_haswell_(a_norm_sq_i32x8);
443
+ nk_i32_t b_norm_sq_i32 = nk_reduce_add_i32x8_haswell_(b_norm_sq_i32x8);
444
+
445
+ // Take care of the tail:
446
+ for (; i < n; ++i) {
447
+ nk_i32_t a_element_i32 = a[i], b_element_i32 = b[i];
448
+ dot_product_i32 += a_element_i32 * b_element_i32;
449
+ a_norm_sq_i32 += a_element_i32 * a_element_i32;
450
+ b_norm_sq_i32 += b_element_i32 * b_element_i32;
451
+ }
452
+
453
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)dot_product_i32, (nk_f32_t)a_norm_sq_i32,
454
+ (nk_f32_t)b_norm_sq_i32);
455
+ }
456
+
457
+ NK_PUBLIC void nk_sqeuclidean_u8_haswell(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_u32_t *result) {
458
+
459
+ __m256i distance_sq_low_i32x8 = _mm256_setzero_si256();
460
+ __m256i distance_sq_high_i32x8 = _mm256_setzero_si256();
461
+ __m256i const zeros_i8x32 = _mm256_setzero_si256();
462
+
463
+ nk_size_t i = 0;
464
+ for (; i + 32 <= n; i += 32) {
465
+ __m256i a_u8x32 = _mm256_loadu_si256((__m256i const *)(a + i));
466
+ __m256i b_u8x32 = _mm256_loadu_si256((__m256i const *)(b + i));
467
+
468
+ // Subtracting unsigned vectors in AVX2 is done by saturating subtraction:
469
+ __m256i diff_u8x32 = _mm256_or_si256(_mm256_subs_epu8(a_u8x32, b_u8x32), _mm256_subs_epu8(b_u8x32, a_u8x32));
470
+
471
+ // Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
472
+ // instructions instead of extracts, as they are much faster and more efficient.
473
+ __m256i diff_low_i16x16 = _mm256_unpacklo_epi8(diff_u8x32, zeros_i8x32);
474
+ __m256i diff_high_i16x16 = _mm256_unpackhi_epi8(diff_u8x32, zeros_i8x32);
475
+
476
+ // Multiply and accumulate at `int16` level, accumulate at `int32` level:
477
+ distance_sq_low_i32x8 = _mm256_add_epi32(distance_sq_low_i32x8,
478
+ _mm256_madd_epi16(diff_low_i16x16, diff_low_i16x16));
479
+ distance_sq_high_i32x8 = _mm256_add_epi32(distance_sq_high_i32x8,
480
+ _mm256_madd_epi16(diff_high_i16x16, diff_high_i16x16));
481
+ }
482
+
483
+ // Accumulate the 32-bit integers from `distance_sq_high_i32x8` and `distance_sq_low_i32x8`
484
+ nk_i32_t distance_sq_i32 = nk_reduce_add_i32x8_haswell_(
485
+ _mm256_add_epi32(distance_sq_low_i32x8, distance_sq_high_i32x8));
486
+
487
+ // Take care of the tail:
488
+ for (; i < n; ++i) {
489
+ nk_i32_t diff_i32 = (nk_i32_t)(a[i]) - b[i];
490
+ distance_sq_i32 += diff_i32 * diff_i32;
491
+ }
492
+
493
+ *result = (nk_u32_t)distance_sq_i32;
494
+ }
495
+
496
+ NK_PUBLIC void nk_euclidean_u8_haswell(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_f32_t *result) {
497
+ nk_u32_t distance_sq_u32;
498
+ nk_sqeuclidean_u8_haswell(a, b, n, &distance_sq_u32);
499
+ *result = nk_f32_sqrt_haswell((nk_f32_t)distance_sq_u32);
500
+ }
501
+
502
+ NK_PUBLIC void nk_angular_u8_haswell(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_f32_t *result) {
503
+
504
+ __m256i dot_product_low_i32x8 = _mm256_setzero_si256();
505
+ __m256i dot_product_high_i32x8 = _mm256_setzero_si256();
506
+ __m256i a_norm_sq_low_i32x8 = _mm256_setzero_si256();
507
+ __m256i a_norm_sq_high_i32x8 = _mm256_setzero_si256();
508
+ __m256i b_norm_sq_low_i32x8 = _mm256_setzero_si256();
509
+ __m256i b_norm_sq_high_i32x8 = _mm256_setzero_si256();
510
+ __m256i const zeros_i8x32 = _mm256_setzero_si256();
511
+
512
+ // AVX2 has no instructions for 8-bit signed integer dot products,
513
+ // but it has a weird instruction for mixed signed-unsigned 8-bit dot product.
514
+ // So we need to normalize the first vector to its absolute value,
515
+ // and shift the product sign into the second vector.
516
+ //
517
+ // __m256i a_i8_abs_vec = _mm256_abs_epi8(a_i8_vec);
518
+ // __m256i b_i8_flipped_vec = _mm256_sign_epi8(b_i8_vec, a_i8_vec);
519
+ // __m256i ab_i16_vec = _mm256_maddubs_epi16(a_i8_abs_vec, b_i8_flipped_vec);
520
+ //
521
+ // The problem with this approach, however, is the `-128` value in the second vector.
522
+ // Flipping its sign will do nothing, and the result will be incorrect.
523
+ // This can easily lead to noticeable numerical errors in the final result.
524
+ nk_size_t i = 0;
525
+ for (; i + 32 <= n; i += 32) {
526
+ __m256i a_u8x32 = _mm256_loadu_si256((__m256i const *)(a + i));
527
+ __m256i b_u8x32 = _mm256_loadu_si256((__m256i const *)(b + i));
528
+
529
+ // Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
530
+ // instructions instead of extracts, as they are much faster and more efficient.
531
+ __m256i a_low_i16x16 = _mm256_unpacklo_epi8(a_u8x32, zeros_i8x32);
532
+ __m256i a_high_i16x16 = _mm256_unpackhi_epi8(a_u8x32, zeros_i8x32);
533
+ __m256i b_low_i16x16 = _mm256_unpacklo_epi8(b_u8x32, zeros_i8x32);
534
+ __m256i b_high_i16x16 = _mm256_unpackhi_epi8(b_u8x32, zeros_i8x32);
535
+
536
+ // Multiply and accumulate as `int16`, accumulate products as `int32`
537
+ dot_product_low_i32x8 = _mm256_add_epi32(dot_product_low_i32x8, _mm256_madd_epi16(a_low_i16x16, b_low_i16x16));
538
+ dot_product_high_i32x8 = _mm256_add_epi32(dot_product_high_i32x8,
539
+ _mm256_madd_epi16(a_high_i16x16, b_high_i16x16));
540
+ a_norm_sq_low_i32x8 = _mm256_add_epi32(a_norm_sq_low_i32x8, _mm256_madd_epi16(a_low_i16x16, a_low_i16x16));
541
+ a_norm_sq_high_i32x8 = _mm256_add_epi32(a_norm_sq_high_i32x8, _mm256_madd_epi16(a_high_i16x16, a_high_i16x16));
542
+ b_norm_sq_low_i32x8 = _mm256_add_epi32(b_norm_sq_low_i32x8, _mm256_madd_epi16(b_low_i16x16, b_low_i16x16));
543
+ b_norm_sq_high_i32x8 = _mm256_add_epi32(b_norm_sq_high_i32x8, _mm256_madd_epi16(b_high_i16x16, b_high_i16x16));
544
+ }
545
+
546
+ // Further reduce to a single sum for each vector
547
+ nk_i32_t dot_product_i32 = nk_reduce_add_i32x8_haswell_(
548
+ _mm256_add_epi32(dot_product_low_i32x8, dot_product_high_i32x8));
549
+ nk_i32_t a_norm_sq_i32 = nk_reduce_add_i32x8_haswell_(_mm256_add_epi32(a_norm_sq_low_i32x8, a_norm_sq_high_i32x8));
550
+ nk_i32_t b_norm_sq_i32 = nk_reduce_add_i32x8_haswell_(_mm256_add_epi32(b_norm_sq_low_i32x8, b_norm_sq_high_i32x8));
551
+
552
+ // Take care of the tail:
553
+ for (; i < n; ++i) {
554
+ nk_i32_t a_element_i32 = a[i], b_element_i32 = b[i];
555
+ dot_product_i32 += a_element_i32 * b_element_i32;
556
+ a_norm_sq_i32 += a_element_i32 * a_element_i32;
557
+ b_norm_sq_i32 += b_element_i32 * b_element_i32;
558
+ }
559
+
560
+ *result = nk_angular_normalize_f32_haswell_((nk_f32_t)dot_product_i32, (nk_f32_t)a_norm_sq_i32,
561
+ (nk_f32_t)b_norm_sq_i32);
562
+ }
563
+
564
+ #pragma endregion I8 and U8 Integers
565
+ #pragma region F32 and F64 Floats
566
+
567
+ NK_PUBLIC void nk_sqeuclidean_f32_haswell(nk_f32_t const *a, nk_f32_t const *b, nk_size_t n, nk_f64_t *result) {
568
+ // Upcast to f64 for higher precision accumulation
569
+ __m256d sum_f64x4 = _mm256_setzero_pd();
570
+ nk_size_t i = 0;
571
+ for (; i + 4 <= n; i += 4) {
572
+ __m128 a_f32x4 = _mm_loadu_ps(a + i);
573
+ __m128 b_f32x4 = _mm_loadu_ps(b + i);
574
+ __m256d a_f64x4 = _mm256_cvtps_pd(a_f32x4);
575
+ __m256d b_f64x4 = _mm256_cvtps_pd(b_f32x4);
576
+ __m256d diff_f64x4 = _mm256_sub_pd(a_f64x4, b_f64x4);
577
+ sum_f64x4 = _mm256_fmadd_pd(diff_f64x4, diff_f64x4, sum_f64x4);
578
+ }
579
+
580
+ nk_f64_t sum_f64 = nk_reduce_add_f64x4_haswell_(sum_f64x4);
581
+ for (; i < n; ++i) {
582
+ nk_f64_t diff_f64 = (nk_f64_t)a[i] - b[i];
583
+ sum_f64 += diff_f64 * diff_f64;
584
+ }
585
+
586
+ *result = sum_f64;
587
+ }
588
+
589
+ NK_PUBLIC void nk_euclidean_f32_haswell(nk_f32_t const *a, nk_f32_t const *b, nk_size_t n, nk_f64_t *result) {
590
+ // Upcast to f64 for higher precision accumulation, use f64 sqrt before downcasting
591
+ __m256d sum_f64x4 = _mm256_setzero_pd();
592
+ nk_size_t i = 0;
593
+ for (; i + 4 <= n; i += 4) {
594
+ __m128 a_f32x4 = _mm_loadu_ps(a + i);
595
+ __m128 b_f32x4 = _mm_loadu_ps(b + i);
596
+ __m256d a_f64x4 = _mm256_cvtps_pd(a_f32x4);
597
+ __m256d b_f64x4 = _mm256_cvtps_pd(b_f32x4);
598
+ __m256d diff_f64x4 = _mm256_sub_pd(a_f64x4, b_f64x4);
599
+ sum_f64x4 = _mm256_fmadd_pd(diff_f64x4, diff_f64x4, sum_f64x4);
600
+ }
601
+
602
+ nk_f64_t sum_f64 = nk_reduce_add_f64x4_haswell_(sum_f64x4);
603
+ for (; i < n; ++i) {
604
+ nk_f64_t diff_f64 = (nk_f64_t)a[i] - b[i];
605
+ sum_f64 += diff_f64 * diff_f64;
606
+ }
607
+
608
+ *result = nk_f64_sqrt_haswell(sum_f64);
609
+ }
610
+
611
+ NK_PUBLIC void nk_angular_f32_haswell(nk_f32_t const *a, nk_f32_t const *b, nk_size_t n, nk_f64_t *result) {
612
+ // Upcast to f64 for higher precision accumulation
613
+ __m256d dot_f64x4 = _mm256_setzero_pd();
614
+ __m256d a_norm_sq_f64x4 = _mm256_setzero_pd();
615
+ __m256d b_norm_sq_f64x4 = _mm256_setzero_pd();
616
+ nk_size_t i = 0;
617
+ for (; i + 4 <= n; i += 4) {
618
+ __m128 a_f32x4 = _mm_loadu_ps(a + i);
619
+ __m128 b_f32x4 = _mm_loadu_ps(b + i);
620
+ __m256d a_f64x4 = _mm256_cvtps_pd(a_f32x4);
621
+ __m256d b_f64x4 = _mm256_cvtps_pd(b_f32x4);
622
+ dot_f64x4 = _mm256_fmadd_pd(a_f64x4, b_f64x4, dot_f64x4);
623
+ a_norm_sq_f64x4 = _mm256_fmadd_pd(a_f64x4, a_f64x4, a_norm_sq_f64x4);
624
+ b_norm_sq_f64x4 = _mm256_fmadd_pd(b_f64x4, b_f64x4, b_norm_sq_f64x4);
625
+ }
626
+
627
+ nk_f64_t dot_f64 = nk_reduce_add_f64x4_haswell_(dot_f64x4);
628
+ nk_f64_t a_norm_sq_f64 = nk_reduce_add_f64x4_haswell_(a_norm_sq_f64x4);
629
+ nk_f64_t b_norm_sq_f64 = nk_reduce_add_f64x4_haswell_(b_norm_sq_f64x4);
630
+ for (; i < n; ++i) {
631
+ nk_f64_t a_f64 = a[i], b_f64 = b[i];
632
+ dot_f64 += a_f64 * b_f64;
633
+ a_norm_sq_f64 += a_f64 * a_f64;
634
+ b_norm_sq_f64 += b_f64 * b_f64;
635
+ }
636
+ *result = nk_angular_normalize_f64_haswell_(dot_f64, a_norm_sq_f64, b_norm_sq_f64);
637
+ }
638
+
639
+ NK_PUBLIC void nk_sqeuclidean_f64_haswell(nk_f64_t const *a, nk_f64_t const *b, nk_size_t n, nk_f64_t *result) {
640
+ __m256d sum_f64x4 = _mm256_setzero_pd();
641
+ __m256d a_f64x4, b_f64x4;
642
+
643
+ nk_sqeuclidean_f64_haswell_cycle:
644
+ if (n < 4) {
645
+ nk_b256_vec_t a_tail, b_tail;
646
+ nk_partial_load_b64x4_serial_(a, &a_tail, n);
647
+ nk_partial_load_b64x4_serial_(b, &b_tail, n);
648
+ a_f64x4 = a_tail.ymm_pd;
649
+ b_f64x4 = b_tail.ymm_pd;
650
+ n = 0;
651
+ }
652
+ else {
653
+ a_f64x4 = _mm256_loadu_pd(a);
654
+ b_f64x4 = _mm256_loadu_pd(b);
655
+ a += 4, b += 4, n -= 4;
656
+ }
657
+ __m256d diff_f64x4 = _mm256_sub_pd(a_f64x4, b_f64x4);
658
+ sum_f64x4 = _mm256_fmadd_pd(diff_f64x4, diff_f64x4, sum_f64x4);
659
+ if (n) goto nk_sqeuclidean_f64_haswell_cycle;
660
+
661
+ *result = nk_reduce_add_f64x4_haswell_(sum_f64x4);
662
+ }
663
+
664
+ NK_PUBLIC void nk_euclidean_f64_haswell(nk_f64_t const *a, nk_f64_t const *b, nk_size_t n, nk_f64_t *result) {
665
+ nk_sqeuclidean_f64_haswell(a, b, n, result);
666
+ *result = nk_f64_sqrt_haswell(*result);
667
+ }
668
+
669
+ NK_PUBLIC void nk_angular_f64_haswell(nk_f64_t const *a, nk_f64_t const *b, nk_size_t n, nk_f64_t *result) {
670
+ // Dot2 (Ogita-Rump-Oishi 2005) for cross-product a × b only - it may have cancellation.
671
+ // Self-products ‖a‖² and ‖b‖² use simple FMA - all terms are non-negative, no cancellation.
672
+ // Note: For cross-product we use Knuth TwoSum (6 ops) rather than Neumaier with blends (10 ops)
673
+ // since products can be signed and Knuth handles any operand ordering efficiently.
674
+ __m256d dot_sum_f64x4 = _mm256_setzero_pd();
675
+ __m256d dot_compensation_f64x4 = _mm256_setzero_pd();
676
+ __m256d a_norm_sq_f64x4 = _mm256_setzero_pd();
677
+ __m256d b_norm_sq_f64x4 = _mm256_setzero_pd();
678
+ __m256d a_f64x4, b_f64x4;
679
+
680
+ nk_angular_f64_haswell_cycle:
681
+ if (n < 4) {
682
+ nk_b256_vec_t a_tail, b_tail;
683
+ nk_partial_load_b64x4_serial_(a, &a_tail, n);
684
+ nk_partial_load_b64x4_serial_(b, &b_tail, n);
685
+ a_f64x4 = a_tail.ymm_pd;
686
+ b_f64x4 = b_tail.ymm_pd;
687
+ n = 0;
688
+ }
689
+ else {
690
+ a_f64x4 = _mm256_loadu_pd(a);
691
+ b_f64x4 = _mm256_loadu_pd(b);
692
+ a += 4, b += 4, n -= 4;
693
+ }
694
+ // TwoProd: product = a × b, error = fma(a, b, -product)
695
+ __m256d x_f64x4 = _mm256_mul_pd(a_f64x4, b_f64x4);
696
+ __m256d product_error_f64x4 = _mm256_fmsub_pd(a_f64x4, b_f64x4, x_f64x4);
697
+ // Knuth TwoSum: error = (sum - (t - z)) + (x - z) where z = t - sum
698
+ __m256d tentative_sum_f64x4 = _mm256_add_pd(dot_sum_f64x4, x_f64x4);
699
+ __m256d virtual_addend_f64x4 = _mm256_sub_pd(tentative_sum_f64x4, dot_sum_f64x4);
700
+ __m256d sum_error_f64x4 = _mm256_add_pd(
701
+ _mm256_sub_pd(dot_sum_f64x4, _mm256_sub_pd(tentative_sum_f64x4, virtual_addend_f64x4)),
702
+ _mm256_sub_pd(x_f64x4, virtual_addend_f64x4));
703
+ dot_sum_f64x4 = tentative_sum_f64x4;
704
+ dot_compensation_f64x4 = _mm256_add_pd(dot_compensation_f64x4, _mm256_add_pd(sum_error_f64x4, product_error_f64x4));
705
+ // Simple FMA for self-products (no cancellation possible)
706
+ a_norm_sq_f64x4 = _mm256_fmadd_pd(a_f64x4, a_f64x4, a_norm_sq_f64x4);
707
+ b_norm_sq_f64x4 = _mm256_fmadd_pd(b_f64x4, b_f64x4, b_norm_sq_f64x4);
708
+ if (n) goto nk_angular_f64_haswell_cycle;
709
+
710
+ *result = nk_angular_normalize_f64_haswell_( //
711
+ nk_dot_stable_sum_f64x4_haswell_(dot_sum_f64x4, dot_compensation_f64x4),
712
+ nk_reduce_add_f64x4_haswell_(a_norm_sq_f64x4), nk_reduce_add_f64x4_haswell_(b_norm_sq_f64x4));
713
+ }
714
+
715
+ #pragma endregion F32 and F64 Floats
716
+ #pragma region FP8 Floats
717
+
718
+ NK_PUBLIC void nk_sqeuclidean_e2m3_haswell(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
719
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
720
+
721
+ nk_sqeuclidean_e2m3_haswell_cycle:
722
+ if (n < 8) {
723
+ nk_b128_vec_t a_vec, b_vec;
724
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
725
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
726
+ __m256 a_f32x8 = nk_e2m3x8_to_f32x8_haswell_(a_vec.xmm);
727
+ __m256 b_f32x8 = nk_e2m3x8_to_f32x8_haswell_(b_vec.xmm);
728
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
729
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
730
+ }
731
+ else {
732
+ __m256 a_f32x8 = nk_e2m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
733
+ __m256 b_f32x8 = nk_e2m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
734
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
735
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
736
+ n -= 8, a += 8, b += 8;
737
+ goto nk_sqeuclidean_e2m3_haswell_cycle;
738
+ }
739
+
740
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
741
+ }
742
+
743
+ NK_PUBLIC void nk_euclidean_e2m3_haswell(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
744
+ nk_sqeuclidean_e2m3_haswell(a, b, n, result);
745
+ *result = nk_f32_sqrt_haswell(*result);
746
+ }
747
+
748
+ NK_PUBLIC void nk_angular_e2m3_haswell(nk_e2m3_t const *a, nk_e2m3_t const *b, nk_size_t n, nk_f32_t *result) {
749
+ __m256 dot_product_f32x8 = _mm256_setzero_ps();
750
+ __m256 a_norm_sq_f32x8 = _mm256_setzero_ps();
751
+ __m256 b_norm_sq_f32x8 = _mm256_setzero_ps();
752
+
753
+ nk_angular_e2m3_haswell_cycle:
754
+ if (n < 8) {
755
+ nk_b128_vec_t a_vec, b_vec;
756
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
757
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
758
+ __m256 a_f32x8 = nk_e2m3x8_to_f32x8_haswell_(a_vec.xmm);
759
+ __m256 b_f32x8 = nk_e2m3x8_to_f32x8_haswell_(b_vec.xmm);
760
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
761
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
762
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
763
+ }
764
+ else {
765
+ __m256 a_f32x8 = nk_e2m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
766
+ __m256 b_f32x8 = nk_e2m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
767
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
768
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
769
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
770
+ n -= 8, a += 8, b += 8;
771
+ goto nk_angular_e2m3_haswell_cycle;
772
+ }
773
+
774
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
775
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
776
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
777
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
778
+ }
779
+
780
+ NK_PUBLIC void nk_sqeuclidean_e3m2_haswell(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
781
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
782
+
783
+ nk_sqeuclidean_e3m2_haswell_cycle:
784
+ if (n < 8) {
785
+ nk_b128_vec_t a_vec, b_vec;
786
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
787
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
788
+ __m256 a_f32x8 = nk_e3m2x8_to_f32x8_haswell_(a_vec.xmm);
789
+ __m256 b_f32x8 = nk_e3m2x8_to_f32x8_haswell_(b_vec.xmm);
790
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
791
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
792
+ }
793
+ else {
794
+ __m256 a_f32x8 = nk_e3m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
795
+ __m256 b_f32x8 = nk_e3m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
796
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
797
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
798
+ n -= 8, a += 8, b += 8;
799
+ goto nk_sqeuclidean_e3m2_haswell_cycle;
800
+ }
801
+
802
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
803
+ }
804
+
805
+ NK_PUBLIC void nk_euclidean_e3m2_haswell(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
806
+ nk_sqeuclidean_e3m2_haswell(a, b, n, result);
807
+ *result = nk_f32_sqrt_haswell(*result);
808
+ }
809
+
810
+ NK_PUBLIC void nk_angular_e3m2_haswell(nk_e3m2_t const *a, nk_e3m2_t const *b, nk_size_t n, nk_f32_t *result) {
811
+ __m256 dot_product_f32x8 = _mm256_setzero_ps();
812
+ __m256 a_norm_sq_f32x8 = _mm256_setzero_ps();
813
+ __m256 b_norm_sq_f32x8 = _mm256_setzero_ps();
814
+
815
+ nk_angular_e3m2_haswell_cycle:
816
+ if (n < 8) {
817
+ nk_b128_vec_t a_vec, b_vec;
818
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
819
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
820
+ __m256 a_f32x8 = nk_e3m2x8_to_f32x8_haswell_(a_vec.xmm);
821
+ __m256 b_f32x8 = nk_e3m2x8_to_f32x8_haswell_(b_vec.xmm);
822
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
823
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
824
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
825
+ }
826
+ else {
827
+ __m256 a_f32x8 = nk_e3m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
828
+ __m256 b_f32x8 = nk_e3m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
829
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
830
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
831
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
832
+ n -= 8, a += 8, b += 8;
833
+ goto nk_angular_e3m2_haswell_cycle;
834
+ }
835
+
836
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
837
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
838
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
839
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
840
+ }
841
+
842
+ NK_PUBLIC void nk_sqeuclidean_e4m3_haswell(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
843
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
844
+
845
+ nk_sqeuclidean_e4m3_haswell_cycle:
846
+ if (n < 8) {
847
+ nk_b128_vec_t a_vec, b_vec;
848
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
849
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
850
+ __m256 a_f32x8 = nk_e4m3x8_to_f32x8_haswell_(a_vec.xmm);
851
+ __m256 b_f32x8 = nk_e4m3x8_to_f32x8_haswell_(b_vec.xmm);
852
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
853
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
854
+ }
855
+ else {
856
+ __m256 a_f32x8 = nk_e4m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
857
+ __m256 b_f32x8 = nk_e4m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
858
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
859
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
860
+ n -= 8, a += 8, b += 8;
861
+ goto nk_sqeuclidean_e4m3_haswell_cycle;
862
+ }
863
+
864
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
865
+ }
866
+
867
+ NK_PUBLIC void nk_euclidean_e4m3_haswell(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
868
+ nk_sqeuclidean_e4m3_haswell(a, b, n, result);
869
+ *result = nk_f32_sqrt_haswell(*result);
870
+ }
871
+
872
+ NK_PUBLIC void nk_angular_e4m3_haswell(nk_e4m3_t const *a, nk_e4m3_t const *b, nk_size_t n, nk_f32_t *result) {
873
+ __m256 dot_product_f32x8 = _mm256_setzero_ps();
874
+ __m256 a_norm_sq_f32x8 = _mm256_setzero_ps();
875
+ __m256 b_norm_sq_f32x8 = _mm256_setzero_ps();
876
+
877
+ nk_angular_e4m3_haswell_cycle:
878
+ if (n < 8) {
879
+ nk_b128_vec_t a_vec, b_vec;
880
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
881
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
882
+ __m256 a_f32x8 = nk_e4m3x8_to_f32x8_haswell_(a_vec.xmm);
883
+ __m256 b_f32x8 = nk_e4m3x8_to_f32x8_haswell_(b_vec.xmm);
884
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
885
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
886
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
887
+ }
888
+ else {
889
+ __m256 a_f32x8 = nk_e4m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
890
+ __m256 b_f32x8 = nk_e4m3x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
891
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
892
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
893
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
894
+ n -= 8, a += 8, b += 8;
895
+ goto nk_angular_e4m3_haswell_cycle;
896
+ }
897
+
898
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
899
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
900
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
901
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
902
+ }
903
+
904
+ NK_PUBLIC void nk_sqeuclidean_e5m2_haswell(nk_e5m2_t const *a, nk_e5m2_t const *b, nk_size_t n, nk_f32_t *result) {
905
+ __m256 distance_sq_f32x8 = _mm256_setzero_ps();
906
+
907
+ nk_sqeuclidean_e5m2_haswell_cycle:
908
+ if (n < 8) {
909
+ nk_b128_vec_t a_vec, b_vec;
910
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
911
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
912
+ __m256 a_f32x8 = nk_e5m2x8_to_f32x8_haswell_(a_vec.xmm);
913
+ __m256 b_f32x8 = nk_e5m2x8_to_f32x8_haswell_(b_vec.xmm);
914
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
915
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
916
+ }
917
+ else {
918
+ __m256 a_f32x8 = nk_e5m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
919
+ __m256 b_f32x8 = nk_e5m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
920
+ __m256 diff_f32x8 = _mm256_sub_ps(a_f32x8, b_f32x8);
921
+ distance_sq_f32x8 = _mm256_fmadd_ps(diff_f32x8, diff_f32x8, distance_sq_f32x8);
922
+ n -= 8, a += 8, b += 8;
923
+ goto nk_sqeuclidean_e5m2_haswell_cycle;
924
+ }
925
+
926
+ *result = nk_reduce_add_f32x8_haswell_(distance_sq_f32x8);
927
+ }
928
+
929
+ NK_PUBLIC void nk_euclidean_e5m2_haswell(nk_e5m2_t const *a, nk_e5m2_t const *b, nk_size_t n, nk_f32_t *result) {
930
+ nk_sqeuclidean_e5m2_haswell(a, b, n, result);
931
+ *result = nk_f32_sqrt_haswell(*result);
932
+ }
933
+
934
+ NK_PUBLIC void nk_angular_e5m2_haswell(nk_e5m2_t const *a, nk_e5m2_t const *b, nk_size_t n, nk_f32_t *result) {
935
+ __m256 dot_product_f32x8 = _mm256_setzero_ps();
936
+ __m256 a_norm_sq_f32x8 = _mm256_setzero_ps();
937
+ __m256 b_norm_sq_f32x8 = _mm256_setzero_ps();
938
+
939
+ nk_angular_e5m2_haswell_cycle:
940
+ if (n < 8) {
941
+ nk_b128_vec_t a_vec, b_vec;
942
+ nk_partial_load_b8x16_serial_(a, &a_vec, n);
943
+ nk_partial_load_b8x16_serial_(b, &b_vec, n);
944
+ __m256 a_f32x8 = nk_e5m2x8_to_f32x8_haswell_(a_vec.xmm);
945
+ __m256 b_f32x8 = nk_e5m2x8_to_f32x8_haswell_(b_vec.xmm);
946
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
947
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
948
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
949
+ }
950
+ else {
951
+ __m256 a_f32x8 = nk_e5m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)a));
952
+ __m256 b_f32x8 = nk_e5m2x8_to_f32x8_haswell_(_mm_loadu_si128((__m128i const *)b));
953
+ dot_product_f32x8 = _mm256_fmadd_ps(a_f32x8, b_f32x8, dot_product_f32x8);
954
+ a_norm_sq_f32x8 = _mm256_fmadd_ps(a_f32x8, a_f32x8, a_norm_sq_f32x8);
955
+ b_norm_sq_f32x8 = _mm256_fmadd_ps(b_f32x8, b_f32x8, b_norm_sq_f32x8);
956
+ n -= 8, a += 8, b += 8;
957
+ goto nk_angular_e5m2_haswell_cycle;
958
+ }
959
+
960
+ nk_f32_t dot_product_f32 = nk_reduce_add_f32x8_haswell_(dot_product_f32x8);
961
+ nk_f32_t a_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(a_norm_sq_f32x8);
962
+ nk_f32_t b_norm_sq_f32 = nk_reduce_add_f32x8_haswell_(b_norm_sq_f32x8);
963
+ *result = nk_angular_normalize_f32_haswell_(dot_product_f32, a_norm_sq_f32, b_norm_sq_f32);
964
+ }
965
+
966
+ #if defined(__clang__)
967
+ #pragma clang attribute pop
968
+ #elif defined(__GNUC__)
969
+ #pragma GCC pop_options
970
+ #endif
971
+
972
+ #if defined(__cplusplus)
973
+ } // extern "C"
974
+ #endif
975
+
976
+ #pragma endregion FP8 Floats
977
+ #endif // NK_TARGET_HASWELL
978
+ #endif // NK_TARGET_X8664_
979
+ #endif // NK_SPATIAL_HASWELL_H