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,1328 @@
1
+ /**
2
+ * @brief SIMD-accelerated Batched Dot Products for SME F64.
3
+ * @file include/numkong/dots/smef64.h
4
+ * @author Ash Vardanian
5
+ * @date January 2, 2026
6
+ *
7
+ * @sa include/numkong/dots.h
8
+ *
9
+ * Uses ARM SME with `FEAT_SME_F64F64` for high-precision GEMM.
10
+ * Requires Apple M4 or equivalent with `f64` outer product support.
11
+ *
12
+ * Provides `f32` and `f64` GEMM using `ZA64` tiles:
13
+ * - `f32` inputs with `f64` accumulation: higher precision than `ZA32`
14
+ * - Native `f64` GEMM via 3-way Ozaki splitting (19+17+17 mantissa bits)
15
+ *
16
+ * Ozaki splitting for `f64`:
17
+ * Each `f64` value is decomposed into 3 non-overlapping mantissa-masked slices
18
+ * that each fit in `f32` (max 19 significant bits < 24). All cross-products with
19
+ * index sum i+j <= 2 are accumulated via 6 FMOPAs into 3 merged accumulators.
20
+ * Products are exact in `f64` (max 19+19 = 38 < 53 mantissa bits).
21
+ * B is pre-split at pack time into interleaved `f32` slices; A is split in-register.
22
+ * A 2-column-tile fast path halves A memory traffic.
23
+ *
24
+ * Tile dimensions for SVL=512 (Apple M4):
25
+ * - `ZA64` tile: 8 × 8 `f64` elements (512B)
26
+ * - `f64` vectors: 8 elements per SVE vector
27
+ * - `f32` vectors: 16 elements per SVE vector, converted to `f64`
28
+ *
29
+ * Key instructions:
30
+ * - `svmopa_za64_f64_m` / `FMOPA`: `f64` outer product, 16cy amortized
31
+ * - `svcvt_f64_f32_x` / `FCVT`: `f32` → `f64` conversion
32
+ * - `svwrite_hor_za64_f64_m` / `MOVA`: direct Z → ZA tile write (no bounce buffer)
33
+ */
34
+ #ifndef NK_DOTS_SMEF64_H
35
+ #define NK_DOTS_SMEF64_H
36
+
37
+ #if NK_TARGET_ARM64_
38
+ #if NK_TARGET_SME
39
+
40
+ #include "numkong/types.h"
41
+ #include "numkong/dots/sme.h" // `nk_dots_sme_packed_header_t`
42
+
43
+ #if defined(__cplusplus)
44
+ extern "C" {
45
+ #endif
46
+
47
+ #if defined(__clang__)
48
+ #pragma clang attribute push(__attribute__((target("sme,sme-f64f64"))), apply_to = function)
49
+ #elif defined(__GNUC__)
50
+ #pragma GCC push_options
51
+ #pragma GCC target("+sme+sme-f64f64")
52
+ #endif
53
+
54
+ /*
55
+ * f32 → f64 GEMM using FMOPA with ZA64 tiles (FEAT_SME_F64F64).
56
+ *
57
+ * Tile layout (SVL=512, Apple M4):
58
+ * - ZA64 output tile: 8 × 8 f64 elements (512 B)
59
+ * - f32 input vectors: 16 elements (SVL/32), converted to f64 in chunks of 8
60
+ * - Depth sub-loop: processes 8 f32 values per iteration (→ 8 f64)
61
+ * - FMOPA predicates: b64 (f64 output granularity)
62
+ * - f32 load predicates: b32 (f32 input granularity)
63
+ * - 4-tile path: ZA0-ZA3 process 4 column tiles simultaneously
64
+ * - Output: native f64 results written directly from ZA64 tiles
65
+ *
66
+ * Non-widening alternative (FEAT_SME_F32F32, `svmopa_za32_f32_m`): ZA32 tiles are 16×16
67
+ * (4× area vs ZA64 8×8) with no f32↔f64 conversion, offering ~3-4× raw throughput. However,
68
+ * ZA32 and ZA64 tiles alias physically (ZA0.S overlaps ZA0.D+ZA1.D), so a periodic flush to
69
+ * f64 stack accumulators would be needed for precision above f32 — erasing most speedup.
70
+ * Pure f32 accumulation (no flush) provides only f32 precision, which is already served by
71
+ * the f16 → f32 GEMM path for reduced-precision workloads. This f64 path exists specifically
72
+ * for higher-than-f32 accumulation precision; replacing it with f32 FMOPA would be
73
+ * counterproductive. Apple M4 has `hw.optional.arm.SME_F32F32: 1` but we don't use it here.
74
+ */
75
+ #pragma region F32 Floats
76
+
77
+ NK_PUBLIC nk_size_t nk_dots_packed_size_f32_smef64(nk_size_t columns, nk_size_t depth) {
78
+ nk_size_t const tile_dimension = nk_sme_cntd_(); // rows per `ZA64` tile (8 for SVL=512)
79
+ nk_size_t const depth_tile_size = nk_sme_cntw_(); // `f32` depth elements per tile (16 for SVL=512)
80
+
81
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
82
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth, depth_tile_size);
83
+
84
+ nk_size_t size = sizeof(nk_dots_sme_packed_header_t);
85
+ size += column_tile_count * depth_tile_count * tile_dimension * depth_tile_size * sizeof(nk_f32_t);
86
+ size += columns * sizeof(nk_f64_t); // per-column squared norms
87
+
88
+ return size;
89
+ }
90
+
91
+ NK_PUBLIC void nk_dots_pack_f32_smef64(nk_f32_t const *b, nk_size_t columns, nk_size_t depth,
92
+ nk_size_t b_stride_in_bytes, void *b_packed) {
93
+
94
+ nk_size_t const tile_dimension = nk_sme_cntd_(); // rows per `ZA64` tile (8 for SVL=512)
95
+ nk_size_t const depth_tile_size = nk_sme_cntw_(); // `f32` depth elements per tile (16 for SVL=512)
96
+ nk_size_t const tile_elements = tile_dimension * depth_tile_size; // 128
97
+ nk_size_t const b_stride_elements = b_stride_in_bytes / sizeof(nk_f32_t);
98
+
99
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
100
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth, depth_tile_size);
101
+ nk_size_t const total_tiles = column_tile_count * depth_tile_count;
102
+
103
+ // Store actual dimensions and tile counts in header
104
+ nk_dots_sme_packed_header_t *header = (nk_dots_sme_packed_header_t *)b_packed;
105
+ header->column_tile_count = (nk_u32_t)column_tile_count;
106
+ header->depth_tile_count = (nk_u32_t)depth_tile_count;
107
+ header->columns = (nk_u32_t)columns;
108
+ header->depth = (nk_u32_t)depth;
109
+ header->svl_bytes = (nk_u32_t)nk_sme_cntb_(); // streaming vector length in bytes
110
+
111
+ nk_f32_t *tiles = (nk_f32_t *)((char *)b_packed + sizeof(nk_dots_sme_packed_header_t));
112
+
113
+ // Zero-initialize all tiles (handles partial tile padding)
114
+ nk_size_t const total_elements = total_tiles * tile_elements;
115
+ for (nk_size_t i = 0; i < total_elements; i++) tiles[i] = 0.0f;
116
+
117
+ // Pack data into tiles with depth-major layout within each tile:
118
+ // dst_idx = depth_idx * tile_dimension + column_idx
119
+ // This allows loading one B vector per depth step: svld1(b_tile + k * tile_dimension)
120
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tile_count; column_tile_idx++) {
121
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
122
+ nk_size_t const tile_index = column_tile_idx * depth_tile_count + depth_tile_idx;
123
+ nk_f32_t *tile_output = tiles + tile_index * tile_elements;
124
+
125
+ nk_size_t const src_row_start = column_tile_idx * tile_dimension;
126
+ nk_size_t const src_column_start = depth_tile_idx * depth_tile_size;
127
+
128
+ // Handle partial tiles at edges
129
+ nk_size_t const rows_to_pack = (src_row_start + tile_dimension <= columns) ? tile_dimension
130
+ : (columns - src_row_start);
131
+ nk_size_t const columns_to_pack = (src_column_start + depth_tile_size <= depth)
132
+ ? depth_tile_size
133
+ : (depth - src_column_start);
134
+
135
+ for (nk_size_t column_idx = 0; column_idx < rows_to_pack; column_idx++) {
136
+ for (nk_size_t depth_idx = 0; depth_idx < columns_to_pack; depth_idx++) {
137
+ nk_size_t const src_idx = (src_row_start + column_idx) * b_stride_elements + src_column_start +
138
+ depth_idx;
139
+ nk_size_t const dst_idx = depth_idx * tile_dimension + column_idx;
140
+ tile_output[dst_idx] = b[src_idx];
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ // Compute per-column squared norms and store after packed data
147
+ nk_size_t const data_size = total_tiles * tile_elements * sizeof(nk_f32_t);
148
+ header->norms_offset = (nk_u32_t)(sizeof(nk_dots_sme_packed_header_t) + data_size);
149
+ nk_f64_t *norms_ptr = (nk_f64_t *)((char *)b_packed + header->norms_offset);
150
+ for (nk_size_t col = 0; col < columns; col++) {
151
+ nk_f32_t const *col_data = (nk_f32_t const *)((char const *)b + col * b_stride_in_bytes);
152
+ norms_ptr[col] = nk_dots_reduce_sumsq_f32_(col_data, depth);
153
+ }
154
+ }
155
+
156
+ __arm_new("za") static void nk_dots_packed_f32_smef64_streaming_( //
157
+ nk_f32_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows, nk_size_t columns, nk_size_t depth,
158
+ nk_size_t a_stride_elements, nk_size_t c_stride_elements) NK_STREAMING_ {
159
+
160
+ nk_dots_sme_packed_header_t const *header = (nk_dots_sme_packed_header_t const *)b_packed;
161
+ nk_size_t const column_tile_count = header->column_tile_count;
162
+ nk_size_t const depth_tile_count = header->depth_tile_count;
163
+
164
+ nk_size_t const tile_dimension = svcntd(); // 8 for 512-bit SVL
165
+ nk_size_t const depth_tile_size = svcntw(); // 16 for 512-bit SVL
166
+ nk_size_t const tile_elements = tile_dimension * depth_tile_size;
167
+ nk_size_t const depth_steps_per_batch = tile_dimension; // 8 depth steps per ZA0.D load
168
+
169
+ nk_f32_t const *b_tiles = (nk_f32_t const *)((char const *)b_packed + sizeof(nk_dots_sme_packed_header_t));
170
+
171
+ svbool_t const predicate_all_b64x = svptrue_b64();
172
+
173
+ // ZA0.D = staging, ZA1-7.D = accumulation (7-tile fast path)
174
+ for (nk_size_t row_tile_index = 0; row_tile_index < nk_size_divide_round_up_(rows, tile_dimension);
175
+ row_tile_index++) {
176
+ nk_size_t const row_start = row_tile_index * tile_dimension;
177
+ nk_size_t const rows_remaining = (row_start + tile_dimension <= rows) ? tile_dimension : (rows - row_start);
178
+ svbool_t const row_predicate_b64x = svwhilelt_b64_u64(0u, rows_remaining);
179
+
180
+ nk_size_t column_tile_index = 0;
181
+
182
+ // Fast path: 7 column tiles using ZA1-ZA7 (ZA0.D = staging)
183
+ for (; column_tile_index + 7 <= column_tile_count; column_tile_index += 7) {
184
+ svzero_mask_za(nk_sme_zero_za64_tiles_1_7_);
185
+
186
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
187
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
188
+
189
+ // Process depth_tile_size elements in batches of tile_dimension (8)
190
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
191
+ depth_batch_start += depth_steps_per_batch) {
192
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
193
+ ? depth_batch_start + depth_steps_per_batch
194
+ : depth_tile_size;
195
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
196
+
197
+ // Check if any elements in this batch are valid
198
+ if (depth_offset + depth_batch_start >= depth) break;
199
+
200
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
201
+
202
+ // Load A rows into ZA0.D: extending load f32→u64 + convert to f64
203
+ svbool_t const batch_predicate_b64x = svwhilelt_b64_u64(0u, batch_size);
204
+ svbool_t const a_depth_predicate_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start, depth);
205
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++) {
206
+ nk_size_t const a_row = row_start + row_in_tile;
207
+ // Extending load: svld1uw_u64 loads f32 bits into lower 32 of each u64 lane
208
+ svfloat64_t a_row_widened_f64x = svcvt_f64_f32_x(
209
+ batch_predicate_b64x,
210
+ svreinterpret_f32_u64(svld1uw_u64(
211
+ a_depth_predicate_b64x,
212
+ (nk_u32_t const *)&a[a_row * a_stride_elements + depth_offset + depth_batch_start])));
213
+ svwrite_hor_za64_f64_m(0, row_in_tile, batch_predicate_b64x, a_row_widened_f64x);
214
+ }
215
+
216
+ // Vertical read + MOPA for each depth step in batch
217
+ for (nk_size_t step = 0; step < batch_size; step++) {
218
+ nk_size_t const k_abs = depth_offset + depth_batch_start + step;
219
+ if (k_abs >= depth) break;
220
+
221
+ svfloat64_t a_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), row_predicate_b64x, 0, step);
222
+
223
+ nk_size_t const b_k = depth_batch_start + step;
224
+
225
+ // Extending load f32→u64 + convert to f64: svld1uw_u64 replaces svld1_f32 + svunpklo_u64
226
+ svfloat64_t b_column_tile_1_f64x = svcvt_f64_f32_x(
227
+ predicate_all_b64x,
228
+ svreinterpret_f32_u64(svld1uw_u64(
229
+ predicate_all_b64x,
230
+ (nk_u32_t const *)(b_tiles +
231
+ ((column_tile_index + 0) * depth_tile_count + depth_tile_idx) *
232
+ tile_elements +
233
+ b_k * tile_dimension))));
234
+ svfloat64_t b_column_tile_2_f64x = svcvt_f64_f32_x(
235
+ predicate_all_b64x,
236
+ svreinterpret_f32_u64(svld1uw_u64(
237
+ predicate_all_b64x,
238
+ (nk_u32_t const *)(b_tiles +
239
+ ((column_tile_index + 1) * depth_tile_count + depth_tile_idx) *
240
+ tile_elements +
241
+ b_k * tile_dimension))));
242
+ svfloat64_t b_column_tile_3_f64x = svcvt_f64_f32_x(
243
+ predicate_all_b64x,
244
+ svreinterpret_f32_u64(svld1uw_u64(
245
+ predicate_all_b64x,
246
+ (nk_u32_t const *)(b_tiles +
247
+ ((column_tile_index + 2) * depth_tile_count + depth_tile_idx) *
248
+ tile_elements +
249
+ b_k * tile_dimension))));
250
+ svfloat64_t b_column_tile_4_f64x = svcvt_f64_f32_x(
251
+ predicate_all_b64x,
252
+ svreinterpret_f32_u64(svld1uw_u64(
253
+ predicate_all_b64x,
254
+ (nk_u32_t const *)(b_tiles +
255
+ ((column_tile_index + 3) * depth_tile_count + depth_tile_idx) *
256
+ tile_elements +
257
+ b_k * tile_dimension))));
258
+ svfloat64_t b_column_tile_5_f64x = svcvt_f64_f32_x(
259
+ predicate_all_b64x,
260
+ svreinterpret_f32_u64(svld1uw_u64(
261
+ predicate_all_b64x,
262
+ (nk_u32_t const *)(b_tiles +
263
+ ((column_tile_index + 4) * depth_tile_count + depth_tile_idx) *
264
+ tile_elements +
265
+ b_k * tile_dimension))));
266
+ svfloat64_t b_column_tile_6_f64x = svcvt_f64_f32_x(
267
+ predicate_all_b64x,
268
+ svreinterpret_f32_u64(svld1uw_u64(
269
+ predicate_all_b64x,
270
+ (nk_u32_t const *)(b_tiles +
271
+ ((column_tile_index + 5) * depth_tile_count + depth_tile_idx) *
272
+ tile_elements +
273
+ b_k * tile_dimension))));
274
+ svfloat64_t b_column_tile_7_f64x = svcvt_f64_f32_x(
275
+ predicate_all_b64x,
276
+ svreinterpret_f32_u64(svld1uw_u64(
277
+ predicate_all_b64x,
278
+ (nk_u32_t const *)(b_tiles +
279
+ ((column_tile_index + 6) * depth_tile_count + depth_tile_idx) *
280
+ tile_elements +
281
+ b_k * tile_dimension))));
282
+
283
+ svmopa_za64_f64_m(1, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_1_f64x);
284
+ svmopa_za64_f64_m(2, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_2_f64x);
285
+ svmopa_za64_f64_m(3, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_3_f64x);
286
+ svmopa_za64_f64_m(4, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_4_f64x);
287
+ svmopa_za64_f64_m(5, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_5_f64x);
288
+ svmopa_za64_f64_m(6, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_6_f64x);
289
+ svmopa_za64_f64_m(7, row_predicate_b64x, predicate_all_b64x, a_f64x, b_column_tile_7_f64x);
290
+ }
291
+ }
292
+ }
293
+
294
+ // Extract from ZA1-7 and store native f64 outputs.
295
+ svbool_t const predicate_tile_b64x = svwhilelt_b64_u64(0u, tile_dimension);
296
+ // The 7th tile (index 6) may be partial when it's the last column tile
297
+ nk_size_t const last_fast_col_start = (column_tile_index + 6) * tile_dimension;
298
+ nk_size_t const last_fast_cols = (last_fast_col_start + tile_dimension <= columns)
299
+ ? tile_dimension
300
+ : (columns - last_fast_col_start);
301
+ svbool_t const last_tile_pred_b64x = svwhilelt_b64_u64(0u, last_fast_cols);
302
+ for (nk_size_t row_idx = 0; row_idx < rows_remaining; row_idx++) {
303
+ nk_f64_t *c_row = c + (row_start + row_idx) * c_stride_elements;
304
+
305
+ svfloat64_t za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 1, row_idx);
306
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 0) * tile_dimension, za_row_f64x);
307
+
308
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 2, row_idx);
309
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 1) * tile_dimension, za_row_f64x);
310
+
311
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 3, row_idx);
312
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 2) * tile_dimension, za_row_f64x);
313
+
314
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 4, row_idx);
315
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 3) * tile_dimension, za_row_f64x);
316
+
317
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 5, row_idx);
318
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 4) * tile_dimension, za_row_f64x);
319
+
320
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 6, row_idx);
321
+ svst1_f64(predicate_tile_b64x, c_row + (column_tile_index + 5) * tile_dimension, za_row_f64x);
322
+
323
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 7, row_idx);
324
+ svst1_f64(last_tile_pred_b64x, c_row + (column_tile_index + 6) * tile_dimension, za_row_f64x);
325
+ }
326
+ }
327
+
328
+ // Remainder: 1 column tile at a time using ZA1
329
+ for (; column_tile_index < column_tile_count; column_tile_index++) {
330
+ nk_size_t const column_start = column_tile_index * tile_dimension;
331
+ nk_size_t const columns_remaining = (column_start + tile_dimension <= columns) ? tile_dimension
332
+ : (columns - column_start);
333
+ svbool_t const column_predicate_b64x = svwhilelt_b64_u64(0u, columns_remaining);
334
+
335
+ svzero_mask_za(nk_sme_zero_za64_tile_1_);
336
+
337
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
338
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
339
+
340
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
341
+ depth_batch_start += depth_steps_per_batch) {
342
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
343
+ ? depth_batch_start + depth_steps_per_batch
344
+ : depth_tile_size;
345
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
346
+
347
+ if (depth_offset + depth_batch_start >= depth) break;
348
+
349
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
350
+
351
+ svbool_t const batch_predicate_b64x = svwhilelt_b64_u64(0u, batch_size);
352
+ svbool_t const a_depth_pred_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start, depth);
353
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++) {
354
+ nk_size_t const a_row = row_start + row_in_tile;
355
+ svfloat64_t a_row_widened_f64x = svcvt_f64_f32_x(
356
+ batch_predicate_b64x,
357
+ svreinterpret_f32_u64(svld1uw_u64(
358
+ a_depth_pred_b64x,
359
+ (nk_u32_t const *)&a[a_row * a_stride_elements + depth_offset + depth_batch_start])));
360
+ svwrite_hor_za64_f64_m(0, row_in_tile, batch_predicate_b64x, a_row_widened_f64x);
361
+ }
362
+
363
+ for (nk_size_t step = 0; step < batch_size; step++) {
364
+ nk_size_t const k_abs = depth_offset + depth_batch_start + step;
365
+ if (k_abs >= depth) break;
366
+
367
+ svfloat64_t a_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), row_predicate_b64x, 0, step);
368
+
369
+ nk_size_t const b_k = depth_batch_start + step;
370
+ nk_f32_t const *b_tile = b_tiles + (column_tile_index * depth_tile_count + depth_tile_idx) *
371
+ tile_elements;
372
+ // Extending load f32→u64 + convert to f64
373
+ svfloat64_t b_f64x = svcvt_f64_f32_x(
374
+ predicate_all_b64x,
375
+ svreinterpret_f32_u64(
376
+ svld1uw_u64(predicate_all_b64x, (nk_u32_t const *)(b_tile + b_k * tile_dimension))));
377
+
378
+ svmopa_za64_f64_m(1, row_predicate_b64x, column_predicate_b64x, a_f64x, b_f64x);
379
+ }
380
+ }
381
+ }
382
+
383
+ // Store native f64 outputs for the tail column tile.
384
+ for (nk_size_t row_idx = 0; row_idx < rows_remaining; row_idx++) {
385
+ svfloat64_t za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 1, row_idx);
386
+ nk_f64_t *c_row = c + (row_start + row_idx) * c_stride_elements + column_start;
387
+ svst1_f64(column_predicate_b64x, c_row, za_row_f64x);
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+ NK_PUBLIC void nk_dots_packed_f32_smef64( //
394
+ nk_f32_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows, nk_size_t columns, nk_size_t depth,
395
+ nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) {
396
+
397
+ nk_size_t const a_stride_elements = a_stride_in_bytes / sizeof(nk_f32_t);
398
+ nk_size_t const c_stride_elements = c_stride_in_bytes / sizeof(nk_f64_t);
399
+
400
+ nk_sme_start_streaming_();
401
+ nk_dots_packed_f32_smef64_streaming_(a, b_packed, c, rows, columns, depth, a_stride_elements, c_stride_elements);
402
+ nk_sme_stop_streaming_();
403
+ }
404
+
405
+ /**
406
+ * `f32` × `f32` → `f32` symmetric kernel using MOPA self-GEMM with f64 accumulation.
407
+ * Time-shares ZA0 for both A and B transposition: loads A horizontally,
408
+ * pre-reads A columns into Z registers, then reloads ZA0 with widened B data
409
+ * per column tile. Eliminates all scalar B-packing loops.
410
+ */
411
+ __arm_new("za") static void nk_dots_symmetric_f32_smef64_streaming_( //
412
+ nk_f32_t const *vectors, nk_size_t vectors_count, nk_size_t depth, nk_size_t stride_elements, nk_f64_t *result,
413
+ nk_size_t result_stride_elements, nk_size_t row_start, nk_size_t row_count) NK_STREAMING_ {
414
+
415
+ nk_size_t const tile_dimension = svcntd(); // 8 for SVL=512
416
+ nk_size_t const depth_tile_size = svcntw(); // 16 for SVL=512
417
+ nk_size_t const depth_steps_per_batch = tile_dimension; // 8
418
+
419
+ svbool_t const predicate_all_b64x = svptrue_b64();
420
+
421
+ NK_ALIGN64 nk_f64_t a_buffer[8][8];
422
+
423
+ nk_size_t const row_end = row_start + row_count;
424
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(vectors_count, tile_dimension);
425
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth, depth_tile_size);
426
+
427
+ for (nk_size_t row_tile_start = row_start; row_tile_start < row_end && row_tile_start < vectors_count;
428
+ row_tile_start += tile_dimension) {
429
+ nk_size_t const rows_clamped = (row_tile_start + tile_dimension <= row_end) ? tile_dimension
430
+ : (row_end - row_tile_start);
431
+ nk_size_t const rows_actual = (row_tile_start + rows_clamped <= vectors_count)
432
+ ? rows_clamped
433
+ : (vectors_count - row_tile_start);
434
+ svbool_t const row_predicate_b64x = svwhilelt_b64_u64(0u, rows_actual);
435
+
436
+ // Upper triangle: start from this row tile's column
437
+ nk_size_t column_tile_index = row_tile_start / tile_dimension;
438
+
439
+ // Fast path: 7 column tiles at a time
440
+ for (; column_tile_index + 7 <= column_tile_count; column_tile_index += 7) {
441
+ svzero_mask_za(nk_sme_zero_za64_tiles_1_7_);
442
+
443
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
444
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
445
+
446
+ // Process depth_tile_size in batches of depth_steps_per_batch (8)
447
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
448
+ depth_batch_start += depth_steps_per_batch) {
449
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
450
+ ? depth_batch_start + depth_steps_per_batch
451
+ : depth_tile_size;
452
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
453
+
454
+ if (depth_offset + depth_batch_start >= depth) break;
455
+
456
+ // ZA transpose for A rows: extending load f32→f64, MOVA directly into ZA0
457
+ svbool_t const batch_predicate_b64x = svwhilelt_b64_u64(0u, batch_size);
458
+ svbool_t const a_depth_predicate_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start, depth);
459
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
460
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_actual; row_in_tile++) {
461
+ nk_size_t const row_abs = row_tile_start + row_in_tile;
462
+ svfloat64_t a_row_widened_f64x = svcvt_f64_f32_x(
463
+ batch_predicate_b64x,
464
+ svreinterpret_f32_u64(svld1uw_u64(
465
+ a_depth_predicate_b64x, (nk_u32_t const *)&vectors[row_abs * stride_elements +
466
+ depth_offset + depth_batch_start])));
467
+ svwrite_hor_za64_f64_m(0, row_in_tile, batch_predicate_b64x, a_row_widened_f64x);
468
+ }
469
+
470
+ // Save A columns from ZA0 to stack buffer
471
+ for (nk_size_t s = 0; s < batch_size; s++)
472
+ svst1_f64(predicate_all_b64x, a_buffer[s],
473
+ svread_ver_za64_f64_m(svdup_f64(0), row_predicate_b64x, 0, s));
474
+
475
+ // Column tile 0 → ZA1 via MOVA
476
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
477
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
478
+ nk_size_t const column_abs = (column_tile_index + 0) * tile_dimension + column;
479
+ if (column_abs < vectors_count) {
480
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
481
+ batch_predicate_b64x,
482
+ svreinterpret_f32_u64(svld1uw_u64(
483
+ a_depth_predicate_b64x,
484
+ (nk_u32_t const
485
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
486
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
487
+ }
488
+ }
489
+ for (nk_size_t step = 0; step < batch_size; step++) {
490
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
491
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
492
+ svmopa_za64_f64_m(1, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
493
+ }
494
+
495
+ // Column tile 1 → ZA2 via MOVA
496
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
497
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
498
+ nk_size_t const column_abs = (column_tile_index + 1) * tile_dimension + column;
499
+ if (column_abs < vectors_count) {
500
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
501
+ batch_predicate_b64x,
502
+ svreinterpret_f32_u64(svld1uw_u64(
503
+ a_depth_predicate_b64x,
504
+ (nk_u32_t const
505
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
506
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
507
+ }
508
+ }
509
+ for (nk_size_t step = 0; step < batch_size; step++) {
510
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
511
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
512
+ svmopa_za64_f64_m(2, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
513
+ }
514
+
515
+ // Column tile 2 → ZA3 via MOVA
516
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
517
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
518
+ nk_size_t const column_abs = (column_tile_index + 2) * tile_dimension + column;
519
+ if (column_abs < vectors_count) {
520
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
521
+ batch_predicate_b64x,
522
+ svreinterpret_f32_u64(svld1uw_u64(
523
+ a_depth_predicate_b64x,
524
+ (nk_u32_t const
525
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
526
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
527
+ }
528
+ }
529
+ for (nk_size_t step = 0; step < batch_size; step++) {
530
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
531
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
532
+ svmopa_za64_f64_m(3, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
533
+ }
534
+
535
+ // Column tile 3 → ZA4 via MOVA
536
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
537
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
538
+ nk_size_t const column_abs = (column_tile_index + 3) * tile_dimension + column;
539
+ if (column_abs < vectors_count) {
540
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
541
+ batch_predicate_b64x,
542
+ svreinterpret_f32_u64(svld1uw_u64(
543
+ a_depth_predicate_b64x,
544
+ (nk_u32_t const
545
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
546
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
547
+ }
548
+ }
549
+ for (nk_size_t step = 0; step < batch_size; step++) {
550
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
551
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
552
+ svmopa_za64_f64_m(4, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
553
+ }
554
+
555
+ // Column tile 4 → ZA5 via MOVA
556
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
557
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
558
+ nk_size_t const column_abs = (column_tile_index + 4) * tile_dimension + column;
559
+ if (column_abs < vectors_count) {
560
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
561
+ batch_predicate_b64x,
562
+ svreinterpret_f32_u64(svld1uw_u64(
563
+ a_depth_predicate_b64x,
564
+ (nk_u32_t const
565
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
566
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
567
+ }
568
+ }
569
+ for (nk_size_t step = 0; step < batch_size; step++) {
570
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
571
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
572
+ svmopa_za64_f64_m(5, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
573
+ }
574
+
575
+ // Column tile 5 → ZA6 via MOVA
576
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
577
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
578
+ nk_size_t const column_abs = (column_tile_index + 5) * tile_dimension + column;
579
+ if (column_abs < vectors_count) {
580
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
581
+ batch_predicate_b64x,
582
+ svreinterpret_f32_u64(svld1uw_u64(
583
+ a_depth_predicate_b64x,
584
+ (nk_u32_t const
585
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
586
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
587
+ }
588
+ }
589
+ for (nk_size_t step = 0; step < batch_size; step++) {
590
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
591
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
592
+ svmopa_za64_f64_m(6, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
593
+ }
594
+
595
+ // Column tile 6 → ZA7 via MOVA
596
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
597
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
598
+ nk_size_t const column_abs = (column_tile_index + 6) * tile_dimension + column;
599
+ if (column_abs < vectors_count) {
600
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
601
+ batch_predicate_b64x,
602
+ svreinterpret_f32_u64(svld1uw_u64(
603
+ a_depth_predicate_b64x,
604
+ (nk_u32_t const
605
+ *)&vectors[column_abs * stride_elements + depth_offset + depth_batch_start])));
606
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
607
+ }
608
+ }
609
+ for (nk_size_t step = 0; step < batch_size; step++) {
610
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
611
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 0, step);
612
+ svmopa_za64_f64_m(7, row_predicate_b64x, predicate_all_b64x, a_f64x, b_f64x);
613
+ }
614
+ }
615
+ }
616
+
617
+ // Extract results and store native f64 outputs.
618
+ svbool_t const predicate_tile_b64x = svwhilelt_b64_u64(0u, tile_dimension);
619
+ // The 7th tile (index 6) may be partial when it's the last column tile
620
+ nk_size_t const last_fast_col_start = (column_tile_index + 6) * tile_dimension;
621
+ nk_size_t const last_fast_cols = (last_fast_col_start + tile_dimension <= vectors_count)
622
+ ? tile_dimension
623
+ : (vectors_count - last_fast_col_start);
624
+ svbool_t const last_tile_pred_b64x = svwhilelt_b64_u64(0u, last_fast_cols);
625
+ for (nk_size_t row = 0; row < rows_actual; row++) {
626
+ nk_size_t const row_abs = row_tile_start + row;
627
+ nk_f64_t *result_row = result + row_abs * result_stride_elements;
628
+
629
+ svfloat64_t za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 1, row);
630
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 0) * tile_dimension, za_row_f64x);
631
+
632
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 2, row);
633
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 1) * tile_dimension, za_row_f64x);
634
+
635
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 3, row);
636
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 2) * tile_dimension, za_row_f64x);
637
+
638
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 4, row);
639
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 3) * tile_dimension, za_row_f64x);
640
+
641
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 5, row);
642
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 4) * tile_dimension, za_row_f64x);
643
+
644
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 6, row);
645
+ svst1_f64(predicate_tile_b64x, result_row + (column_tile_index + 5) * tile_dimension, za_row_f64x);
646
+
647
+ za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 7, row);
648
+ svst1_f64(last_tile_pred_b64x, result_row + (column_tile_index + 6) * tile_dimension, za_row_f64x);
649
+ }
650
+ }
651
+
652
+ // Remainder: 1 column tile at a time
653
+ for (; column_tile_index < column_tile_count; column_tile_index++) {
654
+ nk_size_t const column_tile_start = column_tile_index * tile_dimension;
655
+ nk_size_t const columns_remaining = (column_tile_start + tile_dimension <= vectors_count)
656
+ ? tile_dimension
657
+ : (vectors_count - column_tile_start);
658
+ svbool_t const column_predicate_b64x = svwhilelt_b64_u64(0u, columns_remaining);
659
+
660
+ svzero_mask_za(nk_sme_zero_za64_tile_1_);
661
+
662
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
663
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
664
+
665
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
666
+ depth_batch_start += depth_steps_per_batch) {
667
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
668
+ ? depth_batch_start + depth_steps_per_batch
669
+ : depth_tile_size;
670
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
671
+
672
+ if (depth_offset + depth_batch_start >= depth) break;
673
+
674
+ svbool_t const batch_predicate_b64x = svwhilelt_b64_u64(0u, batch_size);
675
+ svbool_t const a_depth_pred_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start, depth);
676
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
677
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_actual; row_in_tile++) {
678
+ nk_size_t const row_abs = row_tile_start + row_in_tile;
679
+ svfloat64_t a_row_widened_f64x = svcvt_f64_f32_x(
680
+ batch_predicate_b64x,
681
+ svreinterpret_f32_u64(svld1uw_u64(
682
+ a_depth_pred_b64x, (nk_u32_t const *)&vectors[row_abs * stride_elements + depth_offset +
683
+ depth_batch_start])));
684
+ svwrite_hor_za64_f64_m(0, row_in_tile, batch_predicate_b64x, a_row_widened_f64x);
685
+ }
686
+
687
+ // Save A columns from ZA0 to stack buffer
688
+ for (nk_size_t s = 0; s < batch_size; s++)
689
+ svst1_f64(predicate_all_b64x, a_buffer[s],
690
+ svread_ver_za64_f64_m(svdup_f64(0), row_predicate_b64x, 0, s));
691
+
692
+ // Load B column tile into ZA0 via MOVA, vertical read + FMOPA into ZA1
693
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
694
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
695
+ nk_size_t const column_abs = column_tile_start + column;
696
+ if (column_abs < vectors_count) {
697
+ svfloat64_t widened_f64x = svcvt_f64_f32_x(
698
+ batch_predicate_b64x,
699
+ svreinterpret_f32_u64(svld1uw_u64(
700
+ a_depth_pred_b64x, (nk_u32_t const *)&vectors[column_abs * stride_elements +
701
+ depth_offset + depth_batch_start])));
702
+ svwrite_hor_za64_f64_m(0, column, batch_predicate_b64x, widened_f64x);
703
+ }
704
+ }
705
+ for (nk_size_t step = 0; step < batch_size; step++) {
706
+ nk_size_t const k_abs = depth_offset + depth_batch_start + step;
707
+ if (k_abs >= depth) break;
708
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
709
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), column_predicate_b64x, 0, step);
710
+ svmopa_za64_f64_m(1, row_predicate_b64x, column_predicate_b64x, a_f64x, b_f64x);
711
+ }
712
+ }
713
+ }
714
+
715
+ // Store native f64 outputs for the tail column tile.
716
+ for (nk_size_t row = 0; row < rows_actual; row++) {
717
+ nk_size_t const row_abs = row_tile_start + row;
718
+ svfloat64_t za_row_f64x = svread_hor_za64_f64_m(svdup_f64(0), predicate_all_b64x, 1, row);
719
+ svst1_f64(column_predicate_b64x, result + row_abs * result_stride_elements + column_tile_start,
720
+ za_row_f64x);
721
+ }
722
+ }
723
+ }
724
+ }
725
+
726
+ NK_PUBLIC void nk_dots_symmetric_f32_smef64( //
727
+ nk_f32_t const *vectors, nk_size_t vectors_count, nk_size_t depth, nk_size_t stride_in_bytes, nk_f64_t *result,
728
+ nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) {
729
+
730
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_f32_t);
731
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f64_t);
732
+ nk_sme_start_streaming_();
733
+ nk_dots_symmetric_f32_smef64_streaming_(vectors, vectors_count, depth, stride_elements, result,
734
+ result_stride_elements, row_start, row_count);
735
+ nk_sme_stop_streaming_();
736
+ }
737
+
738
+ #pragma endregion F32 Floats
739
+
740
+ /*
741
+ * f64 GEMM via 3-way Ozaki splitting using FMOPA with ZA64 tiles.
742
+ * Uses ZA transpose for A-vector construction (expansion=1, no interleaving needed).
743
+ *
744
+ * Each f64 is split into 3 non-overlapping mantissa-masked slices (19+17+17 bits).
745
+ * All slices fit in f32 (max 19 significant bits < 24-bit significand).
746
+ * Cross-products with index sum i+j ≤ 2 are accumulated via 6 FMOPAs into 3 merged
747
+ * accumulators. Products are exact in f64 (max 19+19 = 38 < 53 mantissa bits).
748
+ * Accumulation is exact for K ≤ 16,384 (ceil(log2(K)) + 38 + 1 ≤ 53).
749
+ *
750
+ * Packed GEMM tile allocation (2-column fast path):
751
+ * - ZA0.D: A-staging (horizontal load, vertical read, shared by both col tiles)
752
+ * - ZA1.D: col0 acc0 — a₀×b₀ (i+j=0, dominant)
753
+ * - ZA2.D: col0 acc1 — a₀×b₁ + a₁×b₀ (i+j=1)
754
+ * - ZA3.D: col0 acc2 — a₀×b₂ + a₁×b₁ + a₂×b₀ (i+j=2, smallest)
755
+ * - ZA4.D: col1 acc0 — a₀×b₀ (i+j=0)
756
+ * - ZA5.D: col1 acc1 — a₀×b₁ + a₁×b₀ (i+j=1)
757
+ * - ZA6.D: col1 acc2 — a₀×b₂ + a₁×b₁ + a₂×b₀ (i+j=2)
758
+ * - ZA7.D: unused
759
+ *
760
+ * 1-column remainder uses ZA1-3 only.
761
+ * B is pre-split at pack time into interleaved f32 slices.
762
+ * A is split in-register per depth step via SVE integer AND.
763
+ *
764
+ * Symmetric GEMM tile allocation:
765
+ * - ZA0.D: staging (A rows, then B columns via horizontal load)
766
+ * - ZA1-3.D: merged Ozaki accumulators (i+j=0,1,2)
767
+ * Both A and B are split in-register per depth step.
768
+ *
769
+ * Tile dimensions for SVL=512 (Apple M4):
770
+ * - ZA64 tile: 8 × 8 f64 elements (512B)
771
+ * - f64 input vectors: 8 elements (SVL/64)
772
+ * - FMOPA predicates: b64 (native f64 granularity)
773
+ */
774
+ #pragma region F64 Floats
775
+
776
+ /* Mantissa bit masks for 3-way Ozaki splitting of f64 values.
777
+ *
778
+ * f64 layout: [63]=sign, [62:52]=exponent (11 bits), [51:0]=mantissa (52 bits).
779
+ * Significand = implicit 1 + mantissa = 53 significant bits.
780
+ *
781
+ * Slice 0 (19 significant bits): keep sign + exponent + top 18 mantissa bits.
782
+ * Zeroes mantissa bits [33:0] (34 bits). Mask = 0xFFFFFFFC00000000.
783
+ * Slice 1 (17 significant bits): keep sign + exponent + top 16 mantissa bits of residual.
784
+ * Zeroes mantissa bits [35:0] (36 bits). Mask = 0xFFFFFFF000000000.
785
+ * Slice 2 = residual of residual (at most 17 significant bits, fits f32).
786
+ *
787
+ * All slices fit in f32 (24-bit significand). Products: max 19+19 = 38 ≤ 53, exact in f64.
788
+ */
789
+ NK_PUBLIC nk_u64_t nk_f64_smef64_ozaki_mask_19_bits_(void) {
790
+ return 0xFFFFFFFC00000000ULL; // keep top 19 sig bits
791
+ }
792
+ NK_PUBLIC nk_u64_t nk_f64_smef64_ozaki_mask_17_bits_(void) {
793
+ return 0xFFFFFFF000000000ULL; // keep top 17 sig bits
794
+ }
795
+
796
+ /* Split a scalar f64 into 3 non-overlapping Ozaki slices (19+17+17 mantissa bits).
797
+ * Each slice fits in f32. Outputs stored via pointers. */
798
+ NK_PUBLIC void nk_f64_smef64_ozaki_split_f64_(nk_f64_t val, nk_f64_t *slice_0, nk_f64_t *slice_1, nk_f64_t *slice_2) {
799
+ nk_fui64_t pun;
800
+ pun.f = val;
801
+ pun.u &= nk_f64_smef64_ozaki_mask_19_bits_();
802
+ *slice_0 = pun.f;
803
+ nk_f64_t residual = val - *slice_0;
804
+ pun.f = residual;
805
+ pun.u &= nk_f64_smef64_ozaki_mask_17_bits_();
806
+ *slice_1 = pun.f;
807
+ *slice_2 = residual - *slice_1;
808
+ }
809
+
810
+ __arm_new("za") static void nk_dots_symmetric_f64_smef64_streaming_( //
811
+ nk_f64_t const *vectors, nk_size_t vectors_count, nk_size_t depth, nk_size_t stride_elements, nk_f64_t *result,
812
+ nk_size_t result_stride_elements, nk_size_t row_start, nk_size_t row_count) NK_STREAMING_ {
813
+
814
+ nk_size_t const tile_dimension = svcntd();
815
+ nk_size_t const depth_steps_per_batch = tile_dimension;
816
+
817
+ svbool_t const predicate_all_b64x = svptrue_b64();
818
+ svuint64_t const ozaki_mask_19_u64x = svdup_u64(nk_f64_smef64_ozaki_mask_19_bits_());
819
+ svuint64_t const ozaki_mask_17_u64x = svdup_u64(nk_f64_smef64_ozaki_mask_17_bits_());
820
+
821
+ NK_ALIGN64 nk_f64_t a_buffer[8][8]; // save A columns before reusing ZA0 for B
822
+
823
+ nk_size_t const row_end = row_start + row_count;
824
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(vectors_count, tile_dimension);
825
+
826
+ // ZA0.D = staging (A then B), ZA1-3.D = merged Ozaki accumulators (i+j=0,1,2)
827
+ for (nk_size_t row_tile_start = row_start; row_tile_start < row_end && row_tile_start < vectors_count;
828
+ row_tile_start += tile_dimension) {
829
+ nk_size_t const rows_remaining = (row_tile_start + tile_dimension <= row_end) ? tile_dimension
830
+ : (row_end - row_tile_start);
831
+ nk_size_t const rows_clamped = (row_tile_start + rows_remaining <= vectors_count)
832
+ ? rows_remaining
833
+ : (vectors_count - row_tile_start);
834
+ svbool_t const row_predicate_b64x = svwhilelt_b64_u64(0u, rows_clamped);
835
+
836
+ // Upper triangle: start from this row tile's column
837
+ for (nk_size_t column_tile_index = row_tile_start / tile_dimension; column_tile_index < column_tile_count;
838
+ column_tile_index++) {
839
+ nk_size_t const column_tile_start = column_tile_index * tile_dimension;
840
+ nk_size_t const columns_remaining = (column_tile_start + tile_dimension <= vectors_count)
841
+ ? tile_dimension
842
+ : (vectors_count - column_tile_start);
843
+ svbool_t const column_predicate_b64x = svwhilelt_b64_u64(0u, columns_remaining);
844
+
845
+ // Zero ZA1-3 (3 merged Ozaki accumulators)
846
+ svzero_mask_za(nk_sme_zero_za64_tiles_1_3_);
847
+
848
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth;
849
+ depth_batch_start += depth_steps_per_batch) {
850
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth)
851
+ ? depth_batch_start + depth_steps_per_batch
852
+ : depth;
853
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
854
+ svbool_t const batch_predicate_b64x = svwhilelt_b64_u64(0u, batch_size);
855
+
856
+ // Load A rows into ZA0
857
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
858
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_clamped; row_in_tile++) {
859
+ nk_size_t const row_abs = row_tile_start + row_in_tile;
860
+ svld1_hor_za64(0, row_in_tile, batch_predicate_b64x,
861
+ vectors + row_abs * stride_elements + depth_batch_start);
862
+ }
863
+
864
+ // Save A columns to buffer before reusing ZA0 for B
865
+ for (nk_size_t s = 0; s < batch_size; s++)
866
+ svst1_f64(predicate_all_b64x, a_buffer[s],
867
+ svread_ver_za64_f64_m(svdup_f64(0.0), row_predicate_b64x, 0, s));
868
+
869
+ // Load B columns into ZA0 (reuse)
870
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
871
+ for (nk_size_t column = 0; column < tile_dimension; column++) {
872
+ nk_size_t const column_abs = column_tile_start + column;
873
+ if (column_abs < vectors_count)
874
+ svld1_hor_za64(0, column, batch_predicate_b64x,
875
+ vectors + column_abs * stride_elements + depth_batch_start);
876
+ }
877
+
878
+ // Split both A and B into 3 Ozaki slices, 6 FMOPAs per step
879
+ for (nk_size_t step = 0; step < batch_size; step++) {
880
+ svfloat64_t a_f64x = svld1_f64(predicate_all_b64x, a_buffer[step]);
881
+ svuint64_t a_bits_u64x = svreinterpret_u64_f64(a_f64x);
882
+ svfloat64_t a_slice_0_f64x = svreinterpret_f64_u64(
883
+ svand_u64_x(predicate_all_b64x, a_bits_u64x, ozaki_mask_19_u64x));
884
+ svfloat64_t residual_a_f64x = svsub_f64_x(predicate_all_b64x, a_f64x, a_slice_0_f64x);
885
+ svuint64_t residual_a_bits_u64x = svreinterpret_u64_f64(residual_a_f64x);
886
+ svfloat64_t a_slice_1_f64x = svreinterpret_f64_u64(
887
+ svand_u64_x(predicate_all_b64x, residual_a_bits_u64x, ozaki_mask_17_u64x));
888
+ svfloat64_t a_slice_2_f64x = svsub_f64_x(predicate_all_b64x, residual_a_f64x, a_slice_1_f64x);
889
+
890
+ svfloat64_t b_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), column_predicate_b64x, 0, step);
891
+ svuint64_t b_bits_u64x = svreinterpret_u64_f64(b_f64x);
892
+ svfloat64_t b_slice_0_f64x = svreinterpret_f64_u64(
893
+ svand_u64_x(predicate_all_b64x, b_bits_u64x, ozaki_mask_19_u64x));
894
+ svfloat64_t residual_b_f64x = svsub_f64_x(predicate_all_b64x, b_f64x, b_slice_0_f64x);
895
+ svuint64_t residual_b_bits_u64x = svreinterpret_u64_f64(residual_b_f64x);
896
+ svfloat64_t b_slice_1_f64x = svreinterpret_f64_u64(
897
+ svand_u64_x(predicate_all_b64x, residual_b_bits_u64x, ozaki_mask_17_u64x));
898
+ svfloat64_t b_slice_2_f64x = svsub_f64_x(predicate_all_b64x, residual_b_f64x, b_slice_1_f64x);
899
+
900
+ // 6 FMOPAs reordered to minimize WAW pipeline stalls on 3 tiles.
901
+ // Same-tile accumulation order preserved (bit-identical output).
902
+ // Tile schedule: ZA3(0), ZA2(1), ZA1(2), ZA3(4), ZA2(5), ZA3(8).
903
+ // 9 cycles vs 15 original (3 unavoidable bubbles with only 3 tiles).
904
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
905
+ b_slice_2_f64x); // ZA3: i+j=2 (1/3)
906
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
907
+ b_slice_1_f64x); // ZA2: i+j=1 (1/2)
908
+ svmopa_za64_f64_m(1, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
909
+ b_slice_0_f64x); // ZA1: i+j=0
910
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_1_f64x,
911
+ b_slice_1_f64x); // ZA3: i+j=2 (2/3)
912
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_b64x, a_slice_1_f64x,
913
+ b_slice_0_f64x); // ZA2: i+j=1 (2/2)
914
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_2_f64x,
915
+ b_slice_0_f64x); // ZA3: i+j=2 (3/3)
916
+ }
917
+ }
918
+
919
+ // Sum ZA3 + ZA2 + ZA1 (smallest to largest)
920
+ for (nk_size_t row = 0; row < rows_clamped; row++) {
921
+ nk_size_t const row_abs = row_tile_start + row;
922
+ svfloat64_t result_f64x = svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 3, row);
923
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
924
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 2, row));
925
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
926
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 1, row));
927
+ svst1_f64(column_predicate_b64x, result + row_abs * result_stride_elements + column_tile_start,
928
+ result_f64x);
929
+ }
930
+ }
931
+ }
932
+ }
933
+
934
+ NK_PUBLIC void nk_dots_symmetric_f64_smef64( //
935
+ nk_f64_t const *vectors, nk_size_t vectors_count, nk_size_t depth, nk_size_t stride_in_bytes, nk_f64_t *result,
936
+ nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) {
937
+
938
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_f64_t);
939
+ nk_size_t const result_stride_elements = result_stride_in_bytes / sizeof(nk_f64_t);
940
+ nk_sme_start_streaming_();
941
+ nk_dots_symmetric_f64_smef64_streaming_(vectors, vectors_count, depth, stride_elements, result,
942
+ result_stride_elements, row_start, row_count);
943
+ nk_sme_stop_streaming_();
944
+ }
945
+
946
+ NK_PUBLIC nk_size_t nk_dots_packed_size_f64_smef64(nk_size_t columns, nk_size_t depth) {
947
+ nk_size_t const tile_dimension = nk_sme_cntd_();
948
+ nk_size_t const depth_tile_size = nk_sme_cntw_();
949
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
950
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth, depth_tile_size);
951
+ // Single header + interleaved 3-slice data (3× tile_dimension elements per depth step)
952
+ nk_size_t size = sizeof(nk_dots_sme_packed_header_t);
953
+ size += column_tile_count * depth_tile_count * 3 * tile_dimension * depth_tile_size * sizeof(nk_f32_t);
954
+ size += columns * sizeof(nk_f64_t); // per-column squared norms
955
+ return size;
956
+ }
957
+
958
+ NK_PUBLIC void nk_dots_pack_f64_smef64(nk_f64_t const *b, nk_size_t columns, nk_size_t depth,
959
+ nk_size_t b_stride_in_bytes, void *b_packed) {
960
+
961
+ nk_size_t const b_stride_elements = b_stride_in_bytes / sizeof(nk_f64_t);
962
+
963
+ nk_size_t const tile_dimension = nk_sme_cntd_();
964
+ nk_size_t const depth_tile_size = nk_sme_cntw_();
965
+ nk_size_t const interleaved_stride = 3 * tile_dimension;
966
+ nk_size_t const interleaved_tile_elements = depth_tile_size * interleaved_stride;
967
+
968
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
969
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth, depth_tile_size);
970
+ nk_size_t const total_tiles = column_tile_count * depth_tile_count;
971
+
972
+ // Write single header
973
+ nk_dots_sme_packed_header_t *header = (nk_dots_sme_packed_header_t *)b_packed;
974
+ header->column_tile_count = (nk_u32_t)column_tile_count;
975
+ header->depth_tile_count = (nk_u32_t)depth_tile_count;
976
+ header->columns = (nk_u32_t)columns;
977
+ header->depth = (nk_u32_t)depth;
978
+ header->svl_bytes = (nk_u32_t)nk_sme_cntb_();
979
+
980
+ nk_f32_t *tiles = (nk_f32_t *)((char *)b_packed + sizeof(nk_dots_sme_packed_header_t));
981
+
982
+ // Zero-initialize all tiles (handles partial tile padding)
983
+ nk_size_t const total_elements = total_tiles * interleaved_tile_elements;
984
+ for (nk_size_t i = 0; i < total_elements; i++) tiles[i] = 0.0f;
985
+
986
+ // Inline tiling + 3-way mantissa-mask split with interleaved slice layout.
987
+ // Per depth step depth_idx, 3 slices are stored contiguously:
988
+ // tiles[tile_output + depth_idx * interleaved_stride + slice * tile_dimension + column_idx]
989
+ for (nk_size_t column_tile_idx = 0; column_tile_idx < column_tile_count; column_tile_idx++) {
990
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
991
+ nk_f32_t *tile_output = tiles +
992
+ (column_tile_idx * depth_tile_count + depth_tile_idx) * interleaved_tile_elements;
993
+
994
+ nk_size_t const column_start = column_tile_idx * tile_dimension;
995
+ nk_size_t const k_start = depth_tile_idx * depth_tile_size;
996
+ nk_size_t const columns_to_pack = (column_start + tile_dimension <= columns) ? tile_dimension
997
+ : (columns - column_start);
998
+ nk_size_t const depth_to_pack = (k_start + depth_tile_size <= depth) ? depth_tile_size : (depth - k_start);
999
+
1000
+ for (nk_size_t column_idx = 0; column_idx < columns_to_pack; column_idx++) {
1001
+ for (nk_size_t depth_idx = 0; depth_idx < depth_to_pack; depth_idx++) {
1002
+ nk_f64_t val = b[(column_start + column_idx) * b_stride_elements + k_start + depth_idx];
1003
+ nk_f64_t slice_0, slice_1, slice_2;
1004
+ nk_f64_smef64_ozaki_split_f64_(val, &slice_0, &slice_1, &slice_2);
1005
+
1006
+ tile_output[depth_idx * interleaved_stride + 0 * tile_dimension + column_idx] = (nk_f32_t)slice_0;
1007
+ tile_output[depth_idx * interleaved_stride + 1 * tile_dimension + column_idx] = (nk_f32_t)slice_1;
1008
+ tile_output[depth_idx * interleaved_stride + 2 * tile_dimension + column_idx] = (nk_f32_t)slice_2;
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ // Compute per-column squared norms and store after packed data
1015
+ nk_size_t const data_size = total_tiles * interleaved_tile_elements * sizeof(nk_f32_t);
1016
+ header->norms_offset = (nk_u32_t)(sizeof(nk_dots_sme_packed_header_t) + data_size);
1017
+ nk_f64_t *norms_ptr = (nk_f64_t *)((char *)b_packed + header->norms_offset);
1018
+ for (nk_size_t col = 0; col < columns; col++) {
1019
+ nk_f64_t const *col_data = (nk_f64_t const *)((char const *)b + col * b_stride_in_bytes);
1020
+ norms_ptr[col] = nk_dots_reduce_sumsq_f64_(col_data, depth);
1021
+ }
1022
+ }
1023
+
1024
+ __arm_new("za") static void nk_dots_packed_f64_smef64_streaming_( //
1025
+ nk_f64_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows, nk_size_t columns, nk_size_t depth,
1026
+ nk_size_t a_stride_elements, nk_size_t c_stride_elements) NK_STREAMING_ {
1027
+
1028
+ // Read header
1029
+ nk_dots_sme_packed_header_t const *header = (nk_dots_sme_packed_header_t const *)b_packed;
1030
+ nk_size_t const column_tile_count = header->column_tile_count;
1031
+ nk_size_t const depth_tile_count = header->depth_tile_count;
1032
+
1033
+ nk_size_t const tile_dimension = svcntd(); // 8 for 512-bit SVL
1034
+ nk_size_t const depth_tile_size = svcntw(); // 16 (f32 packing granularity)
1035
+ nk_size_t const interleaved_stride = 3 * tile_dimension; // 24
1036
+ nk_size_t const interleaved_tile_elements = depth_tile_size * interleaved_stride; // 384
1037
+ nk_size_t const depth_steps_per_batch = tile_dimension; // 8 f64 steps per ZA0 load
1038
+
1039
+ // B tile data pointer (f32, interleaved slices)
1040
+ nk_f32_t const *b_tiles = (nk_f32_t const *)((char const *)b_packed + sizeof(nk_dots_sme_packed_header_t));
1041
+
1042
+ svbool_t const predicate_all_b64x = svptrue_b64();
1043
+
1044
+ // Mantissa masks for in-register Ozaki splitting (19+17+17 bits)
1045
+ svuint64_t const ozaki_mask_19_u64x = svdup_u64(nk_f64_smef64_ozaki_mask_19_bits_());
1046
+ svuint64_t const ozaki_mask_17_u64x = svdup_u64(nk_f64_smef64_ozaki_mask_17_bits_());
1047
+
1048
+ // ZA0.D = A staging
1049
+ // ZA1-3.D = col0 merged accumulators (i+j=0,1,2)
1050
+ // ZA4-6.D = col1 merged accumulators (i+j=0,1,2) [2-col path only]
1051
+ for (nk_size_t row_tile_index = 0; row_tile_index < nk_size_divide_round_up_(rows, tile_dimension);
1052
+ row_tile_index++) {
1053
+ nk_size_t const row_start = row_tile_index * tile_dimension;
1054
+ nk_size_t const rows_remaining = (row_start + tile_dimension <= rows) ? tile_dimension : (rows - row_start);
1055
+ svbool_t const row_predicate_b64x = svwhilelt_b64_u64(0u, rows_remaining);
1056
+
1057
+ nk_size_t column_tile_index = 0;
1058
+
1059
+ // 2-column fast path: process 2 column tiles per iteration, A staged once
1060
+ for (; column_tile_index + 2 <= column_tile_count; column_tile_index += 2) {
1061
+ nk_size_t const column_start_0 = column_tile_index * tile_dimension;
1062
+ nk_size_t const column_start_1 = (column_tile_index + 1) * tile_dimension;
1063
+ nk_size_t const columns_remaining_0 = (column_start_0 + tile_dimension <= columns)
1064
+ ? tile_dimension
1065
+ : (columns - column_start_0);
1066
+ nk_size_t const columns_remaining_1 = (column_start_1 + tile_dimension <= columns)
1067
+ ? tile_dimension
1068
+ : (columns - column_start_1);
1069
+ svbool_t const column_predicate_0_b64x = svwhilelt_b64_u64(0u, columns_remaining_0);
1070
+ svbool_t const column_predicate_1_b64x = svwhilelt_b64_u64(0u, columns_remaining_1);
1071
+
1072
+ // Zero ZA1-6 (3 accumulators × 2 column tiles)
1073
+ svzero_mask_za(nk_sme_zero_za64_tiles_1_6_);
1074
+
1075
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
1076
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
1077
+
1078
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
1079
+ depth_batch_start += depth_steps_per_batch) {
1080
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
1081
+ ? depth_batch_start + depth_steps_per_batch
1082
+ : depth_tile_size;
1083
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
1084
+
1085
+ if (depth_offset + depth_batch_start >= depth) break;
1086
+
1087
+ // Load A rows into ZA0.D (shared for both column tiles)
1088
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
1089
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++) {
1090
+ nk_size_t const a_row = row_start + row_in_tile;
1091
+ svbool_t const a_depth_predicate_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start,
1092
+ depth);
1093
+ svld1_hor_za64(0, row_in_tile, a_depth_predicate_b64x,
1094
+ &a[a_row * a_stride_elements + depth_offset + depth_batch_start]);
1095
+ }
1096
+
1097
+ // B base offsets for both column tiles
1098
+ nk_size_t const b_batch_offset_0 = (column_tile_index * depth_tile_count + depth_tile_idx) *
1099
+ interleaved_tile_elements +
1100
+ depth_batch_start * interleaved_stride;
1101
+ nk_size_t const b_batch_offset_1 = ((column_tile_index + 1) * depth_tile_count + depth_tile_idx) *
1102
+ interleaved_tile_elements +
1103
+ depth_batch_start * interleaved_stride;
1104
+
1105
+ for (nk_size_t step = 0; step < batch_size; step++) {
1106
+ nk_size_t const k_abs = depth_offset + depth_batch_start + step;
1107
+ if (k_abs >= depth) break;
1108
+
1109
+ // Read A column from ZA0 and split into 3 Ozaki slices
1110
+ svfloat64_t a_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), row_predicate_b64x, 0, step);
1111
+ svuint64_t a_bits_u64x = svreinterpret_u64_f64(a_f64x);
1112
+ svfloat64_t a_slice_0_f64x = svreinterpret_f64_u64(
1113
+ svand_u64_x(predicate_all_b64x, a_bits_u64x, ozaki_mask_19_u64x));
1114
+ svfloat64_t residual_a_f64x = svsub_f64_x(predicate_all_b64x, a_f64x, a_slice_0_f64x);
1115
+ svuint64_t residual_a_bits_u64x = svreinterpret_u64_f64(residual_a_f64x);
1116
+ svfloat64_t a_slice_1_f64x = svreinterpret_f64_u64(
1117
+ svand_u64_x(predicate_all_b64x, residual_a_bits_u64x, ozaki_mask_17_u64x));
1118
+ svfloat64_t a_slice_2_f64x = svsub_f64_x(predicate_all_b64x, residual_a_f64x, a_slice_1_f64x);
1119
+
1120
+ // Load all 6 B slices upfront (3 per column tile) for pipeline interleaving
1121
+ nk_size_t const b_tile_offset_0 = b_batch_offset_0 + step * interleaved_stride;
1122
+ nk_size_t const b_tile_offset_1 = b_batch_offset_1 + step * interleaved_stride;
1123
+ svfloat64_t b_column_0_slice_0_f64x = svcvt_f64_f32_x(
1124
+ predicate_all_b64x,
1125
+ svreinterpret_f32_u64(
1126
+ svld1uw_u64(predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_0))));
1127
+ svfloat64_t b_column_0_slice_1_f64x = svcvt_f64_f32_x(
1128
+ predicate_all_b64x,
1129
+ svreinterpret_f32_u64(svld1uw_u64(
1130
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_0 + tile_dimension))));
1131
+ svfloat64_t b_column_0_slice_2_f64x = svcvt_f64_f32_x(
1132
+ predicate_all_b64x, svreinterpret_f32_u64(svld1uw_u64(
1133
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_0 +
1134
+ 2 * tile_dimension))));
1135
+ svfloat64_t b_column_1_slice_0_f64x = svcvt_f64_f32_x(
1136
+ predicate_all_b64x,
1137
+ svreinterpret_f32_u64(
1138
+ svld1uw_u64(predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_1))));
1139
+ svfloat64_t b_column_1_slice_1_f64x = svcvt_f64_f32_x(
1140
+ predicate_all_b64x,
1141
+ svreinterpret_f32_u64(svld1uw_u64(
1142
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_1 + tile_dimension))));
1143
+ svfloat64_t b_column_1_slice_2_f64x = svcvt_f64_f32_x(
1144
+ predicate_all_b64x, svreinterpret_f32_u64(svld1uw_u64(
1145
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset_1 +
1146
+ 2 * tile_dimension))));
1147
+
1148
+ // 12 FMOPAs interleaved across 6 tiles to eliminate WAW pipeline stalls.
1149
+ // Same-tile accumulation order preserved (bit-identical output).
1150
+ // Tile gaps: ZA3 at 0,6,10 (6,4); ZA6 at 1,7,11 (6,4); ZA2 at 4,8 (4);
1151
+ // ZA5 at 5,9 (4); ZA1 at 2; ZA4 at 3. All gaps >= 4-cycle latency.
1152
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_0_b64x, a_slice_0_f64x,
1153
+ b_column_0_slice_2_f64x); // ZA3: i+j=2 (1/3)
1154
+ svmopa_za64_f64_m(6, row_predicate_b64x, column_predicate_1_b64x, a_slice_0_f64x,
1155
+ b_column_1_slice_2_f64x); // ZA6: i+j=2 (1/3)
1156
+ svmopa_za64_f64_m(1, row_predicate_b64x, column_predicate_0_b64x, a_slice_0_f64x,
1157
+ b_column_0_slice_0_f64x); // ZA1: i+j=0
1158
+ svmopa_za64_f64_m(4, row_predicate_b64x, column_predicate_1_b64x, a_slice_0_f64x,
1159
+ b_column_1_slice_0_f64x); // ZA4: i+j=0
1160
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_0_b64x, a_slice_0_f64x,
1161
+ b_column_0_slice_1_f64x); // ZA2: i+j=1 (1/2)
1162
+ svmopa_za64_f64_m(5, row_predicate_b64x, column_predicate_1_b64x, a_slice_0_f64x,
1163
+ b_column_1_slice_1_f64x); // ZA5: i+j=1 (1/2)
1164
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_0_b64x, a_slice_1_f64x,
1165
+ b_column_0_slice_1_f64x); // ZA3: i+j=2 (2/3)
1166
+ svmopa_za64_f64_m(6, row_predicate_b64x, column_predicate_1_b64x, a_slice_1_f64x,
1167
+ b_column_1_slice_1_f64x); // ZA6: i+j=2 (2/3)
1168
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_0_b64x, a_slice_1_f64x,
1169
+ b_column_0_slice_0_f64x); // ZA2: i+j=1 (2/2)
1170
+ svmopa_za64_f64_m(5, row_predicate_b64x, column_predicate_1_b64x, a_slice_1_f64x,
1171
+ b_column_1_slice_0_f64x); // ZA5: i+j=1 (2/2)
1172
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_0_b64x, a_slice_2_f64x,
1173
+ b_column_0_slice_0_f64x); // ZA3: i+j=2 (3/3)
1174
+ svmopa_za64_f64_m(6, row_predicate_b64x, column_predicate_1_b64x, a_slice_2_f64x,
1175
+ b_column_1_slice_0_f64x); // ZA6: i+j=2 (3/3)
1176
+ }
1177
+ }
1178
+ }
1179
+
1180
+ // Simple summation for col tile 0: ZA3 + ZA2 + ZA1 (smallest to largest)
1181
+ for (nk_size_t row = 0; row < rows_remaining; row++) {
1182
+ nk_f64_t *c_row = c + (row_start + row) * c_stride_elements + column_start_0;
1183
+ svfloat64_t result_f64x = svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 3, row);
1184
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1185
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 2, row));
1186
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1187
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 1, row));
1188
+ svst1_f64(column_predicate_0_b64x, c_row, result_f64x);
1189
+ }
1190
+
1191
+ // Simple summation for col tile 1: ZA6 + ZA5 + ZA4 (smallest to largest)
1192
+ for (nk_size_t row = 0; row < rows_remaining; row++) {
1193
+ nk_f64_t *c_row = c + (row_start + row) * c_stride_elements + column_start_1;
1194
+ svfloat64_t result_f64x = svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 6, row);
1195
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1196
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 5, row));
1197
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1198
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 4, row));
1199
+ svst1_f64(column_predicate_1_b64x, c_row, result_f64x);
1200
+ }
1201
+ }
1202
+
1203
+ // 1-column remainder (when column_tile_count is odd)
1204
+ for (; column_tile_index < column_tile_count; column_tile_index++) {
1205
+ nk_size_t const column_start = column_tile_index * tile_dimension;
1206
+ nk_size_t const columns_remaining = (column_start + tile_dimension <= columns) ? tile_dimension
1207
+ : (columns - column_start);
1208
+ svbool_t const column_predicate_b64x = svwhilelt_b64_u64(0u, columns_remaining);
1209
+
1210
+ // Zero ZA1-3 (3 merged accumulators)
1211
+ svzero_mask_za(nk_sme_zero_za64_tiles_1_3_);
1212
+
1213
+ for (nk_size_t depth_tile_idx = 0; depth_tile_idx < depth_tile_count; depth_tile_idx++) {
1214
+ nk_size_t const depth_offset = depth_tile_idx * depth_tile_size;
1215
+
1216
+ for (nk_size_t depth_batch_start = 0; depth_batch_start < depth_tile_size;
1217
+ depth_batch_start += depth_steps_per_batch) {
1218
+ nk_size_t const depth_batch_end = (depth_batch_start + depth_steps_per_batch < depth_tile_size)
1219
+ ? depth_batch_start + depth_steps_per_batch
1220
+ : depth_tile_size;
1221
+ nk_size_t const batch_size = depth_batch_end - depth_batch_start;
1222
+
1223
+ if (depth_offset + depth_batch_start >= depth) break;
1224
+
1225
+ // Load A rows into ZA0.D
1226
+ svzero_mask_za(nk_sme_zero_za64_tile_0_);
1227
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++) {
1228
+ nk_size_t const a_row = row_start + row_in_tile;
1229
+ svbool_t const a_depth_predicate_b64x = svwhilelt_b64_u64(depth_offset + depth_batch_start,
1230
+ depth);
1231
+ svld1_hor_za64(0, row_in_tile, a_depth_predicate_b64x,
1232
+ &a[a_row * a_stride_elements + depth_offset + depth_batch_start]);
1233
+ }
1234
+
1235
+ nk_size_t const b_batch_offset = (column_tile_index * depth_tile_count + depth_tile_idx) *
1236
+ interleaved_tile_elements +
1237
+ depth_batch_start * interleaved_stride;
1238
+
1239
+ for (nk_size_t step = 0; step < batch_size; step++) {
1240
+ nk_size_t const k_abs = depth_offset + depth_batch_start + step;
1241
+ if (k_abs >= depth) break;
1242
+
1243
+ // Read A column from ZA0 and split into 3 Ozaki slices
1244
+ svfloat64_t a_f64x = svread_ver_za64_f64_m(svdup_f64(0.0), row_predicate_b64x, 0, step);
1245
+ svuint64_t a_bits_u64x = svreinterpret_u64_f64(a_f64x);
1246
+ svfloat64_t a_slice_0_f64x = svreinterpret_f64_u64(
1247
+ svand_u64_x(predicate_all_b64x, a_bits_u64x, ozaki_mask_19_u64x));
1248
+ svfloat64_t residual_a_f64x = svsub_f64_x(predicate_all_b64x, a_f64x, a_slice_0_f64x);
1249
+ svuint64_t residual_a_bits_u64x = svreinterpret_u64_f64(residual_a_f64x);
1250
+ svfloat64_t a_slice_1_f64x = svreinterpret_f64_u64(
1251
+ svand_u64_x(predicate_all_b64x, residual_a_bits_u64x, ozaki_mask_17_u64x));
1252
+ svfloat64_t a_slice_2_f64x = svsub_f64_x(predicate_all_b64x, residual_a_f64x, a_slice_1_f64x);
1253
+
1254
+ // Load 3 B slices (contiguous in interleaved layout)
1255
+ nk_size_t const b_tile_offset = b_batch_offset + step * interleaved_stride;
1256
+ svfloat64_t b_slice_0_f64x = svcvt_f64_f32_x(
1257
+ predicate_all_b64x, svreinterpret_f32_u64(svld1uw_u64(
1258
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset))));
1259
+ svfloat64_t b_slice_1_f64x = svcvt_f64_f32_x(
1260
+ predicate_all_b64x,
1261
+ svreinterpret_f32_u64(svld1uw_u64(
1262
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset + tile_dimension))));
1263
+ svfloat64_t b_slice_2_f64x = svcvt_f64_f32_x(
1264
+ predicate_all_b64x,
1265
+ svreinterpret_f32_u64(svld1uw_u64(
1266
+ predicate_all_b64x, (nk_u32_t const *)(b_tiles + b_tile_offset + 2 * tile_dimension))));
1267
+
1268
+ // 6 FMOPAs reordered to minimize WAW pipeline stalls on 3 tiles.
1269
+ // Same-tile accumulation order preserved (bit-identical output).
1270
+ // Tile schedule: ZA3(0), ZA2(1), ZA1(2), ZA3(4), ZA2(5), ZA3(8).
1271
+ // 9 cycles vs 15 original (3 unavoidable bubbles with only 3 tiles).
1272
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
1273
+ b_slice_2_f64x); // ZA3: i+j=2 (1/3)
1274
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
1275
+ b_slice_1_f64x); // ZA2: i+j=1 (1/2)
1276
+ svmopa_za64_f64_m(1, row_predicate_b64x, column_predicate_b64x, a_slice_0_f64x,
1277
+ b_slice_0_f64x); // ZA1: i+j=0
1278
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_1_f64x,
1279
+ b_slice_1_f64x); // ZA3: i+j=2 (2/3)
1280
+ svmopa_za64_f64_m(2, row_predicate_b64x, column_predicate_b64x, a_slice_1_f64x,
1281
+ b_slice_0_f64x); // ZA2: i+j=1 (2/2)
1282
+ svmopa_za64_f64_m(3, row_predicate_b64x, column_predicate_b64x, a_slice_2_f64x,
1283
+ b_slice_0_f64x); // ZA3: i+j=2 (3/3)
1284
+ }
1285
+ }
1286
+ }
1287
+
1288
+ // Simple summation: ZA3 + ZA2 + ZA1 (smallest to largest)
1289
+ for (nk_size_t row = 0; row < rows_remaining; row++) {
1290
+ nk_f64_t *c_row = c + (row_start + row) * c_stride_elements + column_start;
1291
+ svfloat64_t result_f64x = svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 3, row);
1292
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1293
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 2, row));
1294
+ result_f64x = svadd_f64_x(predicate_all_b64x, result_f64x,
1295
+ svread_hor_za64_f64_m(svdup_f64(0.0), predicate_all_b64x, 1, row));
1296
+ svst1_f64(column_predicate_b64x, c_row, result_f64x);
1297
+ }
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ NK_PUBLIC void nk_dots_packed_f64_smef64( //
1303
+ nk_f64_t const *a, void const *b_packed, nk_f64_t *c, nk_size_t rows, nk_size_t columns, nk_size_t depth,
1304
+ nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) {
1305
+
1306
+ nk_size_t const a_stride_elements = a_stride_in_bytes / sizeof(nk_f64_t);
1307
+ nk_size_t const c_stride_elements = c_stride_in_bytes / sizeof(nk_f64_t);
1308
+
1309
+ nk_sme_start_streaming_();
1310
+ nk_dots_packed_f64_smef64_streaming_(a, b_packed, c, rows, columns, depth, a_stride_elements, c_stride_elements);
1311
+ nk_sme_stop_streaming_();
1312
+ }
1313
+
1314
+ #pragma endregion F64 Floats
1315
+
1316
+ #if defined(__clang__)
1317
+ #pragma clang attribute pop
1318
+ #elif defined(__GNUC__)
1319
+ #pragma GCC pop_options
1320
+ #endif
1321
+
1322
+ #if defined(__cplusplus)
1323
+ } // extern "C"
1324
+ #endif
1325
+
1326
+ #endif // NK_TARGET_SME
1327
+ #endif // NK_TARGET_ARM64_
1328
+ #endif // NK_DOTS_SMEF64_H