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,869 @@
1
+ /**
2
+ * @brief SIMD-accelerated Dot Products for NEON.
3
+ * @file include/numkong/dot/neon.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/dot.h
8
+ *
9
+ * @section dot_neon_instructions NEON Dot Product Instructions
10
+ *
11
+ * Key NEON instructions for dot products:
12
+ *
13
+ * Intrinsic Instruction A76 M5
14
+ * vfmaq_f32 FMLA (V.4S, V.4S, V.4S) 4cy @ 2p 3cy @ 4p
15
+ * vfmaq_f64 FMLA (V.2D, V.2D, V.2D) 4cy @ 2p 4cy @ 4p
16
+ * vfmsq_f64 FMLS (V.2D, V.2D, V.2D) 4cy @ 2p 4cy @ 4p
17
+ * vmulq_f32 FMUL (V.4S, V.4S, V.4S) 3cy @ 2p 3cy @ 4p
18
+ * vmulq_f64 FMUL (V.2D, V.2D, V.2D) 3cy @ 2p 3cy @ 4p
19
+ * vaddvq_f32 FADDP+FADDP (reduce) 5cy @ 1p 8cy @ 1p
20
+ * vaddvq_f64 FADDP (V.2D to scalar) 3cy @ 1p 3cy @ 1p
21
+ * vpaddq_f32 FADDP (V.4S, V.4S, V.4S) 2cy @ 2p 3cy @ 4p
22
+ * vpaddq_f64 FADDP (V.2D, V.2D, V.2D) 2cy @ 2p 3cy @ 4p
23
+ * vcvt_f64_f32 FCVTL (V.2D, V.2S) 3cy @ 2p 3cy @ 2p
24
+ * vld2_f32 LD2 ({Vt.2S, Vt2.2S}, [Xn]) 4cy @ 1p 4cy @ 1p
25
+ *
26
+ * FMA throughput doubles on cores with 4 SIMD pipes (Apple M4+, Graviton3+, Oryon), but
27
+ * horizontal reductions remain at 1/cy on all cores and become the main bottleneck.
28
+ *
29
+ * For f32 dot products, we upcast to f64 for accumulation to preserve precision and
30
+ * avoid catastrophic cancellation in large-magnitude sums.
31
+ *
32
+ * @section dot_neon_stateful Stateful Streaming Logic
33
+ *
34
+ * To build memory-optimal tiled algorithms, this file defines following structures and force-inlined
35
+ * `NK_INTERNAL` functions:
36
+ *
37
+ * - nk_dot_f32x2 state for f32 inputs with double-precision accumulation,
38
+ * - nk_dot_f64x2 state with Dot2 stable dot-products for f64 inputs.
39
+ *
40
+ * @code{c}
41
+ * nk_dot_f32x2_state_neon_t state_first, state_second, state_third, state_fourth;
42
+ * float32x2_t query_f32x2, target_first_f32x2, target_second_f32x2, target_third_f32x2, target_fourth_f32x2;
43
+ * nk_dot_f32x2_init_neon(&state_first);
44
+ * nk_dot_f32x2_init_neon(&state_second);
45
+ * nk_dot_f32x2_init_neon(&state_third);
46
+ * nk_dot_f32x2_init_neon(&state_fourth);
47
+ * for (nk_size_t idx = 0; idx + 2 <= depth; idx += 2) {
48
+ * query_f32x2 = vld1_f32(query_ptr + idx);
49
+ * target_first_f32x2 = vld1_f32(target_first_ptr + idx);
50
+ * target_second_f32x2 = vld1_f32(target_second_ptr + idx);
51
+ * target_third_f32x2 = vld1_f32(target_third_ptr + idx);
52
+ * target_fourth_f32x2 = vld1_f32(target_fourth_ptr + idx);
53
+ * nk_dot_f32x2_update_neon(&state_first, query_f32x2, target_first_f32x2, idx, 2);
54
+ * nk_dot_f32x2_update_neon(&state_second, query_f32x2, target_second_f32x2, idx, 2);
55
+ * nk_dot_f32x2_update_neon(&state_third, query_f32x2, target_third_f32x2, idx, 2);
56
+ * nk_dot_f32x2_update_neon(&state_fourth, query_f32x2, target_fourth_f32x2, idx, 2);
57
+ * }
58
+ * float32x4_t results_f32x4;
59
+ * nk_dot_f32x2_finalize_neon(&state_first, &state_second, &state_third, &state_fourth, depth, &results_f32x4);
60
+ * @endcode
61
+ *
62
+ * For f64 inputs, Dot2 compensated summation provides numerical stability:
63
+ *
64
+ * @code{c}
65
+ * nk_dot_f64x2_state_neon_t state_first, state_second, state_third, state_fourth;
66
+ * float64x2_t query_f64x2, target_first_f64x2, target_second_f64x2, target_third_f64x2, target_fourth_f64x2;
67
+ * nk_dot_f64x2_init_neon(&state_first);
68
+ * nk_dot_f64x2_init_neon(&state_second);
69
+ * nk_dot_f64x2_init_neon(&state_third);
70
+ * nk_dot_f64x2_init_neon(&state_fourth);
71
+ * for (nk_size_t idx = 0; idx + 2 <= depth; idx += 2) {
72
+ * query_f64x2 = vld1q_f64(query_ptr + idx);
73
+ * target_first_f64x2 = vld1q_f64(target_first_ptr + idx);
74
+ * target_second_f64x2 = vld1q_f64(target_second_ptr + idx);
75
+ * target_third_f64x2 = vld1q_f64(target_third_ptr + idx);
76
+ * target_fourth_f64x2 = vld1q_f64(target_fourth_ptr + idx);
77
+ * nk_dot_f64x2_update_neon(&state_first, query_f64x2, target_first_f64x2, idx, 2);
78
+ * nk_dot_f64x2_update_neon(&state_second, query_f64x2, target_second_f64x2, idx, 2);
79
+ * nk_dot_f64x2_update_neon(&state_third, query_f64x2, target_third_f64x2, idx, 2);
80
+ * nk_dot_f64x2_update_neon(&state_fourth, query_f64x2, target_fourth_f64x2, idx, 2);
81
+ * }
82
+ * float64x4_t results_f64x4;
83
+ * nk_dot_f64x2_finalize_neon(&state_first, &state_second, &state_third, &state_fourth, depth, &results_f64x4);
84
+ * @endcode
85
+ */
86
+ #ifndef NK_DOT_NEON_H
87
+ #define NK_DOT_NEON_H
88
+
89
+ #if NK_TARGET_ARM64_
90
+ #if NK_TARGET_NEON
91
+
92
+ #include "numkong/cast/neon.h" // `nk_e4m3x8_to_f16x8_neon_`
93
+
94
+ #if defined(__cplusplus)
95
+ extern "C" {
96
+ #endif
97
+
98
+ #if defined(__clang__)
99
+ #pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
100
+ #elif defined(__GNUC__)
101
+ #pragma GCC push_options
102
+ #pragma GCC target("arch=armv8-a+simd")
103
+ #endif
104
+
105
+ /** @brief Compensated horizontal sum of 2 f64 lanes via TwoSum. */
106
+ NK_INTERNAL nk_f64_t nk_dot_stable_sum_f64x2_neon_(float64x2_t sum_f64x2, float64x2_t compensation_f64x2) {
107
+ // TwoSum merge of sum + compensation (2-wide)
108
+ float64x2_t tentative_sum_f64x2 = vaddq_f64(sum_f64x2, compensation_f64x2);
109
+ float64x2_t virtual_addend_f64x2 = vsubq_f64(tentative_sum_f64x2, sum_f64x2);
110
+ float64x2_t rounding_error_f64x2 = vaddq_f64(
111
+ vsubq_f64(sum_f64x2, vsubq_f64(tentative_sum_f64x2, virtual_addend_f64x2)),
112
+ vsubq_f64(compensation_f64x2, virtual_addend_f64x2));
113
+ // Scalar TwoSum 2→1
114
+ nk_f64_t lower_sum = vgetq_lane_f64(tentative_sum_f64x2, 0);
115
+ nk_f64_t upper_sum = vgetq_lane_f64(tentative_sum_f64x2, 1);
116
+ nk_f64_t lower_error = vgetq_lane_f64(rounding_error_f64x2, 0);
117
+ nk_f64_t upper_error = vgetq_lane_f64(rounding_error_f64x2, 1);
118
+ nk_f64_t tentative_sum = lower_sum + upper_sum;
119
+ nk_f64_t virtual_addend = tentative_sum - lower_sum;
120
+ nk_f64_t rounding_error = (lower_sum - (tentative_sum - virtual_addend)) + (upper_sum - virtual_addend);
121
+ return tentative_sum + (lower_error + upper_error + rounding_error);
122
+ }
123
+
124
+ #pragma region F32 and F64 Floats
125
+
126
+ NK_PUBLIC void nk_dot_f32_neon(nk_f32_t const *a_scalars, nk_f32_t const *b_scalars, nk_size_t count_scalars,
127
+ nk_f64_t *result) {
128
+ // Upcast f32 to f64 via FCVTL/FCVTL2, two independent FMA chains for ILP
129
+ float64x2_t sum_low_f64x2 = vdupq_n_f64(0);
130
+ float64x2_t sum_high_f64x2 = vdupq_n_f64(0);
131
+ nk_size_t idx_scalars = 0;
132
+ for (; idx_scalars + 4 <= count_scalars; idx_scalars += 4) {
133
+ float32x4_t a_f32x4 = vld1q_f32(a_scalars + idx_scalars);
134
+ float32x4_t b_f32x4 = vld1q_f32(b_scalars + idx_scalars);
135
+ float64x2_t a_low_f64x2 = vcvt_f64_f32(vget_low_f32(a_f32x4));
136
+ float64x2_t a_high_f64x2 = vcvt_high_f64_f32(a_f32x4);
137
+ float64x2_t b_low_f64x2 = vcvt_f64_f32(vget_low_f32(b_f32x4));
138
+ float64x2_t b_high_f64x2 = vcvt_high_f64_f32(b_f32x4);
139
+ sum_low_f64x2 = vfmaq_f64(sum_low_f64x2, a_low_f64x2, b_low_f64x2);
140
+ sum_high_f64x2 = vfmaq_f64(sum_high_f64x2, a_high_f64x2, b_high_f64x2);
141
+ }
142
+ nk_f64_t sum_f64 = vaddvq_f64(vaddq_f64(sum_low_f64x2, sum_high_f64x2));
143
+ for (; idx_scalars < count_scalars; ++idx_scalars)
144
+ sum_f64 += (nk_f64_t)a_scalars[idx_scalars] * (nk_f64_t)b_scalars[idx_scalars];
145
+ *result = sum_f64;
146
+ }
147
+
148
+ NK_PUBLIC void nk_dot_f32c_neon(nk_f32c_t const *a_pairs, nk_f32c_t const *b_pairs, nk_size_t count_pairs,
149
+ nk_f64c_t *result) {
150
+ // Upcast f32 to f64 for accumulation (2 complex pairs per iteration, avoids slow vget_low/high)
151
+ float64x2_t sum_real_f64x2 = vdupq_n_f64(0);
152
+ float64x2_t sum_imag_f64x2 = vdupq_n_f64(0);
153
+ nk_size_t idx_pairs = 0;
154
+ // ARMv8.3-A FCMLA (`vcmlaq_rot0/rot90_f32`) was benchmarked as an alternative to the
155
+ // deinterleave+4FMA pattern below. FCMLA processes only 2 complex pairs per iteration
156
+ // (interleaved 128-bit operands, 2x `vcmlaq`), while `vld2_f32` deinterleaves 2 pairs
157
+ // with 4 independent FMA instructions that fully utilize M4's 4 SIMD pipes. Result on
158
+ // Apple M4 at n=4096: manual f32 39.7 GiB/s, FCMLA 17.1 GiB/s (2.3x slower).
159
+ // The f64 upcast here trades throughput for precision — FCMLA offers neither advantage.
160
+ for (; idx_pairs + 2 <= count_pairs; idx_pairs += 2) {
161
+ // Unpack 2 complex pairs into real and imaginary parts:
162
+ float32x2x2_t a_f32x2x2 = vld2_f32((nk_f32_t const *)(a_pairs + idx_pairs));
163
+ float32x2x2_t b_f32x2x2 = vld2_f32((nk_f32_t const *)(b_pairs + idx_pairs));
164
+ // Upcast to f64
165
+ float64x2_t a_real_f64x2 = vcvt_f64_f32(a_f32x2x2.val[0]);
166
+ float64x2_t a_imag_f64x2 = vcvt_f64_f32(a_f32x2x2.val[1]);
167
+ float64x2_t b_real_f64x2 = vcvt_f64_f32(b_f32x2x2.val[0]);
168
+ float64x2_t b_imag_f64x2 = vcvt_f64_f32(b_f32x2x2.val[1]);
169
+ // Compute the dot product: real = aᵣ × bᵣ - aᵢ × bᵢ, imag = aᵣ × bᵢ + aᵢ × bᵣ
170
+ sum_real_f64x2 = vfmaq_f64(sum_real_f64x2, a_real_f64x2, b_real_f64x2);
171
+ sum_real_f64x2 = vfmsq_f64(sum_real_f64x2, a_imag_f64x2, b_imag_f64x2);
172
+ sum_imag_f64x2 = vfmaq_f64(sum_imag_f64x2, a_real_f64x2, b_imag_f64x2);
173
+ sum_imag_f64x2 = vfmaq_f64(sum_imag_f64x2, a_imag_f64x2, b_real_f64x2);
174
+ }
175
+ // Reduce horizontal sums:
176
+ nk_f64_t sum_real_f64 = vaddvq_f64(sum_real_f64x2);
177
+ nk_f64_t sum_imag_f64 = vaddvq_f64(sum_imag_f64x2);
178
+ // Handle the tail:
179
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
180
+ nk_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
181
+ nk_f64_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
182
+ sum_real_f64 += ar * br - ai * bi;
183
+ sum_imag_f64 += ar * bi + ai * br;
184
+ }
185
+ result->real = sum_real_f64;
186
+ result->imag = sum_imag_f64;
187
+ }
188
+
189
+ NK_PUBLIC void nk_vdot_f32c_neon(nk_f32c_t const *a_pairs, nk_f32c_t const *b_pairs, nk_size_t count_pairs,
190
+ nk_f64c_t *result) {
191
+ // Upcast f32 to f64 for accumulation (2 complex pairs per iteration, avoids slow vget_low/high)
192
+ float64x2_t sum_real_f64x2 = vdupq_n_f64(0);
193
+ float64x2_t sum_imag_f64x2 = vdupq_n_f64(0);
194
+ nk_size_t idx_pairs = 0;
195
+ for (; idx_pairs + 2 <= count_pairs; idx_pairs += 2) {
196
+ // Unpack 2 complex pairs into real and imaginary parts:
197
+ float32x2x2_t a_f32x2x2 = vld2_f32((nk_f32_t const *)(a_pairs + idx_pairs));
198
+ float32x2x2_t b_f32x2x2 = vld2_f32((nk_f32_t const *)(b_pairs + idx_pairs));
199
+ // Upcast to f64
200
+ float64x2_t a_real_f64x2 = vcvt_f64_f32(a_f32x2x2.val[0]);
201
+ float64x2_t a_imag_f64x2 = vcvt_f64_f32(a_f32x2x2.val[1]);
202
+ float64x2_t b_real_f64x2 = vcvt_f64_f32(b_f32x2x2.val[0]);
203
+ float64x2_t b_imag_f64x2 = vcvt_f64_f32(b_f32x2x2.val[1]);
204
+ // Compute conjugate dot product: real = aᵣ × bᵣ + aᵢ × bᵢ, imag = aᵣ × bᵢ - aᵢ × bᵣ
205
+ sum_real_f64x2 = vfmaq_f64(sum_real_f64x2, a_real_f64x2, b_real_f64x2);
206
+ sum_real_f64x2 = vfmaq_f64(sum_real_f64x2, a_imag_f64x2, b_imag_f64x2);
207
+ sum_imag_f64x2 = vfmaq_f64(sum_imag_f64x2, a_real_f64x2, b_imag_f64x2);
208
+ sum_imag_f64x2 = vfmsq_f64(sum_imag_f64x2, a_imag_f64x2, b_real_f64x2);
209
+ }
210
+ // Reduce horizontal sums:
211
+ nk_f64_t sum_real_f64 = vaddvq_f64(sum_real_f64x2);
212
+ nk_f64_t sum_imag_f64 = vaddvq_f64(sum_imag_f64x2);
213
+ // Handle the tail:
214
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
215
+ nk_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
216
+ nk_f64_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
217
+ sum_real_f64 += ar * br + ai * bi;
218
+ sum_imag_f64 += ar * bi - ai * br;
219
+ }
220
+ result->real = sum_real_f64;
221
+ result->imag = sum_imag_f64;
222
+ }
223
+
224
+ /**
225
+ * @brief Running state for 64-bit dot accumulation over f32 scalars on NEON.
226
+ *
227
+ * Processes 2 f32 values at a time, upcasting to f64 for accumulation to avoid
228
+ * catastrophic cancellation in long reductions.
229
+ */
230
+ typedef struct nk_dot_f32x2_state_neon_t {
231
+ float64x2_t sum_f64x2;
232
+ } nk_dot_f32x2_state_neon_t;
233
+
234
+ NK_INTERNAL void nk_dot_f32x2_init_neon(nk_dot_f32x2_state_neon_t *state) { state->sum_f64x2 = vdupq_n_f64(0); }
235
+
236
+ NK_INTERNAL void nk_dot_f32x2_update_neon(nk_dot_f32x2_state_neon_t *state, nk_b64_vec_t a, nk_b64_vec_t b,
237
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
238
+ nk_unused_(depth_offset);
239
+ nk_unused_(active_dimensions);
240
+ // Upcast 2 f32s to f64s for high-precision accumulation
241
+ float32x2_t a_f32x2 = vreinterpret_f32_u32(a.u32x2);
242
+ float32x2_t b_f32x2 = vreinterpret_f32_u32(b.u32x2);
243
+ float64x2_t a_f64x2 = vcvt_f64_f32(a_f32x2);
244
+ float64x2_t b_f64x2 = vcvt_f64_f32(b_f32x2);
245
+ state->sum_f64x2 = vfmaq_f64(state->sum_f64x2, a_f64x2, b_f64x2);
246
+ }
247
+
248
+ NK_INTERNAL void nk_dot_f32x2_finalize_neon( //
249
+ nk_dot_f32x2_state_neon_t const *state_a, nk_dot_f32x2_state_neon_t const *state_b, //
250
+ nk_dot_f32x2_state_neon_t const *state_c, nk_dot_f32x2_state_neon_t const *state_d, //
251
+ nk_size_t total_dimensions, nk_b256_vec_t *result) {
252
+ nk_unused_(total_dimensions);
253
+ float64x2_t ab_f64x2 = vpaddq_f64(state_a->sum_f64x2, state_b->sum_f64x2);
254
+ float64x2_t cd_f64x2 = vpaddq_f64(state_c->sum_f64x2, state_d->sum_f64x2);
255
+ vst1q_f64(&result->f64s[0], ab_f64x2);
256
+ vst1q_f64(&result->f64s[2], cd_f64x2);
257
+ }
258
+
259
+ NK_PUBLIC void nk_dot_f64_neon(nk_f64_t const *a_scalars, nk_f64_t const *b_scalars, nk_size_t count_scalars,
260
+ nk_f64_t *result) {
261
+ // Dot2 algorithm (Ogita-Rump-Oishi 2005) for compensated dot product
262
+ float64x2_t sum_f64x2 = vdupq_n_f64(0);
263
+ float64x2_t compensation_f64x2 = vdupq_n_f64(0);
264
+ float64x2_t a_f64x2, b_f64x2;
265
+
266
+ nk_dot_f64_neon_cycle:
267
+ if (count_scalars < 2) {
268
+ nk_b128_vec_t a_tail, b_tail;
269
+ nk_partial_load_b64x2_serial_(a_scalars, &a_tail, count_scalars);
270
+ nk_partial_load_b64x2_serial_(b_scalars, &b_tail, count_scalars);
271
+ a_f64x2 = a_tail.f64x2;
272
+ b_f64x2 = b_tail.f64x2;
273
+ count_scalars = 0;
274
+ }
275
+ else {
276
+ a_f64x2 = vld1q_f64(a_scalars);
277
+ b_f64x2 = vld1q_f64(b_scalars);
278
+ a_scalars += 2, b_scalars += 2, count_scalars -= 2;
279
+ }
280
+
281
+ // TwoProd: h = a × b, r = fma(a, b, -h) captures the rounding error
282
+ float64x2_t product_f64x2 = vmulq_f64(a_f64x2, b_f64x2);
283
+ float64x2_t product_error_f64x2 = vnegq_f64(vfmsq_f64(product_f64x2, a_f64x2, b_f64x2));
284
+ // TwoSum: (t, q) = TwoSum(sum, h) where t = sum + h rounded, q = error
285
+ float64x2_t tentative_sum_f64x2 = vaddq_f64(sum_f64x2, product_f64x2);
286
+ float64x2_t virtual_addend_f64x2 = vsubq_f64(tentative_sum_f64x2, sum_f64x2);
287
+ float64x2_t sum_error_f64x2 = vaddq_f64(vsubq_f64(sum_f64x2, vsubq_f64(tentative_sum_f64x2, virtual_addend_f64x2)),
288
+ vsubq_f64(product_f64x2, virtual_addend_f64x2));
289
+ // Update: sum = t, compensation += q + r
290
+ sum_f64x2 = tentative_sum_f64x2;
291
+ compensation_f64x2 = vaddq_f64(compensation_f64x2, vaddq_f64(sum_error_f64x2, product_error_f64x2));
292
+
293
+ if (count_scalars) goto nk_dot_f64_neon_cycle;
294
+ // Compensated horizontal reduction preserving Dot2 error tracking
295
+ *result = nk_dot_stable_sum_f64x2_neon_(sum_f64x2, compensation_f64x2);
296
+ }
297
+
298
+ NK_PUBLIC void nk_dot_f64c_neon(nk_f64c_t const *a_pairs, nk_f64c_t const *b_pairs, nk_size_t count_pairs,
299
+ nk_f64c_t *result) {
300
+ // Dot2 algorithm (Ogita-Rump-Oishi 2005) for compensated complex dot product
301
+ float64x2_t sum_real_f64x2 = vdupq_n_f64(0);
302
+ float64x2_t sum_imag_f64x2 = vdupq_n_f64(0);
303
+ float64x2_t compensation_real_f64x2 = vdupq_n_f64(0);
304
+ float64x2_t compensation_imag_f64x2 = vdupq_n_f64(0);
305
+ float64x2_t a_real_f64x2, a_imag_f64x2, b_real_f64x2, b_imag_f64x2;
306
+
307
+ nk_dot_f64c_neon_cycle:
308
+ if (count_pairs < 2) {
309
+ nk_b128_vec_t a_tail, b_tail;
310
+ nk_partial_load_b64x2_serial_(a_pairs, &a_tail, count_pairs * 2);
311
+ nk_partial_load_b64x2_serial_(b_pairs, &b_tail, count_pairs * 2);
312
+ float64x2_t zeros_f64x2 = vdupq_n_f64(0);
313
+ a_real_f64x2 = vzip1q_f64(a_tail.f64x2, zeros_f64x2);
314
+ a_imag_f64x2 = vzip2q_f64(a_tail.f64x2, zeros_f64x2);
315
+ b_real_f64x2 = vzip1q_f64(b_tail.f64x2, zeros_f64x2);
316
+ b_imag_f64x2 = vzip2q_f64(b_tail.f64x2, zeros_f64x2);
317
+ count_pairs = 0;
318
+ }
319
+ else {
320
+ float64x2x2_t a_f64x2x2 = vld2q_f64((nk_f64_t const *)a_pairs);
321
+ float64x2x2_t b_f64x2x2 = vld2q_f64((nk_f64_t const *)b_pairs);
322
+ a_real_f64x2 = a_f64x2x2.val[0];
323
+ a_imag_f64x2 = a_f64x2x2.val[1];
324
+ b_real_f64x2 = b_f64x2x2.val[0];
325
+ b_imag_f64x2 = b_f64x2x2.val[1];
326
+ a_pairs += 2, b_pairs += 2, count_pairs -= 2;
327
+ }
328
+
329
+ // Real part: aᵣ × bᵣ - aᵢ × bᵢ (using TwoProd and TwoSum)
330
+ // First term: +aᵣ × bᵣ
331
+ float64x2_t product_rr_f64x2 = vmulq_f64(a_real_f64x2, b_real_f64x2);
332
+ float64x2_t error_rr_f64x2 = vnegq_f64(vfmsq_f64(product_rr_f64x2, a_real_f64x2, b_real_f64x2));
333
+ float64x2_t tentative_sum_real_f64x2 = vaddq_f64(sum_real_f64x2, product_rr_f64x2);
334
+ float64x2_t virtual_addend_real_f64x2 = vsubq_f64(tentative_sum_real_f64x2, sum_real_f64x2);
335
+ float64x2_t error_sum_real_f64x2 = vaddq_f64(
336
+ vsubq_f64(sum_real_f64x2, vsubq_f64(tentative_sum_real_f64x2, virtual_addend_real_f64x2)),
337
+ vsubq_f64(product_rr_f64x2, virtual_addend_real_f64x2));
338
+ sum_real_f64x2 = tentative_sum_real_f64x2;
339
+ compensation_real_f64x2 = vaddq_f64(compensation_real_f64x2, vaddq_f64(error_sum_real_f64x2, error_rr_f64x2));
340
+ // Second term: -aᵢ × bᵢ (negate product and error, then standard TwoSum)
341
+ float64x2_t product_ii_f64x2 = vmulq_f64(a_imag_f64x2, b_imag_f64x2);
342
+ float64x2_t error_ii_f64x2 = vnegq_f64(vfmsq_f64(product_ii_f64x2, a_imag_f64x2, b_imag_f64x2));
343
+ float64x2_t neg_product_ii_f64x2 = vnegq_f64(product_ii_f64x2);
344
+ float64x2_t neg_error_ii_f64x2 = vnegq_f64(error_ii_f64x2);
345
+ tentative_sum_real_f64x2 = vaddq_f64(sum_real_f64x2, neg_product_ii_f64x2);
346
+ virtual_addend_real_f64x2 = vsubq_f64(tentative_sum_real_f64x2, sum_real_f64x2);
347
+ error_sum_real_f64x2 = vaddq_f64(
348
+ vsubq_f64(sum_real_f64x2, vsubq_f64(tentative_sum_real_f64x2, virtual_addend_real_f64x2)),
349
+ vsubq_f64(neg_product_ii_f64x2, virtual_addend_real_f64x2));
350
+ sum_real_f64x2 = tentative_sum_real_f64x2;
351
+ compensation_real_f64x2 = vaddq_f64(compensation_real_f64x2, vaddq_f64(error_sum_real_f64x2, neg_error_ii_f64x2));
352
+
353
+ // Imag part: aᵣ × bᵢ + aᵢ × bᵣ (using TwoProd and TwoSum)
354
+ // First term: +aᵣ × bᵢ
355
+ float64x2_t product_ri_f64x2 = vmulq_f64(a_real_f64x2, b_imag_f64x2);
356
+ float64x2_t error_ri_f64x2 = vnegq_f64(vfmsq_f64(product_ri_f64x2, a_real_f64x2, b_imag_f64x2));
357
+ float64x2_t tentative_sum_imag_f64x2 = vaddq_f64(sum_imag_f64x2, product_ri_f64x2);
358
+ float64x2_t virtual_addend_imag_f64x2 = vsubq_f64(tentative_sum_imag_f64x2, sum_imag_f64x2);
359
+ float64x2_t error_sum_imag_f64x2 = vaddq_f64(
360
+ vsubq_f64(sum_imag_f64x2, vsubq_f64(tentative_sum_imag_f64x2, virtual_addend_imag_f64x2)),
361
+ vsubq_f64(product_ri_f64x2, virtual_addend_imag_f64x2));
362
+ sum_imag_f64x2 = tentative_sum_imag_f64x2;
363
+ compensation_imag_f64x2 = vaddq_f64(compensation_imag_f64x2, vaddq_f64(error_sum_imag_f64x2, error_ri_f64x2));
364
+ // Second term: +aᵢ × bᵣ
365
+ float64x2_t product_ir_f64x2 = vmulq_f64(a_imag_f64x2, b_real_f64x2);
366
+ float64x2_t error_ir_f64x2 = vnegq_f64(vfmsq_f64(product_ir_f64x2, a_imag_f64x2, b_real_f64x2));
367
+ tentative_sum_imag_f64x2 = vaddq_f64(sum_imag_f64x2, product_ir_f64x2);
368
+ virtual_addend_imag_f64x2 = vsubq_f64(tentative_sum_imag_f64x2, sum_imag_f64x2);
369
+ error_sum_imag_f64x2 = vaddq_f64(
370
+ vsubq_f64(sum_imag_f64x2, vsubq_f64(tentative_sum_imag_f64x2, virtual_addend_imag_f64x2)),
371
+ vsubq_f64(product_ir_f64x2, virtual_addend_imag_f64x2));
372
+ sum_imag_f64x2 = tentative_sum_imag_f64x2;
373
+ compensation_imag_f64x2 = vaddq_f64(compensation_imag_f64x2, vaddq_f64(error_sum_imag_f64x2, error_ir_f64x2));
374
+
375
+ if (count_pairs) goto nk_dot_f64c_neon_cycle;
376
+ // Compensated horizontal reduction preserving Dot2 error tracking
377
+ result->real = nk_dot_stable_sum_f64x2_neon_(sum_real_f64x2, compensation_real_f64x2);
378
+ result->imag = nk_dot_stable_sum_f64x2_neon_(sum_imag_f64x2, compensation_imag_f64x2);
379
+ }
380
+
381
+ NK_PUBLIC void nk_vdot_f64c_neon(nk_f64c_t const *a_pairs, nk_f64c_t const *b_pairs, nk_size_t count_pairs,
382
+ nk_f64c_t *result) {
383
+ // Dot2 algorithm (Ogita-Rump-Oishi 2005) for compensated conjugate dot product
384
+ float64x2_t sum_real_f64x2 = vdupq_n_f64(0);
385
+ float64x2_t sum_imag_f64x2 = vdupq_n_f64(0);
386
+ float64x2_t compensation_real_f64x2 = vdupq_n_f64(0);
387
+ float64x2_t compensation_imag_f64x2 = vdupq_n_f64(0);
388
+ float64x2_t a_real_f64x2, a_imag_f64x2, b_real_f64x2, b_imag_f64x2;
389
+
390
+ nk_vdot_f64c_neon_cycle:
391
+ if (count_pairs < 2) {
392
+ nk_b128_vec_t a_tail, b_tail;
393
+ nk_partial_load_b64x2_serial_(a_pairs, &a_tail, count_pairs * 2);
394
+ nk_partial_load_b64x2_serial_(b_pairs, &b_tail, count_pairs * 2);
395
+ float64x2_t zeros_f64x2 = vdupq_n_f64(0);
396
+ a_real_f64x2 = vzip1q_f64(a_tail.f64x2, zeros_f64x2);
397
+ a_imag_f64x2 = vzip2q_f64(a_tail.f64x2, zeros_f64x2);
398
+ b_real_f64x2 = vzip1q_f64(b_tail.f64x2, zeros_f64x2);
399
+ b_imag_f64x2 = vzip2q_f64(b_tail.f64x2, zeros_f64x2);
400
+ count_pairs = 0;
401
+ }
402
+ else {
403
+ float64x2x2_t a_f64x2x2 = vld2q_f64((nk_f64_t const *)a_pairs);
404
+ float64x2x2_t b_f64x2x2 = vld2q_f64((nk_f64_t const *)b_pairs);
405
+ a_real_f64x2 = a_f64x2x2.val[0];
406
+ a_imag_f64x2 = a_f64x2x2.val[1];
407
+ b_real_f64x2 = b_f64x2x2.val[0];
408
+ b_imag_f64x2 = b_f64x2x2.val[1];
409
+ a_pairs += 2, b_pairs += 2, count_pairs -= 2;
410
+ }
411
+
412
+ // Real part: aᵣ × bᵣ + aᵢ × bᵢ (using TwoProd and TwoSum)
413
+ // First term: +aᵣ × bᵣ
414
+ float64x2_t product_rr_f64x2 = vmulq_f64(a_real_f64x2, b_real_f64x2);
415
+ float64x2_t error_rr_f64x2 = vnegq_f64(vfmsq_f64(product_rr_f64x2, a_real_f64x2, b_real_f64x2));
416
+ float64x2_t tentative_sum_real_f64x2 = vaddq_f64(sum_real_f64x2, product_rr_f64x2);
417
+ float64x2_t virtual_addend_real_f64x2 = vsubq_f64(tentative_sum_real_f64x2, sum_real_f64x2);
418
+ float64x2_t error_sum_real_f64x2 = vaddq_f64(
419
+ vsubq_f64(sum_real_f64x2, vsubq_f64(tentative_sum_real_f64x2, virtual_addend_real_f64x2)),
420
+ vsubq_f64(product_rr_f64x2, virtual_addend_real_f64x2));
421
+ sum_real_f64x2 = tentative_sum_real_f64x2;
422
+ compensation_real_f64x2 = vaddq_f64(compensation_real_f64x2, vaddq_f64(error_sum_real_f64x2, error_rr_f64x2));
423
+ // Second term: +aᵢ × bᵢ (conjugate: add instead of subtract)
424
+ float64x2_t product_ii_f64x2 = vmulq_f64(a_imag_f64x2, b_imag_f64x2);
425
+ float64x2_t error_ii_f64x2 = vnegq_f64(vfmsq_f64(product_ii_f64x2, a_imag_f64x2, b_imag_f64x2));
426
+ tentative_sum_real_f64x2 = vaddq_f64(sum_real_f64x2, product_ii_f64x2);
427
+ virtual_addend_real_f64x2 = vsubq_f64(tentative_sum_real_f64x2, sum_real_f64x2);
428
+ error_sum_real_f64x2 = vaddq_f64(
429
+ vsubq_f64(sum_real_f64x2, vsubq_f64(tentative_sum_real_f64x2, virtual_addend_real_f64x2)),
430
+ vsubq_f64(product_ii_f64x2, virtual_addend_real_f64x2));
431
+ sum_real_f64x2 = tentative_sum_real_f64x2;
432
+ compensation_real_f64x2 = vaddq_f64(compensation_real_f64x2, vaddq_f64(error_sum_real_f64x2, error_ii_f64x2));
433
+
434
+ // Imag part: aᵣ × bᵢ - aᵢ × bᵣ (using TwoProd and TwoSum)
435
+ // First term: +aᵣ × bᵢ
436
+ float64x2_t product_ri_f64x2 = vmulq_f64(a_real_f64x2, b_imag_f64x2);
437
+ float64x2_t error_ri_f64x2 = vnegq_f64(vfmsq_f64(product_ri_f64x2, a_real_f64x2, b_imag_f64x2));
438
+ float64x2_t tentative_sum_imag_f64x2 = vaddq_f64(sum_imag_f64x2, product_ri_f64x2);
439
+ float64x2_t virtual_addend_imag_f64x2 = vsubq_f64(tentative_sum_imag_f64x2, sum_imag_f64x2);
440
+ float64x2_t error_sum_imag_f64x2 = vaddq_f64(
441
+ vsubq_f64(sum_imag_f64x2, vsubq_f64(tentative_sum_imag_f64x2, virtual_addend_imag_f64x2)),
442
+ vsubq_f64(product_ri_f64x2, virtual_addend_imag_f64x2));
443
+ sum_imag_f64x2 = tentative_sum_imag_f64x2;
444
+ compensation_imag_f64x2 = vaddq_f64(compensation_imag_f64x2, vaddq_f64(error_sum_imag_f64x2, error_ri_f64x2));
445
+ // Second term: -aᵢ × bᵣ (conjugate: negate product and error, then standard TwoSum)
446
+ float64x2_t product_ir_f64x2 = vmulq_f64(a_imag_f64x2, b_real_f64x2);
447
+ float64x2_t error_ir_f64x2 = vnegq_f64(vfmsq_f64(product_ir_f64x2, a_imag_f64x2, b_real_f64x2));
448
+ float64x2_t neg_product_ir_f64x2 = vnegq_f64(product_ir_f64x2);
449
+ float64x2_t neg_error_ir_f64x2 = vnegq_f64(error_ir_f64x2);
450
+ tentative_sum_imag_f64x2 = vaddq_f64(sum_imag_f64x2, neg_product_ir_f64x2);
451
+ virtual_addend_imag_f64x2 = vsubq_f64(tentative_sum_imag_f64x2, sum_imag_f64x2);
452
+ error_sum_imag_f64x2 = vaddq_f64(
453
+ vsubq_f64(sum_imag_f64x2, vsubq_f64(tentative_sum_imag_f64x2, virtual_addend_imag_f64x2)),
454
+ vsubq_f64(neg_product_ir_f64x2, virtual_addend_imag_f64x2));
455
+ sum_imag_f64x2 = tentative_sum_imag_f64x2;
456
+ compensation_imag_f64x2 = vaddq_f64(compensation_imag_f64x2, vaddq_f64(error_sum_imag_f64x2, neg_error_ir_f64x2));
457
+
458
+ if (count_pairs) goto nk_vdot_f64c_neon_cycle;
459
+ // Compensated horizontal reduction preserving Dot2 error tracking
460
+ result->real = nk_dot_stable_sum_f64x2_neon_(sum_real_f64x2, compensation_real_f64x2);
461
+ result->imag = nk_dot_stable_sum_f64x2_neon_(sum_imag_f64x2, compensation_imag_f64x2);
462
+ }
463
+
464
+ /**
465
+ * @brief Running state for 128-bit dot accumulation over f64 scalars on NEON.
466
+ *
467
+ * Uses the Dot2 algorithm (Ogita-Rump-Oishi 2005) for compensated dot product.
468
+ */
469
+ typedef struct nk_dot_f64x2_state_neon_t {
470
+ float64x2_t sum_f64x2;
471
+ float64x2_t compensation_f64x2;
472
+ } nk_dot_f64x2_state_neon_t;
473
+
474
+ NK_INTERNAL void nk_dot_f64x2_init_neon(nk_dot_f64x2_state_neon_t *state) {
475
+ state->sum_f64x2 = vdupq_n_f64(0);
476
+ state->compensation_f64x2 = vdupq_n_f64(0);
477
+ }
478
+
479
+ NK_INTERNAL void nk_dot_f64x2_update_neon(nk_dot_f64x2_state_neon_t *state, nk_b128_vec_t a, nk_b128_vec_t b,
480
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
481
+ nk_unused_(depth_offset);
482
+ nk_unused_(active_dimensions);
483
+ float64x2_t sum_f64x2 = state->sum_f64x2;
484
+ float64x2_t compensation_f64x2 = state->compensation_f64x2;
485
+ float64x2_t a_f64x2 = vreinterpretq_f64_u64(a.u64x2);
486
+ float64x2_t b_f64x2 = vreinterpretq_f64_u64(b.u64x2);
487
+
488
+ // TwoProd: h = a × b, r = fma(a, b, -h) captures the rounding error
489
+ float64x2_t product_f64x2 = vmulq_f64(a_f64x2, b_f64x2);
490
+ float64x2_t product_error_f64x2 = vnegq_f64(vfmsq_f64(product_f64x2, a_f64x2, b_f64x2));
491
+
492
+ // TwoSum: (t, q) = TwoSum(sum, h) where t = sum + h rounded, q = error
493
+ float64x2_t tentative_sum_f64x2 = vaddq_f64(sum_f64x2, product_f64x2);
494
+ float64x2_t virtual_addend_f64x2 = vsubq_f64(tentative_sum_f64x2, sum_f64x2);
495
+ float64x2_t sum_error_f64x2 = vaddq_f64(vsubq_f64(sum_f64x2, vsubq_f64(tentative_sum_f64x2, virtual_addend_f64x2)),
496
+ vsubq_f64(product_f64x2, virtual_addend_f64x2));
497
+
498
+ // Update: sum = t, compensation += q + r
499
+ state->sum_f64x2 = tentative_sum_f64x2;
500
+ state->compensation_f64x2 = vaddq_f64(compensation_f64x2, vaddq_f64(sum_error_f64x2, product_error_f64x2));
501
+ }
502
+
503
+ NK_INTERNAL void nk_dot_f64x2_finalize_neon( //
504
+ nk_dot_f64x2_state_neon_t const *state_a, nk_dot_f64x2_state_neon_t const *state_b, //
505
+ nk_dot_f64x2_state_neon_t const *state_c, nk_dot_f64x2_state_neon_t const *state_d, //
506
+ nk_size_t total_dimensions, nk_b256_vec_t *result) {
507
+ nk_unused_(total_dimensions);
508
+ // Compensated horizontal reduction preserving Dot2 error tracking per state
509
+ result->f64s[0] = nk_dot_stable_sum_f64x2_neon_(state_a->sum_f64x2, state_a->compensation_f64x2);
510
+ result->f64s[1] = nk_dot_stable_sum_f64x2_neon_(state_b->sum_f64x2, state_b->compensation_f64x2);
511
+ result->f64s[2] = nk_dot_stable_sum_f64x2_neon_(state_c->sum_f64x2, state_c->compensation_f64x2);
512
+ result->f64s[3] = nk_dot_stable_sum_f64x2_neon_(state_d->sum_f64x2, state_d->compensation_f64x2);
513
+ }
514
+
515
+ #pragma endregion F32 and F64 Floats
516
+
517
+ #pragma region F16 and BF16 Floats
518
+
519
+ NK_PUBLIC void nk_dot_bf16_neon(nk_bf16_t const *a_scalars, nk_bf16_t const *b_scalars, nk_size_t count_scalars,
520
+ nk_f32_t *result) {
521
+ uint16x8_t a_u16x8, b_u16x8;
522
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
523
+ nk_dot_bf16_neon_cycle:
524
+ if (count_scalars < 8) {
525
+ nk_b128_vec_t a_vec, b_vec;
526
+ nk_partial_load_b16x8_serial_(a_scalars, &a_vec, count_scalars);
527
+ nk_partial_load_b16x8_serial_(b_scalars, &b_vec, count_scalars);
528
+ a_u16x8 = a_vec.u16x8;
529
+ b_u16x8 = b_vec.u16x8;
530
+ count_scalars = 0;
531
+ }
532
+ else {
533
+ a_u16x8 = vld1q_u16((nk_u16_t const *)a_scalars);
534
+ b_u16x8 = vld1q_u16((nk_u16_t const *)b_scalars);
535
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
536
+ }
537
+ float32x4_t a_low_f32x4 = vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a_u16x8), 16));
538
+ float32x4_t a_high_f32x4 = vreinterpretq_f32_u32(vshll_high_n_u16(a_u16x8, 16));
539
+ float32x4_t b_low_f32x4 = vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(b_u16x8), 16));
540
+ float32x4_t b_high_f32x4 = vreinterpretq_f32_u32(vshll_high_n_u16(b_u16x8, 16));
541
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_low_f32x4, b_low_f32x4);
542
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_high_f32x4, b_high_f32x4);
543
+ if (count_scalars) goto nk_dot_bf16_neon_cycle;
544
+ *result = vaddvq_f32(sum_f32x4);
545
+ }
546
+
547
+ /**
548
+ * @brief Running state for 128-bit dot accumulation over bf16 scalars on plain NEON.
549
+ *
550
+ * Processes 8 bf16 values at a time (128 bits), converting to f32 via USHLL shift-16
551
+ * for accumulation without requiring the ARMv8.6-BF16 extension.
552
+ */
553
+ typedef struct nk_dot_bf16x8_state_neon_t {
554
+ float32x4_t sum_f32x4;
555
+ } nk_dot_bf16x8_state_neon_t;
556
+
557
+ NK_INTERNAL void nk_dot_bf16x8_init_neon(nk_dot_bf16x8_state_neon_t *state) { state->sum_f32x4 = vdupq_n_f32(0); }
558
+
559
+ NK_INTERNAL void nk_dot_bf16x8_update_neon(nk_dot_bf16x8_state_neon_t *state, nk_b128_vec_t a, nk_b128_vec_t b,
560
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
561
+ nk_unused_(depth_offset);
562
+ nk_unused_(active_dimensions);
563
+ // Convert bf16 to f32 via USHLL shift-16 (low and high halves)
564
+ float32x4_t a_low_f32x4 = vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(a.u16x8), 16));
565
+ float32x4_t a_high_f32x4 = vreinterpretq_f32_u32(vshll_high_n_u16(a.u16x8, 16));
566
+ float32x4_t b_low_f32x4 = vreinterpretq_f32_u32(vshll_n_u16(vget_low_u16(b.u16x8), 16));
567
+ float32x4_t b_high_f32x4 = vreinterpretq_f32_u32(vshll_high_n_u16(b.u16x8, 16));
568
+ state->sum_f32x4 = vfmaq_f32(state->sum_f32x4, a_low_f32x4, b_low_f32x4);
569
+ state->sum_f32x4 = vfmaq_f32(state->sum_f32x4, a_high_f32x4, b_high_f32x4);
570
+ }
571
+
572
+ NK_INTERNAL void nk_dot_bf16x8_finalize_neon( //
573
+ nk_dot_bf16x8_state_neon_t const *state_a, nk_dot_bf16x8_state_neon_t const *state_b, //
574
+ nk_dot_bf16x8_state_neon_t const *state_c, nk_dot_bf16x8_state_neon_t const *state_d, //
575
+ nk_size_t total_dimensions, nk_b128_vec_t *result) {
576
+ nk_unused_(total_dimensions);
577
+ float32x4_t ab_f32x4 = vpaddq_f32(state_a->sum_f32x4, state_b->sum_f32x4);
578
+ float32x4_t cd_f32x4 = vpaddq_f32(state_c->sum_f32x4, state_d->sum_f32x4);
579
+ result->f32x4 = vpaddq_f32(ab_f32x4, cd_f32x4);
580
+ }
581
+
582
+ NK_PUBLIC void nk_dot_f16_neon(nk_f16_t const *a_scalars, nk_f16_t const *b_scalars, nk_size_t count_scalars,
583
+ nk_f32_t *result) {
584
+ uint16x8_t a_u16x8, b_u16x8;
585
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
586
+ nk_dot_f16_neon_cycle:
587
+ if (count_scalars < 8) {
588
+ nk_b128_vec_t a_vec, b_vec;
589
+ nk_partial_load_b16x8_serial_(a_scalars, &a_vec, count_scalars);
590
+ nk_partial_load_b16x8_serial_(b_scalars, &b_vec, count_scalars);
591
+ a_u16x8 = a_vec.u16x8;
592
+ b_u16x8 = b_vec.u16x8;
593
+ count_scalars = 0;
594
+ }
595
+ else {
596
+ a_u16x8 = vld1q_u16((nk_u16_t const *)a_scalars);
597
+ b_u16x8 = vld1q_u16((nk_u16_t const *)b_scalars);
598
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
599
+ }
600
+ float16x8_t a_f16x8 = vreinterpretq_f16_u16(a_u16x8);
601
+ float16x8_t b_f16x8 = vreinterpretq_f16_u16(b_u16x8);
602
+ float32x4_t a_low_f32x4 = vcvt_f32_f16(vget_low_f16(a_f16x8));
603
+ float32x4_t a_high_f32x4 = vcvt_high_f32_f16(a_f16x8);
604
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
605
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
606
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_low_f32x4, b_low_f32x4);
607
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_high_f32x4, b_high_f32x4);
608
+ if (count_scalars) goto nk_dot_f16_neon_cycle;
609
+ *result = vaddvq_f32(sum_f32x4);
610
+ }
611
+
612
+ /**
613
+ * @brief Running state for 128-bit dot accumulation over f16 scalars on plain NEON.
614
+ *
615
+ * Processes 8 f16 values at a time (128 bits), converting to f32 via FCVTL
616
+ * for accumulation without requiring the ARMv8.2-A FP16 arithmetic extension.
617
+ */
618
+ typedef struct nk_dot_f16x8_state_neon_t {
619
+ float32x4_t sum_f32x4;
620
+ } nk_dot_f16x8_state_neon_t;
621
+
622
+ NK_INTERNAL void nk_dot_f16x8_init_neon(nk_dot_f16x8_state_neon_t *state) { state->sum_f32x4 = vdupq_n_f32(0); }
623
+
624
+ NK_INTERNAL void nk_dot_f16x8_update_neon(nk_dot_f16x8_state_neon_t *state, nk_b128_vec_t a, nk_b128_vec_t b,
625
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
626
+ nk_unused_(depth_offset);
627
+ nk_unused_(active_dimensions);
628
+ // Convert f16 to f32 via FCVTL / FCVTL2 (low and high halves)
629
+ float16x8_t a_f16x8 = vreinterpretq_f16_u16(a.u16x8);
630
+ float16x8_t b_f16x8 = vreinterpretq_f16_u16(b.u16x8);
631
+ float32x4_t a_low_f32x4 = vcvt_f32_f16(vget_low_f16(a_f16x8));
632
+ float32x4_t a_high_f32x4 = vcvt_high_f32_f16(a_f16x8);
633
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
634
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
635
+ state->sum_f32x4 = vfmaq_f32(state->sum_f32x4, a_low_f32x4, b_low_f32x4);
636
+ state->sum_f32x4 = vfmaq_f32(state->sum_f32x4, a_high_f32x4, b_high_f32x4);
637
+ }
638
+
639
+ NK_INTERNAL void nk_dot_f16x8_finalize_neon( //
640
+ nk_dot_f16x8_state_neon_t const *state_a, nk_dot_f16x8_state_neon_t const *state_b, //
641
+ nk_dot_f16x8_state_neon_t const *state_c, nk_dot_f16x8_state_neon_t const *state_d, //
642
+ nk_size_t total_dimensions, nk_b128_vec_t *result) {
643
+ nk_unused_(total_dimensions);
644
+ float32x4_t ab_f32x4 = vpaddq_f32(state_a->sum_f32x4, state_b->sum_f32x4);
645
+ float32x4_t cd_f32x4 = vpaddq_f32(state_c->sum_f32x4, state_d->sum_f32x4);
646
+ result->f32x4 = vpaddq_f32(ab_f32x4, cd_f32x4);
647
+ }
648
+
649
+ NK_PUBLIC void nk_dot_e4m3_neon(nk_e4m3_t const *a_scalars, nk_e4m3_t const *b_scalars, nk_size_t count_scalars,
650
+ nk_f32_t *result) {
651
+ float16x8_t a_f16x8, b_f16x8;
652
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
653
+ nk_dot_e4m3_neon_cycle:
654
+ if (count_scalars < 8) {
655
+ nk_b64_vec_t a_vec, b_vec;
656
+ nk_partial_load_b8x8_serial_(a_scalars, &a_vec, count_scalars);
657
+ nk_partial_load_b8x8_serial_(b_scalars, &b_vec, count_scalars);
658
+ a_f16x8 = nk_e4m3x8_to_f16x8_neon_(a_vec.u8x8);
659
+ b_f16x8 = nk_e4m3x8_to_f16x8_neon_(b_vec.u8x8);
660
+ count_scalars = 0;
661
+ }
662
+ else {
663
+ a_f16x8 = nk_e4m3x8_to_f16x8_neon_(vld1_u8(a_scalars));
664
+ b_f16x8 = nk_e4m3x8_to_f16x8_neon_(vld1_u8(b_scalars));
665
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
666
+ }
667
+ float32x4_t a_low_f32x4 = vcvt_f32_f16(vget_low_f16(a_f16x8));
668
+ float32x4_t a_high_f32x4 = vcvt_high_f32_f16(a_f16x8);
669
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
670
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
671
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_low_f32x4, b_low_f32x4);
672
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_high_f32x4, b_high_f32x4);
673
+ if (count_scalars) goto nk_dot_e4m3_neon_cycle;
674
+ *result = vaddvq_f32(sum_f32x4);
675
+ }
676
+
677
+ NK_PUBLIC void nk_dot_e5m2_neon(nk_e5m2_t const *a_scalars, nk_e5m2_t const *b_scalars, nk_size_t count_scalars,
678
+ nk_f32_t *result) {
679
+ float16x8_t a_f16x8, b_f16x8;
680
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
681
+ nk_dot_e5m2_neon_cycle:
682
+ if (count_scalars < 8) {
683
+ nk_b64_vec_t a_vec, b_vec;
684
+ nk_partial_load_b8x8_serial_(a_scalars, &a_vec, count_scalars);
685
+ nk_partial_load_b8x8_serial_(b_scalars, &b_vec, count_scalars);
686
+ a_f16x8 = vreinterpretq_f16_u16(vshll_n_u8(a_vec.u8x8, 8));
687
+ b_f16x8 = vreinterpretq_f16_u16(vshll_n_u8(b_vec.u8x8, 8));
688
+ count_scalars = 0;
689
+ }
690
+ else {
691
+ a_f16x8 = vreinterpretq_f16_u16(vshll_n_u8(vld1_u8(a_scalars), 8));
692
+ b_f16x8 = vreinterpretq_f16_u16(vshll_n_u8(vld1_u8(b_scalars), 8));
693
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
694
+ }
695
+ float32x4_t a_low_f32x4 = vcvt_f32_f16(vget_low_f16(a_f16x8));
696
+ float32x4_t a_high_f32x4 = vcvt_high_f32_f16(a_f16x8);
697
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
698
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
699
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_low_f32x4, b_low_f32x4);
700
+ sum_f32x4 = vfmaq_f32(sum_f32x4, a_high_f32x4, b_high_f32x4);
701
+ if (count_scalars) goto nk_dot_e5m2_neon_cycle;
702
+ *result = vaddvq_f32(sum_f32x4);
703
+ }
704
+
705
+ NK_PUBLIC void nk_dot_e2m3_neon(nk_e2m3_t const *a_scalars, nk_e2m3_t const *b_scalars, nk_size_t count_scalars,
706
+ nk_f32_t *result) {
707
+ float16x8_t a_low_f16x8, a_high_f16x8, b_low_f16x8, b_high_f16x8;
708
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
709
+ // x16 TBL path: process 16 elements per iteration via lookup table upcast
710
+ nk_dot_e2m3_neon_cycle:
711
+ if (count_scalars < 16) {
712
+ nk_b128_vec_t a_vec, b_vec;
713
+ nk_partial_load_b8x16_serial_(a_scalars, &a_vec, count_scalars);
714
+ nk_partial_load_b8x16_serial_(b_scalars, &b_vec, count_scalars);
715
+ nk_e2m3x16_to_f16x8x2_neon_(a_vec.u8x16, &a_low_f16x8, &a_high_f16x8);
716
+ nk_e2m3x16_to_f16x8x2_neon_(b_vec.u8x16, &b_low_f16x8, &b_high_f16x8);
717
+ count_scalars = 0;
718
+ }
719
+ else {
720
+ nk_e2m3x16_to_f16x8x2_neon_(vld1q_u8(a_scalars), &a_low_f16x8, &a_high_f16x8);
721
+ nk_e2m3x16_to_f16x8x2_neon_(vld1q_u8(b_scalars), &b_low_f16x8, &b_high_f16x8);
722
+ a_scalars += 16, b_scalars += 16, count_scalars -= 16;
723
+ }
724
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_f32_f16(vget_low_f16(a_low_f16x8)), vcvt_f32_f16(vget_low_f16(b_low_f16x8)));
725
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_high_f32_f16(a_low_f16x8), vcvt_high_f32_f16(b_low_f16x8));
726
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_f32_f16(vget_low_f16(a_high_f16x8)),
727
+ vcvt_f32_f16(vget_low_f16(b_high_f16x8)));
728
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_high_f32_f16(a_high_f16x8), vcvt_high_f32_f16(b_high_f16x8));
729
+ if (count_scalars) goto nk_dot_e2m3_neon_cycle;
730
+ *result = vaddvq_f32(sum_f32x4);
731
+ }
732
+
733
+ NK_PUBLIC void nk_dot_e3m2_neon(nk_e3m2_t const *a_scalars, nk_e3m2_t const *b_scalars, nk_size_t count_scalars,
734
+ nk_f32_t *result) {
735
+ float16x8_t a_low_f16x8, a_high_f16x8, b_low_f16x8, b_high_f16x8;
736
+ float32x4_t sum_f32x4 = vdupq_n_f32(0);
737
+ // x16 TBL path: process 16 elements per iteration via lookup table upcast
738
+ nk_dot_e3m2_neon_cycle:
739
+ if (count_scalars < 16) {
740
+ nk_b128_vec_t a_vec, b_vec;
741
+ nk_partial_load_b8x16_serial_(a_scalars, &a_vec, count_scalars);
742
+ nk_partial_load_b8x16_serial_(b_scalars, &b_vec, count_scalars);
743
+ nk_e3m2x16_to_f16x8x2_neon_(a_vec.u8x16, &a_low_f16x8, &a_high_f16x8);
744
+ nk_e3m2x16_to_f16x8x2_neon_(b_vec.u8x16, &b_low_f16x8, &b_high_f16x8);
745
+ count_scalars = 0;
746
+ }
747
+ else {
748
+ nk_e3m2x16_to_f16x8x2_neon_(vld1q_u8(a_scalars), &a_low_f16x8, &a_high_f16x8);
749
+ nk_e3m2x16_to_f16x8x2_neon_(vld1q_u8(b_scalars), &b_low_f16x8, &b_high_f16x8);
750
+ a_scalars += 16, b_scalars += 16, count_scalars -= 16;
751
+ }
752
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_f32_f16(vget_low_f16(a_low_f16x8)), vcvt_f32_f16(vget_low_f16(b_low_f16x8)));
753
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_high_f32_f16(a_low_f16x8), vcvt_high_f32_f16(b_low_f16x8));
754
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_f32_f16(vget_low_f16(a_high_f16x8)),
755
+ vcvt_f32_f16(vget_low_f16(b_high_f16x8)));
756
+ sum_f32x4 = vfmaq_f32(sum_f32x4, vcvt_high_f32_f16(a_high_f16x8), vcvt_high_f32_f16(b_high_f16x8));
757
+ if (count_scalars) goto nk_dot_e3m2_neon_cycle;
758
+ *result = vaddvq_f32(sum_f32x4);
759
+ }
760
+
761
+ #pragma endregion F16 and BF16 Floats
762
+
763
+ #pragma region Binary
764
+
765
+ NK_PUBLIC void nk_dot_u1_neon(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n_bits, nk_u32_t *result) {
766
+ nk_size_t n_bytes = nk_size_divide_round_up_(n_bits, NK_BITS_PER_BYTE);
767
+ nk_u32_t dot = 0;
768
+ nk_size_t i = 0;
769
+ while (i + 16 <= n_bytes) {
770
+ uint8x16_t popcount_u8x16 = vdupq_n_u8(0);
771
+ for (nk_size_t cycle = 0; cycle < 31 && i + 16 <= n_bytes; ++cycle, i += 16) {
772
+ uint8x16_t a_u8x16 = vld1q_u8(a + i);
773
+ uint8x16_t b_u8x16 = vld1q_u8(b + i);
774
+ popcount_u8x16 = vaddq_u8(popcount_u8x16, vcntq_u8(vandq_u8(a_u8x16, b_u8x16)));
775
+ }
776
+ dot += (nk_u32_t)vaddlvq_u8(popcount_u8x16);
777
+ }
778
+ for (; i != n_bytes; ++i) dot += nk_u1x8_popcount_(a[i] & b[i]);
779
+ *result = dot;
780
+ }
781
+
782
+ typedef struct nk_dot_u1x128_state_neon_t {
783
+ uint32x4_t dot_count_u32x4;
784
+ } nk_dot_u1x128_state_neon_t;
785
+
786
+ NK_INTERNAL void nk_dot_u1x128_init_neon(nk_dot_u1x128_state_neon_t *state) { state->dot_count_u32x4 = vdupq_n_u32(0); }
787
+
788
+ NK_INTERNAL void nk_dot_u1x128_update_neon(nk_dot_u1x128_state_neon_t *state, nk_b128_vec_t a, nk_b128_vec_t b,
789
+ nk_size_t depth_offset, nk_size_t active_dimensions) {
790
+ nk_unused_(depth_offset);
791
+ nk_unused_(active_dimensions);
792
+ uint8x16_t and_u8x16 = vandq_u8(a.u8x16, b.u8x16);
793
+ uint8x16_t popcount_u8x16 = vcntq_u8(and_u8x16);
794
+ uint16x8_t popcount_u16x8 = vpaddlq_u8(popcount_u8x16);
795
+ uint32x4_t popcount_u32x4 = vpaddlq_u16(popcount_u16x8);
796
+ state->dot_count_u32x4 = vaddq_u32(state->dot_count_u32x4, popcount_u32x4);
797
+ }
798
+
799
+ NK_INTERNAL void nk_dot_u1x128_finalize_neon( //
800
+ nk_dot_u1x128_state_neon_t const *state_a, nk_dot_u1x128_state_neon_t const *state_b,
801
+ nk_dot_u1x128_state_neon_t const *state_c, nk_dot_u1x128_state_neon_t const *state_d, nk_size_t total_dimensions,
802
+ nk_b128_vec_t *result) {
803
+ nk_unused_(total_dimensions);
804
+ uint32x4_t ab_sum_u32x4 = vpaddq_u32(state_a->dot_count_u32x4, state_b->dot_count_u32x4);
805
+ uint32x4_t cd_sum_u32x4 = vpaddq_u32(state_c->dot_count_u32x4, state_d->dot_count_u32x4);
806
+ result->u32x4 = vpaddq_u32(ab_sum_u32x4, cd_sum_u32x4);
807
+ }
808
+
809
+ #pragma endregion Binary
810
+
811
+ NK_PUBLIC void nk_dot_f16c_neon(nk_f16c_t const *a_pairs, nk_f16c_t const *b_pairs, nk_size_t count_pairs,
812
+ nk_f32c_t *result) {
813
+ float32x4_t sum_real_f32x4 = vdupq_n_f32(0);
814
+ float32x4_t sum_imag_f32x4 = vdupq_n_f32(0);
815
+ while (count_pairs >= 4) {
816
+ int16x4x2_t a_i16x4x2 = vld2_s16((short *)a_pairs);
817
+ int16x4x2_t b_i16x4x2 = vld2_s16((short *)b_pairs);
818
+ float32x4_t a_real_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(a_i16x4x2.val[0]));
819
+ float32x4_t a_imag_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(a_i16x4x2.val[1]));
820
+ float32x4_t b_real_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(b_i16x4x2.val[0]));
821
+ float32x4_t b_imag_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(b_i16x4x2.val[1]));
822
+ sum_real_f32x4 = vfmaq_f32(sum_real_f32x4, a_real_f32x4, b_real_f32x4);
823
+ sum_real_f32x4 = vfmsq_f32(sum_real_f32x4, a_imag_f32x4, b_imag_f32x4);
824
+ sum_imag_f32x4 = vfmaq_f32(sum_imag_f32x4, a_real_f32x4, b_imag_f32x4);
825
+ sum_imag_f32x4 = vfmaq_f32(sum_imag_f32x4, a_imag_f32x4, b_real_f32x4);
826
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
827
+ }
828
+ nk_f32c_t tail_result;
829
+ nk_dot_f16c_serial(a_pairs, b_pairs, count_pairs, &tail_result);
830
+ result->real = tail_result.real + vaddvq_f32(sum_real_f32x4);
831
+ result->imag = tail_result.imag + vaddvq_f32(sum_imag_f32x4);
832
+ }
833
+
834
+ NK_PUBLIC void nk_vdot_f16c_neon(nk_f16c_t const *a_pairs, nk_f16c_t const *b_pairs, nk_size_t count_pairs,
835
+ nk_f32c_t *result) {
836
+ float32x4_t sum_real_f32x4 = vdupq_n_f32(0);
837
+ float32x4_t sum_imag_f32x4 = vdupq_n_f32(0);
838
+ while (count_pairs >= 4) {
839
+ int16x4x2_t a_i16x4x2 = vld2_s16((short *)a_pairs);
840
+ int16x4x2_t b_i16x4x2 = vld2_s16((short *)b_pairs);
841
+ float32x4_t a_real_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(a_i16x4x2.val[0]));
842
+ float32x4_t a_imag_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(a_i16x4x2.val[1]));
843
+ float32x4_t b_real_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(b_i16x4x2.val[0]));
844
+ float32x4_t b_imag_f32x4 = vcvt_f32_f16(vreinterpret_f16_s16(b_i16x4x2.val[1]));
845
+ sum_real_f32x4 = vfmaq_f32(sum_real_f32x4, a_real_f32x4, b_real_f32x4);
846
+ sum_real_f32x4 = vfmaq_f32(sum_real_f32x4, a_imag_f32x4, b_imag_f32x4);
847
+ sum_imag_f32x4 = vfmaq_f32(sum_imag_f32x4, a_real_f32x4, b_imag_f32x4);
848
+ sum_imag_f32x4 = vfmsq_f32(sum_imag_f32x4, a_imag_f32x4, b_real_f32x4);
849
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
850
+ }
851
+ nk_f32c_t tail_result;
852
+ nk_vdot_f16c_serial(a_pairs, b_pairs, count_pairs, &tail_result);
853
+ result->real = tail_result.real + vaddvq_f32(sum_real_f32x4);
854
+ result->imag = tail_result.imag + vaddvq_f32(sum_imag_f32x4);
855
+ }
856
+
857
+ #if defined(__clang__)
858
+ #pragma clang attribute pop
859
+ #elif defined(__GNUC__)
860
+ #pragma GCC pop_options
861
+ #endif
862
+
863
+ #if defined(__cplusplus)
864
+ } // extern "C"
865
+ #endif
866
+
867
+ #endif // NK_TARGET_NEON
868
+ #endif // NK_TARGET_ARM64_
869
+ #endif // NK_DOT_NEON_H