usearch 2.21.3 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +235 -52
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,993 @@
1
+ /**
2
+ * @brief SIMD-accelerated MaxSim (ColBERT late-interaction) for SME.
3
+ * @file include/numkong/maxsim/sme.h
4
+ * @author Ash Vardanian
5
+ * @date February 10, 2026
6
+ *
7
+ * Computes MaxSim(Q, D) = Σᵢ maxⱼ dot(qᵢ, dⱼ) using ARM SME outer products.
8
+ *
9
+ * Both Q and D are pre-packed with `nk_dots_pack_bf16_sme` from `dots/sme.h`.
10
+ * This frees all 4 ZA tiles for accumulation (vs 3 with A-side staging).
11
+ *
12
+ * Key optimization: vertical column reads for max reduction.
13
+ * Traditional extraction reads tile rows then calls `svmaxv` (horizontal max, ~8cy).
14
+ * Our approach reads tile columns with `svread_ver_za32_f32_m`:
15
+ *
16
+ * - Each column read gives dot products of all query tokens vs one doc token.
17
+ * - Element-wise `svmax` (~1cy) updates a running max vector across doc tokens.
18
+ * - Only `svaddv` at the very end: ⌈n_q/16⌉ = 2 horizontal reductions total.
19
+ *
20
+ * This is ~100x fewer horizontal reductions for typical ColBERT dimensions.
21
+ *
22
+ * ZA tile layout after BFMOPA accumulation (16x16 f32):
23
+ *
24
+ * - Row i, Column j = dot(q_{tile_row_start + i}, d_{tile_col_start + j})
25
+ * - Vertical column read of column j → similarities of all 16 q tokens to doc token j
26
+ * - Element-wise max across columns → per-query-token max over doc tokens in this tile group
27
+ *
28
+ * Benchmark results (Apple M4, SVL=512):
29
+ *
30
+ * Dimensions dots_packed GEMM maxsim fused GEMM speedup End-to-end speedup
31
+ * 32×128×128 (ColBERT) 840 GFLOPS 1516 GFLOPS 1.81× 5.10×
32
+ * 32×256×128 1037 GFLOPS 1591 GFLOPS 1.53× 5.17×
33
+ * 64×512×128 1016 GFLOPS 1651 GFLOPS 1.62× 5.42×
34
+ * 32×128×256 859 GFLOPS 1725 GFLOPS 2.01× 4.06×
35
+ * 32×1024×768 (BERT) 1124 GFLOPS 1932 GFLOPS 1.72× 2.61×
36
+ *
37
+ * Speedup sources:
38
+ *
39
+ * 1. Pre-packing both sides → 4 ZA tiles for accumulation (vs 3 with A-staging): +33% MOPA throughput
40
+ * 2. No output matrix materialization → eliminates M×N f32 memory round-trip
41
+ * 3. Vertical column reads → ~128 element-wise svmax (1cy) vs ~256 svmaxv horizontal reductions (8cy)
42
+ */
43
+ #ifndef NK_MAXSIM_SME_H
44
+ #define NK_MAXSIM_SME_H
45
+
46
+ #if NK_TARGET_ARM64_
47
+ #if NK_TARGET_SME
48
+
49
+ #include "numkong/dots/sme.h" // `nk_dots_sme_packed_header_t`
50
+ #include "numkong/reduce/sve.h" // `nk_svaddv_f64_`
51
+
52
+ #if defined(__cplusplus)
53
+ extern "C" {
54
+ #endif
55
+
56
+ #if defined(__clang__)
57
+ #pragma clang attribute push(__attribute__((target("sme"))), apply_to = function)
58
+ #elif defined(__GNUC__)
59
+ #pragma GCC push_options
60
+ #pragma GCC target("+sme")
61
+ #endif
62
+
63
+ /**
64
+ * Packed header for MaxSim SME kernels. Used by f32 (i8 screening + f32 refinement)
65
+ * and bf16/f16 (BFMOPA/FMOPA + angular normalization) kernels.
66
+ *
67
+ * For f32: stores i8 tile-interleaved data, f32 squared norms, AND f32 originals.
68
+ * For bf16/f16: stores tile-interleaved data and f32 inverse norms (1/||v||).
69
+ * originals_offset and original_stride are 0 (unused).
70
+ */
71
+ typedef struct {
72
+ nk_u32_t column_tile_count; // ceil(n / tile_dimension)
73
+ nk_u32_t depth_tile_count; // ceil(depth / expansion)
74
+ nk_u32_t columns; // actual vector count (for predicates)
75
+ nk_u32_t depth; // actual depth
76
+ nk_u32_t svl_bytes; // SVL in bytes at pack time (validation)
77
+ nk_u32_t norms_offset; // byte offset -> per-vector norms (squared for f32, inverse for bf16/f16)
78
+ nk_u32_t originals_offset; // byte offset -> f32 original vectors (0 for bf16/f16)
79
+ nk_u32_t original_stride; // row stride in bytes for originals (64B-aligned, 0 for bf16/f16)
80
+ nk_u32_t reserved[8]; // padding to 64 bytes
81
+ } nk_maxsim_sme_packed_header_t;
82
+
83
+ NK_STATIC_ASSERT(sizeof(nk_maxsim_sme_packed_header_t) == 64, nk_maxsim_sme_packed_header_must_be_64_bytes);
84
+
85
+ /**
86
+ * MaxSim f16 kernel: both Q and D pre-packed, vertical column read extraction.
87
+ *
88
+ * 4-tile fast path: processes 4 doc column tiles simultaneously using ZA0-ZA3.
89
+ * Inner loop per depth_step: 1 Q load + 4 D loads + 4 FMOPA = 9 ops.
90
+ * Extraction per 4-tile group: 4×16 = 64 vertical reads + 64 svmax = ~128 cycles.
91
+ *
92
+ * 1-tile remainder: uses ZA0 only, with predicated loads for partial tiles.
93
+ */
94
+ __arm_new("za") static void nk_maxsim_packed_f16_streaming_( //
95
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
96
+ nk_size_t depth, nk_f32_t *result) NK_STREAMING_ {
97
+
98
+ nk_maxsim_sme_packed_header_t const *query_header = (nk_maxsim_sme_packed_header_t const *)query_packed;
99
+ nk_maxsim_sme_packed_header_t const *document_header = (nk_maxsim_sme_packed_header_t const *)document_packed;
100
+ nk_size_t const depth_step_count = query_header->depth_tile_count;
101
+ nk_size_t const query_row_tiles = query_header->column_tile_count;
102
+ nk_size_t const document_col_tiles = document_header->column_tile_count;
103
+
104
+ nk_size_t const tile_dimension = svcntw(); // 16: ZA32 tile dimension
105
+ nk_size_t const vector_elements = svcnth(); // 32: f16 elements per SVE vector
106
+
107
+ nk_f16_t const *query_vecs = (nk_f16_t const *)((char const *)query_packed + sizeof(nk_maxsim_sme_packed_header_t));
108
+ nk_f16_t const *document_vecs = (nk_f16_t const *)((char const *)document_packed +
109
+ sizeof(nk_maxsim_sme_packed_header_t));
110
+
111
+ nk_f32_t const *query_inverse_norms = (nk_f32_t const *)((char const *)query_packed + query_header->norms_offset);
112
+ nk_f32_t const *document_inverse_norms = (nk_f32_t const *)((char const *)document_packed +
113
+ document_header->norms_offset);
114
+
115
+ svbool_t const predicate_all_b16x = svptrue_b16();
116
+ svbool_t const predicate_all_b32x = svptrue_b32();
117
+
118
+ nk_f32_t total_angular_distance = 0.0f;
119
+
120
+ for (nk_size_t row_tile_index = 0; row_tile_index < query_row_tiles; row_tile_index++) {
121
+ nk_size_t const row_start = row_tile_index * tile_dimension;
122
+ nk_size_t const rows_remaining = (row_start + tile_dimension <= query_count) ? tile_dimension
123
+ : (query_count - row_start);
124
+ svbool_t const row_predicate_b16x = (rows_remaining == tile_dimension)
125
+ ? svptrue_b16()
126
+ : svwhilelt_b16_u64(0u, rows_remaining * 2);
127
+ svbool_t const row_predicate_b32x = (rows_remaining == tile_dimension) ? svptrue_b32()
128
+ : svwhilelt_b32_u64(0u, rows_remaining);
129
+
130
+ // Running max + argmax vectors for angular distance finalization
131
+ svfloat32_t running_maximum_f32x = svdup_f32(NK_F32_MIN);
132
+ svuint32_t running_argmax_u32x = svdup_u32(0);
133
+
134
+ nk_size_t column_tile_index = 0;
135
+
136
+ // Fast path: 4 doc column tiles at a time using ZA0-ZA3
137
+ for (; column_tile_index + 4 <= document_col_tiles; column_tile_index += 4) {
138
+ svzero_za(); // Zero all 4 tiles
139
+
140
+ // Accumulate: for each depth step, load Q vector and 4 D vectors, issue 4 FMOPAs
141
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
142
+ svfloat16_t query_packed_f16x = svld1_f16(
143
+ row_predicate_b16x,
144
+ (float16_t const *)(query_vecs +
145
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
146
+ svfloat16_t document_packed_0_f16x = svld1_f16(
147
+ predicate_all_b16x,
148
+ (float16_t const *)(document_vecs +
149
+ ((column_tile_index + 0) * depth_step_count + depth_step) * vector_elements));
150
+ svfloat16_t document_packed_1_f16x = svld1_f16(
151
+ predicate_all_b16x,
152
+ (float16_t const *)(document_vecs +
153
+ ((column_tile_index + 1) * depth_step_count + depth_step) * vector_elements));
154
+ svfloat16_t document_packed_2_f16x = svld1_f16(
155
+ predicate_all_b16x,
156
+ (float16_t const *)(document_vecs +
157
+ ((column_tile_index + 2) * depth_step_count + depth_step) * vector_elements));
158
+ svfloat16_t document_packed_3_f16x = svld1_f16(
159
+ predicate_all_b16x,
160
+ (float16_t const *)(document_vecs +
161
+ ((column_tile_index + 3) * depth_step_count + depth_step) * vector_elements));
162
+ svmopa_za32_f16_m(0, row_predicate_b16x, predicate_all_b16x, query_packed_f16x, document_packed_0_f16x);
163
+ svmopa_za32_f16_m(1, row_predicate_b16x, predicate_all_b16x, query_packed_f16x, document_packed_1_f16x);
164
+ svmopa_za32_f16_m(2, row_predicate_b16x, predicate_all_b16x, query_packed_f16x, document_packed_2_f16x);
165
+ svmopa_za32_f16_m(3, row_predicate_b16x, predicate_all_b16x, query_packed_f16x, document_packed_3_f16x);
166
+ }
167
+
168
+ // Vertical column extraction + argmax update (manually unrolled over 4 tiles)
169
+ for (nk_size_t column_within_tile = 0; column_within_tile < tile_dimension; column_within_tile++) {
170
+ // Tile 0
171
+ {
172
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 0) * tile_dimension + column_within_tile);
173
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 0,
174
+ column_within_tile);
175
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
176
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
177
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
178
+ }
179
+ // Tile 1
180
+ {
181
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 1) * tile_dimension + column_within_tile);
182
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 1,
183
+ column_within_tile);
184
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
185
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
186
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
187
+ }
188
+ // Tile 2
189
+ {
190
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 2) * tile_dimension + column_within_tile);
191
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 2,
192
+ column_within_tile);
193
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
194
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
195
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
196
+ }
197
+ // Tile 3
198
+ {
199
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 3) * tile_dimension + column_within_tile);
200
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 3,
201
+ column_within_tile);
202
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
203
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
204
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
205
+ }
206
+ }
207
+ }
208
+
209
+ // Remainder: 1 doc column tile at a time using ZA0 only
210
+ for (; column_tile_index < document_col_tiles; column_tile_index++) {
211
+ nk_size_t const col_start = column_tile_index * tile_dimension;
212
+ nk_size_t const cols_remaining = (col_start + tile_dimension <= document_count)
213
+ ? tile_dimension
214
+ : (document_count - col_start);
215
+ svbool_t const column_predicate_b16x = (cols_remaining == tile_dimension)
216
+ ? svptrue_b16()
217
+ : svwhilelt_b16_u64(0u, cols_remaining * 2);
218
+
219
+ svzero_mask_za(nk_sme_zero_za32_tile_0_); // Zero ZA0 only
220
+
221
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
222
+ svfloat16_t query_packed_f16x = svld1_f16(
223
+ row_predicate_b16x,
224
+ (float16_t const *)(query_vecs +
225
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
226
+ svfloat16_t document_packed_f16x = svld1_f16(
227
+ column_predicate_b16x,
228
+ (float16_t const *)(document_vecs +
229
+ (column_tile_index * depth_step_count + depth_step) * vector_elements));
230
+ svmopa_za32_f16_m(0, row_predicate_b16x, column_predicate_b16x, query_packed_f16x,
231
+ document_packed_f16x);
232
+ }
233
+
234
+ // Vertical column extraction from ZA0 + argmax update
235
+ for (nk_size_t column_within_tile = 0; column_within_tile < cols_remaining; column_within_tile++) {
236
+ nk_u32_t document_index = (nk_u32_t)(col_start + column_within_tile);
237
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 0,
238
+ column_within_tile);
239
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
240
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
241
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
242
+ }
243
+ }
244
+
245
+ // Angular distance finalization — SVE-width vector ops
246
+ // Gather document inverse norms via argmax indices (no SVE gather in streaming mode)
247
+ nk_u32_t best_document_indices[64];
248
+ nk_f32_t document_inverse_norms_gathered[64];
249
+ svst1_u32(row_predicate_b32x, best_document_indices, running_argmax_u32x);
250
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++)
251
+ document_inverse_norms_gathered[row_in_tile] = document_inverse_norms[best_document_indices[row_in_tile]];
252
+
253
+ // SVE-width: cosine = dot * inv_norm_q * inv_norm_d, angular = max(1 - cosine, 0)
254
+ svfloat32_t query_inverse_norms_f32x = svld1_f32(row_predicate_b32x, query_inverse_norms + row_start);
255
+ svfloat32_t document_inverse_norms_f32x = svld1_f32(row_predicate_b32x, document_inverse_norms_gathered);
256
+ svfloat32_t cosine_f32x = svmul_f32_x(
257
+ row_predicate_b32x, svmul_f32_x(row_predicate_b32x, running_maximum_f32x, query_inverse_norms_f32x),
258
+ document_inverse_norms_f32x);
259
+ svfloat32_t angular_distance_f32x = svmax_f32_x(
260
+ row_predicate_b32x, svsub_f32_x(row_predicate_b32x, svdup_f32(1.0f), cosine_f32x), svdup_f32(0.0f));
261
+ total_angular_distance += nk_svaddv_f32_(row_predicate_b32x, angular_distance_f32x);
262
+ }
263
+
264
+ *result = total_angular_distance;
265
+ }
266
+
267
+ NK_PUBLIC void nk_maxsim_packed_f16_sme( //
268
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
269
+ nk_size_t depth, nk_f32_t *result) {
270
+
271
+ nk_sme_start_streaming_();
272
+ nk_maxsim_packed_f16_streaming_(query_packed, document_packed, query_count, document_count, depth, result);
273
+ nk_sme_stop_streaming_();
274
+ }
275
+
276
+ /**
277
+ * MaxSim bf16 kernel: both Q and D pre-packed, vertical column read extraction.
278
+ *
279
+ * 4-tile fast path: processes 4 doc column tiles simultaneously using ZA0-ZA3.
280
+ * Inner loop per depth_step: 1 Q load + 4 D loads + 4 BFMOPA = 9 ops.
281
+ * Extraction per 4-tile group: 4×16 = 64 vertical reads + 64 svmax = ~128 cycles.
282
+ *
283
+ * 1-tile remainder: uses ZA0 only, with predicated loads for partial tiles.
284
+ */
285
+ __arm_new("za") static void nk_maxsim_packed_bf16_streaming_( //
286
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
287
+ nk_size_t depth, nk_f32_t *result) NK_STREAMING_ {
288
+
289
+ nk_maxsim_sme_packed_header_t const *query_header = (nk_maxsim_sme_packed_header_t const *)query_packed;
290
+ nk_maxsim_sme_packed_header_t const *document_header = (nk_maxsim_sme_packed_header_t const *)document_packed;
291
+ nk_size_t const depth_step_count = query_header->depth_tile_count;
292
+ nk_size_t const query_row_tiles = query_header->column_tile_count;
293
+ nk_size_t const document_col_tiles = document_header->column_tile_count;
294
+
295
+ nk_size_t const tile_dimension = svcntw(); // 16: ZA32 tile dimension
296
+ nk_size_t const vector_elements = svcnth(); // 32: bf16 elements per SVE vector
297
+
298
+ nk_bf16_t const *query_vecs = (nk_bf16_t const *)((char const *)query_packed +
299
+ sizeof(nk_maxsim_sme_packed_header_t));
300
+ nk_bf16_t const *document_vecs = (nk_bf16_t const *)((char const *)document_packed +
301
+ sizeof(nk_maxsim_sme_packed_header_t));
302
+
303
+ nk_f32_t const *query_inverse_norms = (nk_f32_t const *)((char const *)query_packed + query_header->norms_offset);
304
+ nk_f32_t const *document_inverse_norms = (nk_f32_t const *)((char const *)document_packed +
305
+ document_header->norms_offset);
306
+
307
+ svbool_t const predicate_all_b16x = svptrue_b16();
308
+ svbool_t const predicate_all_b32x = svptrue_b32();
309
+
310
+ nk_f32_t total_angular_distance = 0.0f;
311
+
312
+ for (nk_size_t row_tile_index = 0; row_tile_index < query_row_tiles; row_tile_index++) {
313
+ nk_size_t const row_start = row_tile_index * tile_dimension;
314
+ nk_size_t const rows_remaining = (row_start + tile_dimension <= query_count) ? tile_dimension
315
+ : (query_count - row_start);
316
+ svbool_t const row_predicate_b16x = (rows_remaining == tile_dimension)
317
+ ? svptrue_b16()
318
+ : svwhilelt_b16_u64(0u, rows_remaining * 2);
319
+ svbool_t const row_predicate_b32x = (rows_remaining == tile_dimension) ? svptrue_b32()
320
+ : svwhilelt_b32_u64(0u, rows_remaining);
321
+
322
+ // Running max + argmax vectors for angular distance finalization
323
+ svfloat32_t running_maximum_f32x = svdup_f32(NK_F32_MIN);
324
+ svuint32_t running_argmax_u32x = svdup_u32(0);
325
+
326
+ nk_size_t column_tile_index = 0;
327
+
328
+ // Fast path: 4 doc column tiles at a time using ZA0-ZA3
329
+ for (; column_tile_index + 4 <= document_col_tiles; column_tile_index += 4) {
330
+ svzero_za(); // Zero all 4 tiles
331
+
332
+ // Accumulate: for each depth step, load Q vector and 4 D vectors, issue 4 BFMOPAs
333
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
334
+ svbfloat16_t query_packed_bf16x = svld1_bf16(
335
+ row_predicate_b16x,
336
+ (bfloat16_t const *)(query_vecs +
337
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
338
+ svbfloat16_t document_packed_0_bf16x = svld1_bf16(
339
+ predicate_all_b16x,
340
+ (bfloat16_t const *)(document_vecs +
341
+ ((column_tile_index + 0) * depth_step_count + depth_step) * vector_elements));
342
+ svbfloat16_t document_packed_1_bf16x = svld1_bf16(
343
+ predicate_all_b16x,
344
+ (bfloat16_t const *)(document_vecs +
345
+ ((column_tile_index + 1) * depth_step_count + depth_step) * vector_elements));
346
+ svbfloat16_t document_packed_2_bf16x = svld1_bf16(
347
+ predicate_all_b16x,
348
+ (bfloat16_t const *)(document_vecs +
349
+ ((column_tile_index + 2) * depth_step_count + depth_step) * vector_elements));
350
+ svbfloat16_t document_packed_3_bf16x = svld1_bf16(
351
+ predicate_all_b16x,
352
+ (bfloat16_t const *)(document_vecs +
353
+ ((column_tile_index + 3) * depth_step_count + depth_step) * vector_elements));
354
+ svmopa_za32_bf16_m(0, row_predicate_b16x, predicate_all_b16x, query_packed_bf16x,
355
+ document_packed_0_bf16x);
356
+ svmopa_za32_bf16_m(1, row_predicate_b16x, predicate_all_b16x, query_packed_bf16x,
357
+ document_packed_1_bf16x);
358
+ svmopa_za32_bf16_m(2, row_predicate_b16x, predicate_all_b16x, query_packed_bf16x,
359
+ document_packed_2_bf16x);
360
+ svmopa_za32_bf16_m(3, row_predicate_b16x, predicate_all_b16x, query_packed_bf16x,
361
+ document_packed_3_bf16x);
362
+ }
363
+
364
+ // Vertical column extraction + argmax update (manually unrolled over 4 tiles)
365
+ for (nk_size_t column_within_tile = 0; column_within_tile < tile_dimension; column_within_tile++) {
366
+ // Tile 0
367
+ {
368
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 0) * tile_dimension + column_within_tile);
369
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 0,
370
+ column_within_tile);
371
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
372
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
373
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
374
+ }
375
+ // Tile 1
376
+ {
377
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 1) * tile_dimension + column_within_tile);
378
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 1,
379
+ column_within_tile);
380
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
381
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
382
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
383
+ }
384
+ // Tile 2
385
+ {
386
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 2) * tile_dimension + column_within_tile);
387
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 2,
388
+ column_within_tile);
389
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
390
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
391
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
392
+ }
393
+ // Tile 3
394
+ {
395
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 3) * tile_dimension + column_within_tile);
396
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 3,
397
+ column_within_tile);
398
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
399
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
400
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
401
+ }
402
+ }
403
+ }
404
+
405
+ // Remainder: 1 doc column tile at a time using ZA0 only
406
+ for (; column_tile_index < document_col_tiles; column_tile_index++) {
407
+ nk_size_t const col_start = column_tile_index * tile_dimension;
408
+ nk_size_t const cols_remaining = (col_start + tile_dimension <= document_count)
409
+ ? tile_dimension
410
+ : (document_count - col_start);
411
+ svbool_t const column_predicate_b16x = (cols_remaining == tile_dimension)
412
+ ? svptrue_b16()
413
+ : svwhilelt_b16_u64(0u, cols_remaining * 2);
414
+
415
+ svzero_mask_za(nk_sme_zero_za32_tile_0_); // Zero ZA0 only
416
+
417
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
418
+ svbfloat16_t query_packed_bf16x = svld1_bf16(
419
+ row_predicate_b16x,
420
+ (bfloat16_t const *)(query_vecs +
421
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
422
+ svbfloat16_t document_packed_bf16x = svld1_bf16(
423
+ column_predicate_b16x,
424
+ (bfloat16_t const *)(document_vecs +
425
+ (column_tile_index * depth_step_count + depth_step) * vector_elements));
426
+ svmopa_za32_bf16_m(0, row_predicate_b16x, column_predicate_b16x, query_packed_bf16x,
427
+ document_packed_bf16x);
428
+ }
429
+
430
+ // Vertical column extraction from ZA0 + argmax update
431
+ for (nk_size_t column_within_tile = 0; column_within_tile < cols_remaining; column_within_tile++) {
432
+ nk_u32_t document_index = (nk_u32_t)(col_start + column_within_tile);
433
+ svfloat32_t column_dots_f32x = svread_ver_za32_f32_m(svdup_f32(NK_F32_MIN), predicate_all_b32x, 0,
434
+ column_within_tile);
435
+ svbool_t is_better_bx = svcmpgt_f32(predicate_all_b32x, column_dots_f32x, running_maximum_f32x);
436
+ running_maximum_f32x = svsel_f32(is_better_bx, column_dots_f32x, running_maximum_f32x);
437
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
438
+ }
439
+ }
440
+
441
+ // Angular distance finalization — SVE-width vector ops
442
+ // Gather document inverse norms via argmax indices (no SVE gather in streaming mode)
443
+ nk_u32_t best_document_indices[64];
444
+ nk_f32_t document_inverse_norms_gathered[64];
445
+ svst1_u32(row_predicate_b32x, best_document_indices, running_argmax_u32x);
446
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++)
447
+ document_inverse_norms_gathered[row_in_tile] = document_inverse_norms[best_document_indices[row_in_tile]];
448
+
449
+ // SVE-width: cosine = dot * inv_norm_q * inv_norm_d, angular = max(1 - cosine, 0)
450
+ svfloat32_t query_inverse_norms_f32x = svld1_f32(row_predicate_b32x, query_inverse_norms + row_start);
451
+ svfloat32_t document_inverse_norms_f32x = svld1_f32(row_predicate_b32x, document_inverse_norms_gathered);
452
+ svfloat32_t cosine_f32x = svmul_f32_x(
453
+ row_predicate_b32x, svmul_f32_x(row_predicate_b32x, running_maximum_f32x, query_inverse_norms_f32x),
454
+ document_inverse_norms_f32x);
455
+ svfloat32_t angular_distance_f32x = svmax_f32_x(
456
+ row_predicate_b32x, svsub_f32_x(row_predicate_b32x, svdup_f32(1.0f), cosine_f32x), svdup_f32(0.0f));
457
+ total_angular_distance += nk_svaddv_f32_(row_predicate_b32x, angular_distance_f32x);
458
+ }
459
+
460
+ *result = total_angular_distance;
461
+ }
462
+
463
+ NK_PUBLIC void nk_maxsim_packed_bf16_sme( //
464
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
465
+ nk_size_t depth, nk_f32_t *result) {
466
+
467
+ nk_sme_start_streaming_();
468
+ nk_maxsim_packed_bf16_streaming_(query_packed, document_packed, query_count, document_count, depth, result);
469
+ nk_sme_stop_streaming_();
470
+ }
471
+
472
+ NK_PUBLIC nk_size_t nk_maxsim_packed_size_bf16_sme(nk_size_t columns, nk_size_t depth) { //
473
+ return nk_dots_packed_size_bf16_sme(columns, depth);
474
+ }
475
+
476
+ NK_PUBLIC nk_size_t nk_maxsim_packed_size_f16_sme(nk_size_t columns, nk_size_t depth) { //
477
+ return nk_dots_packed_size_f16_sme(columns, depth);
478
+ }
479
+
480
+ NK_PUBLIC void nk_maxsim_pack_bf16_sme( //
481
+ nk_bf16_t const *vectors, nk_size_t columns, nk_size_t depth, nk_size_t stride_in_bytes, void *packed) { //
482
+
483
+ // Delegate tile interleaving and squared norms computation to dots pack.
484
+ // Both headers are 64 bytes with identical layout for the first 6 fields.
485
+ nk_dots_pack_bf16_sme(vectors, columns, depth, stride_in_bytes, packed);
486
+
487
+ // Set maxsim-specific header fields (overlaps dots reserved area)
488
+ nk_maxsim_sme_packed_header_t *header = (nk_maxsim_sme_packed_header_t *)packed;
489
+ header->originals_offset = 0; // not used for bf16
490
+ header->original_stride = 0; // not used for bf16
491
+ for (nk_size_t i = 0; i < 8; i++) header->reserved[i] = 0;
492
+
493
+ // Convert squared norms → inverse norms in-place
494
+ nk_f32_t *norms = (nk_f32_t *)((char *)packed + header->norms_offset);
495
+ for (nk_size_t i = 0; i < columns; i++) {
496
+ nk_f32_t norm_sq = norms[i];
497
+ norms[i] = (norm_sq > 0.0f) ? (nk_f32_t)nk_f64_rsqrt_neon((nk_f64_t)norm_sq) : 0.0f;
498
+ }
499
+ }
500
+
501
+ NK_PUBLIC void nk_maxsim_pack_f16_sme( //
502
+ nk_f16_t const *vectors, nk_size_t columns, nk_size_t depth, nk_size_t stride_in_bytes, void *packed) { //
503
+
504
+ // Delegate tile interleaving and squared norms computation to dots pack.
505
+ // Both headers are 64 bytes with identical layout for the first 6 fields.
506
+ nk_dots_pack_f16_sme(vectors, columns, depth, stride_in_bytes, packed);
507
+
508
+ // Set maxsim-specific header fields (overlaps dots reserved area)
509
+ nk_maxsim_sme_packed_header_t *header = (nk_maxsim_sme_packed_header_t *)packed;
510
+ header->originals_offset = 0; // not used for f16
511
+ header->original_stride = 0; // not used for f16
512
+ for (nk_size_t i = 0; i < 8; i++) header->reserved[i] = 0;
513
+
514
+ // Convert squared norms → inverse norms in-place
515
+ nk_f32_t *norms = (nk_f32_t *)((char *)packed + header->norms_offset);
516
+ for (nk_size_t i = 0; i < columns; i++) {
517
+ nk_f32_t norm_sq = norms[i];
518
+ norms[i] = (norm_sq > 0.0f) ? (nk_f32_t)nk_f64_rsqrt_neon((nk_f64_t)norm_sq) : 0.0f;
519
+ }
520
+ }
521
+
522
+ /**
523
+ * MaxSim f32 kernel: i8 SMOPA screening + f32/f64 refinement + angular distance.
524
+ *
525
+ * Screening: i8 SMOPA has expansion=4, processing 4x more depth per instruction than f32 FMOPA.
526
+ * With 4 ZA tiles the fast path processes 64 document columns per iteration.
527
+ *
528
+ * Refinement: tile-wide interleaved f64 dot products for the winning (query, document) pairs.
529
+ * Angular distance: 1 - dot / sqrt(||q||^2 * ||d||^2), accumulated with f64.
530
+ */
531
+ NK_PUBLIC nk_size_t nk_maxsim_packed_size_f32_sme(nk_size_t columns, nk_size_t depth) { //
532
+ nk_size_t const expansion = 4; // i8->i32 SMOPA
533
+ nk_size_t const tile_dimension = nk_sme_cntw_(); // 16 for SVL=512
534
+ nk_size_t const vector_elements = nk_sme_cntb_(); // 64 for SVL=512
535
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
536
+ nk_size_t const depth_step_count = nk_size_divide_round_up_(depth, expansion);
537
+ nk_size_t const original_stride = nk_size_round_up_to_multiple_(depth * sizeof(nk_f32_t), 64);
538
+
539
+ nk_size_t size = sizeof(nk_maxsim_sme_packed_header_t); // 64 B header
540
+ size += column_tile_count * depth_step_count * vector_elements; // i8 tiles
541
+ size += columns * sizeof(nk_f32_t); // f32 squared norms
542
+ size += columns * original_stride; // f32 originals
543
+ return size;
544
+ }
545
+
546
+ NK_PUBLIC void nk_maxsim_pack_f32_sme( //
547
+ nk_f32_t const *vectors, nk_size_t columns, nk_size_t depth, nk_size_t stride_in_bytes, void *packed) { //
548
+
549
+ nk_size_t const expansion = 4; // i8->i32 SMOPA
550
+ nk_size_t const tile_dimension = nk_sme_cntw_(); // 16 for SVL=512
551
+ nk_size_t const vector_elements = nk_sme_cntb_(); // 64 for SVL=512
552
+ nk_size_t const stride_elements = stride_in_bytes / sizeof(nk_f32_t);
553
+
554
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(columns, tile_dimension);
555
+ nk_size_t const depth_step_count = nk_size_divide_round_up_(depth, expansion);
556
+ nk_size_t const total_vectors = column_tile_count * depth_step_count;
557
+ nk_size_t const original_stride = nk_size_round_up_to_multiple_(depth * sizeof(nk_f32_t), 64);
558
+
559
+ // Set up header
560
+ nk_maxsim_sme_packed_header_t *header = (nk_maxsim_sme_packed_header_t *)packed;
561
+ header->column_tile_count = (nk_u32_t)column_tile_count;
562
+ header->depth_tile_count = (nk_u32_t)depth_step_count;
563
+ header->columns = (nk_u32_t)columns;
564
+ header->depth = (nk_u32_t)depth;
565
+ header->svl_bytes = (nk_u32_t)(tile_dimension * sizeof(nk_f32_t));
566
+
567
+ nk_size_t const tiles_size = total_vectors * vector_elements;
568
+ nk_size_t const norms_offset = sizeof(nk_maxsim_sme_packed_header_t) + tiles_size;
569
+ nk_size_t const originals_offset = norms_offset + columns * sizeof(nk_f32_t);
570
+
571
+ header->norms_offset = (nk_u32_t)norms_offset;
572
+ header->originals_offset = (nk_u32_t)originals_offset;
573
+ header->original_stride = (nk_u32_t)original_stride;
574
+ for (nk_size_t i = 0; i < 8; i++) header->reserved[i] = 0;
575
+
576
+ nk_i8_t *tiles = (nk_i8_t *)((char *)packed + sizeof(nk_maxsim_sme_packed_header_t));
577
+ nk_f32_t *norms = (nk_f32_t *)((char *)packed + norms_offset);
578
+ char *originals = (char *)packed + originals_offset;
579
+
580
+ // Zero-initialize tile data (partial vectors stay zero-padded)
581
+ for (nk_size_t i = 0; i < tiles_size; i++) tiles[i] = 0;
582
+
583
+ // For each vector: quantize metadata, quantize+interleave into tiles, copy originals
584
+ for (nk_size_t vector_index = 0; vector_index < columns; vector_index++) {
585
+ nk_f32_t const *source = (nk_f32_t const *)((char const *)vectors + vector_index * stride_in_bytes);
586
+
587
+ // Pass 1: Compute absmax and norm_sq simultaneously
588
+ nk_f32_t absmax = 0.0f;
589
+ nk_f32_t norm_sq = 0.0f;
590
+ for (nk_size_t dim = 0; dim < depth; dim++) {
591
+ nk_f32_t val = source[dim];
592
+ nk_f32_t abs_val = nk_f32_abs_(val);
593
+ if (abs_val > absmax) absmax = abs_val;
594
+ norm_sq += val * val;
595
+ }
596
+ norms[vector_index] = norm_sq;
597
+
598
+ nk_f32_t scale = absmax / 127.0f;
599
+ if (scale == 0.0f) scale = 1.0f;
600
+
601
+ // Pass 2: Quantize and scatter into tile-interleaved positions
602
+ nk_size_t const column_tile = vector_index / tile_dimension;
603
+ nk_size_t const column_in_tile = vector_index % tile_dimension;
604
+
605
+ for (nk_size_t dim = 0; dim < depth; dim++) {
606
+ nk_size_t const depth_step = dim / expansion;
607
+ nk_size_t const sub_element = dim % expansion;
608
+ nk_size_t const vec_index = column_tile * depth_step_count + depth_step;
609
+ nk_size_t const offset = vec_index * vector_elements + expansion * column_in_tile + sub_element;
610
+
611
+ nk_f32_t scaled = source[dim] / scale;
612
+ nk_i32_t quantized;
613
+ if (scaled >= 0.0f) quantized = (nk_i32_t)(scaled + 0.5f);
614
+ else quantized = (nk_i32_t)(scaled - 0.5f);
615
+ if (quantized > 127) quantized = 127;
616
+ if (quantized < -127) quantized = -127;
617
+
618
+ tiles[offset] = (nk_i8_t)quantized;
619
+ }
620
+
621
+ // Pass 3: Copy originals (64B-aligned stride, zero-pad tail)
622
+ char *dest_original = originals + vector_index * original_stride;
623
+ nk_copy_bytes_(dest_original, source, depth * sizeof(nk_f32_t));
624
+ for (nk_size_t byte = depth * sizeof(nk_f32_t); byte < original_stride; byte++) dest_original[byte] = 0;
625
+ }
626
+ }
627
+
628
+ /**
629
+ * Streaming-compatible f32 dot product with f64 accumulation.
630
+ * Follows the svcntd()-stride + svcvt_f64_f32_x pattern from nk_dots_reduce_sumsq_f32_ssve_.
631
+ */
632
+ NK_PUBLIC nk_f64_t nk_maxsim_reduce_dot_f32_ssve_( //
633
+ nk_f32_t const *a, nk_f32_t const *b, nk_size_t count) NK_STREAMING_ { //
634
+ svfloat64_t accumulator_even_f64x = svdup_f64(0.0);
635
+ svfloat64_t accumulator_odd_f64x = svdup_f64(0.0);
636
+ nk_size_t const vector_length = svcntw();
637
+ nk_size_t const half_vector_length = svcntd();
638
+ for (nk_size_t i = 0; i < count; i += vector_length) {
639
+ svbool_t predicate_b32x = svwhilelt_b32_u64(i, count);
640
+ svfloat32_t a_f32x = svld1_f32(predicate_b32x, a + i);
641
+ svfloat32_t b_f32x = svld1_f32(predicate_b32x, b + i);
642
+
643
+ svbool_t predicate_even_b64x = svwhilelt_b64_u64(i, count);
644
+ svfloat64_t a_even_f64x = svcvt_f64_f32_x(predicate_even_b64x, a_f32x);
645
+ svfloat64_t b_even_f64x = svcvt_f64_f32_x(predicate_even_b64x, b_f32x);
646
+ accumulator_even_f64x = svmla_f64_m(predicate_even_b64x, accumulator_even_f64x, a_even_f64x, b_even_f64x);
647
+
648
+ svbool_t predicate_odd_b64x = svwhilelt_b64_u64(i + half_vector_length, count);
649
+ svfloat64_t a_odd_f64x = svcvtlt_f64_f32_x(predicate_odd_b64x, a_f32x);
650
+ svfloat64_t b_odd_f64x = svcvtlt_f64_f32_x(predicate_odd_b64x, b_f32x);
651
+ accumulator_odd_f64x = svmla_f64_m(predicate_odd_b64x, accumulator_odd_f64x, a_odd_f64x, b_odd_f64x);
652
+ }
653
+ return nk_svaddv_f64_(svptrue_b64(), accumulator_even_f64x) + nk_svaddv_f64_(svptrue_b64(), accumulator_odd_f64x);
654
+ }
655
+
656
+ /**
657
+ * Streaming-compatible angular distance accumulation from pre-reduced dot products
658
+ * and contiguous f64 norm arrays.
659
+ * Computes rsqrt via Newton-Raphson and accumulates `1 - dot / sqrt(||q||^2 * ||d||^2)`.
660
+ */
661
+ NK_PUBLIC nk_f64_t nk_maxsim_angular_from_dots_ssve_( //
662
+ nk_f64_t const *dot_products, nk_size_t count, //
663
+ nk_f64_t const *query_norms_f64, nk_f64_t const *document_norms_f64) NK_STREAMING_ { //
664
+
665
+ nk_f64_t total_angular_distance_f64 = 0.0;
666
+ nk_size_t const vector_length = svcntd();
667
+ for (nk_size_t i = 0; i < count; i += vector_length) {
668
+ svbool_t predicate_b64x = svwhilelt_b64_u64(i, count);
669
+ svfloat64_t dot_products_f64x = svld1_f64(predicate_b64x, dot_products + i);
670
+ svfloat64_t query_norms_f64x = svld1_f64(predicate_b64x, query_norms_f64 + i);
671
+ svfloat64_t document_norms_f64x = svld1_f64(predicate_b64x, document_norms_f64 + i);
672
+
673
+ // norm_product = query_norm * document_norm
674
+ svfloat64_t norm_products_f64x = svmul_f64_x(predicate_b64x, query_norms_f64x, document_norms_f64x);
675
+
676
+ // Newton-Raphson rsqrt: estimate then two refinement steps
677
+ svfloat64_t rsqrt_f64x = svrsqrte_f64(norm_products_f64x);
678
+ rsqrt_f64x = svmul_f64_x(predicate_b64x, rsqrt_f64x,
679
+ svrsqrts_f64(svmul_f64_x(predicate_b64x, norm_products_f64x, rsqrt_f64x), rsqrt_f64x));
680
+ rsqrt_f64x = svmul_f64_x(predicate_b64x, rsqrt_f64x,
681
+ svrsqrts_f64(svmul_f64_x(predicate_b64x, norm_products_f64x, rsqrt_f64x), rsqrt_f64x));
682
+
683
+ // cosine = dot_product * rsqrt(norm_product), zeroed where norm <= 0
684
+ svbool_t positive_b64x = svcmpgt_f64(predicate_b64x, norm_products_f64x, svdup_n_f64(0.0));
685
+ svfloat64_t cosine_f64x = svmul_f64_z(positive_b64x, dot_products_f64x, rsqrt_f64x);
686
+
687
+ // angular_distance = max(0, 1 - cosine)
688
+ svfloat64_t angular_distance_f64x = svsub_f64_x(predicate_b64x, svdup_f64(1.0), cosine_f64x);
689
+ angular_distance_f64x = svmax_f64_x(predicate_b64x, angular_distance_f64x, svdup_f64(0.0));
690
+
691
+ total_angular_distance_f64 += nk_svaddv_f64_(predicate_b64x, angular_distance_f64x);
692
+ }
693
+ return total_angular_distance_f64;
694
+ }
695
+
696
+ /**
697
+ * MaxSim f32 kernel: i8 SMOPA screening + f32/f64 refinement + angular distance.
698
+ *
699
+ * Screening: i8 SMOPA has expansion=4, processing 4x more depth per instruction than f32 FMOPA.
700
+ * With 4 ZA tiles the fast path processes 64 document columns per iteration.
701
+ *
702
+ * Refinement: tile-wide interleaved f64 dot products for the winning (query, document) pairs.
703
+ * Angular distance: 1 - dot / sqrt(||q||^2 * ||d||^2), accumulated with f64.
704
+ */
705
+ __arm_new("za") static void nk_maxsim_packed_f32_streaming_( //
706
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
707
+ nk_size_t depth, nk_f64_t *result) NK_STREAMING_ {
708
+
709
+ nk_maxsim_sme_packed_header_t const *query_header = (nk_maxsim_sme_packed_header_t const *)query_packed;
710
+ nk_maxsim_sme_packed_header_t const *document_header = (nk_maxsim_sme_packed_header_t const *)document_packed;
711
+
712
+ nk_size_t const depth_step_count = query_header->depth_tile_count;
713
+ nk_size_t const query_row_tiles = query_header->column_tile_count;
714
+ nk_size_t const document_col_tiles = document_header->column_tile_count;
715
+
716
+ nk_size_t const tile_dimension = svcntw(); // 16: ZA32 tile dimension
717
+ nk_size_t const vector_elements = svcntb(); // 64: i8 elements per SVE vector
718
+
719
+ // Tile data pointers (i8)
720
+ nk_i8_t const *query_tiles = (nk_i8_t const *)((char const *)query_packed + sizeof(nk_maxsim_sme_packed_header_t));
721
+ nk_i8_t const *document_tiles = (nk_i8_t const *)((char const *)document_packed +
722
+ sizeof(nk_maxsim_sme_packed_header_t));
723
+
724
+ // Norms and originals pointers
725
+ nk_f32_t const *query_norms = (nk_f32_t const *)((char const *)query_packed + query_header->norms_offset);
726
+ nk_f32_t const *document_norms = (nk_f32_t const *)((char const *)document_packed + document_header->norms_offset);
727
+ nk_f32_t const *query_originals = (nk_f32_t const *)((char const *)query_packed + query_header->originals_offset);
728
+ nk_f32_t const *document_originals = (nk_f32_t const *)((char const *)document_packed +
729
+ document_header->originals_offset);
730
+ nk_size_t const query_original_stride_elements = query_header->original_stride / sizeof(nk_f32_t);
731
+ nk_size_t const document_original_stride_elements = document_header->original_stride / sizeof(nk_f32_t);
732
+
733
+ nk_size_t const expansion = 4; // i8->i32 SMOPA
734
+
735
+ svbool_t const predicate_all_b8x = svptrue_b8();
736
+ svbool_t const predicate_all_b32x = svptrue_b32();
737
+
738
+ nk_f64_t total_angular_distance_f64 = 0.0;
739
+
740
+ for (nk_size_t row_tile_index = 0; row_tile_index < query_row_tiles; row_tile_index++) {
741
+ nk_size_t const row_start = row_tile_index * tile_dimension;
742
+ nk_size_t const rows_remaining = (row_start + tile_dimension <= query_count) ? tile_dimension
743
+ : (query_count - row_start);
744
+ svbool_t const row_predicate_b8x = (rows_remaining == tile_dimension)
745
+ ? svptrue_b8()
746
+ : svwhilelt_b8_u64(0u, rows_remaining * expansion);
747
+ svbool_t const row_predicate_b32x = (rows_remaining == tile_dimension) ? svptrue_b32()
748
+ : svwhilelt_b32_u64(0u, rows_remaining);
749
+
750
+ svint32_t running_max_i32x = svdup_s32(NK_I32_MIN);
751
+ svuint32_t running_argmax_u32x = svdup_u32(0);
752
+
753
+ nk_size_t column_tile_index = 0;
754
+
755
+ // 4-tile fast path: ZA0-ZA3 process 4 document column tiles simultaneously
756
+ for (; column_tile_index + 4 <= document_col_tiles; column_tile_index += 4) {
757
+ svzero_za();
758
+
759
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
760
+ svint8_t query_packed_i8x = svld1_s8(
761
+ row_predicate_b8x,
762
+ (nk_i8_t const *)(query_tiles +
763
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
764
+ svint8_t document_packed_0_i8x = svld1_s8(
765
+ predicate_all_b8x,
766
+ (nk_i8_t const *)(document_tiles +
767
+ ((column_tile_index + 0) * depth_step_count + depth_step) * vector_elements));
768
+ svint8_t document_packed_1_i8x = svld1_s8(
769
+ predicate_all_b8x,
770
+ (nk_i8_t const *)(document_tiles +
771
+ ((column_tile_index + 1) * depth_step_count + depth_step) * vector_elements));
772
+ svint8_t document_packed_2_i8x = svld1_s8(
773
+ predicate_all_b8x,
774
+ (nk_i8_t const *)(document_tiles +
775
+ ((column_tile_index + 2) * depth_step_count + depth_step) * vector_elements));
776
+ svint8_t document_packed_3_i8x = svld1_s8(
777
+ predicate_all_b8x,
778
+ (nk_i8_t const *)(document_tiles +
779
+ ((column_tile_index + 3) * depth_step_count + depth_step) * vector_elements));
780
+ svmopa_za32_s8_m(0, row_predicate_b8x, predicate_all_b8x, query_packed_i8x, document_packed_0_i8x);
781
+ svmopa_za32_s8_m(1, row_predicate_b8x, predicate_all_b8x, query_packed_i8x, document_packed_1_i8x);
782
+ svmopa_za32_s8_m(2, row_predicate_b8x, predicate_all_b8x, query_packed_i8x, document_packed_2_i8x);
783
+ svmopa_za32_s8_m(3, row_predicate_b8x, predicate_all_b8x, query_packed_i8x, document_packed_3_i8x);
784
+ }
785
+
786
+ // Vertical column extraction + argmax update (manually unrolled over 4 tiles)
787
+ for (nk_size_t column_within_tile = 0; column_within_tile < tile_dimension; column_within_tile++) {
788
+ // Tile 0
789
+ {
790
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 0) * tile_dimension + column_within_tile);
791
+ svint32_t column_dots_i32x = svread_ver_za32_s32_m(svdup_s32(NK_I32_MIN), predicate_all_b32x, 0,
792
+ column_within_tile);
793
+ svbool_t is_better_bx = svcmpgt_s32(predicate_all_b32x, column_dots_i32x, running_max_i32x);
794
+ running_max_i32x = svsel_s32(is_better_bx, column_dots_i32x, running_max_i32x);
795
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
796
+ }
797
+ // Tile 1
798
+ {
799
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 1) * tile_dimension + column_within_tile);
800
+ svint32_t column_dots_i32x = svread_ver_za32_s32_m(svdup_s32(NK_I32_MIN), predicate_all_b32x, 1,
801
+ column_within_tile);
802
+ svbool_t is_better_bx = svcmpgt_s32(predicate_all_b32x, column_dots_i32x, running_max_i32x);
803
+ running_max_i32x = svsel_s32(is_better_bx, column_dots_i32x, running_max_i32x);
804
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
805
+ }
806
+ // Tile 2
807
+ {
808
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 2) * tile_dimension + column_within_tile);
809
+ svint32_t column_dots_i32x = svread_ver_za32_s32_m(svdup_s32(NK_I32_MIN), predicate_all_b32x, 2,
810
+ column_within_tile);
811
+ svbool_t is_better_bx = svcmpgt_s32(predicate_all_b32x, column_dots_i32x, running_max_i32x);
812
+ running_max_i32x = svsel_s32(is_better_bx, column_dots_i32x, running_max_i32x);
813
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
814
+ }
815
+ // Tile 3
816
+ {
817
+ nk_u32_t document_index = (nk_u32_t)((column_tile_index + 3) * tile_dimension + column_within_tile);
818
+ svint32_t column_dots_i32x = svread_ver_za32_s32_m(svdup_s32(NK_I32_MIN), predicate_all_b32x, 3,
819
+ column_within_tile);
820
+ svbool_t is_better_bx = svcmpgt_s32(predicate_all_b32x, column_dots_i32x, running_max_i32x);
821
+ running_max_i32x = svsel_s32(is_better_bx, column_dots_i32x, running_max_i32x);
822
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
823
+ }
824
+ }
825
+ }
826
+
827
+ // 1-tile remainder: ZA0 only
828
+ for (; column_tile_index < document_col_tiles; column_tile_index++) {
829
+ nk_size_t const col_start = column_tile_index * tile_dimension;
830
+ nk_size_t const cols_remaining = (col_start + tile_dimension <= document_count)
831
+ ? tile_dimension
832
+ : (document_count - col_start);
833
+ svbool_t const column_predicate_b8x = (cols_remaining == tile_dimension)
834
+ ? svptrue_b8()
835
+ : svwhilelt_b8_u64(0u, cols_remaining * expansion);
836
+
837
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
838
+
839
+ for (nk_size_t depth_step = 0; depth_step < depth_step_count; depth_step++) {
840
+ svint8_t query_packed_i8x = svld1_s8(
841
+ row_predicate_b8x,
842
+ (nk_i8_t const *)(query_tiles +
843
+ (row_tile_index * depth_step_count + depth_step) * vector_elements));
844
+ svint8_t document_packed_i8x = svld1_s8(
845
+ column_predicate_b8x,
846
+ (nk_i8_t const *)(document_tiles +
847
+ (column_tile_index * depth_step_count + depth_step) * vector_elements));
848
+ svmopa_za32_s8_m(0, row_predicate_b8x, column_predicate_b8x, query_packed_i8x, document_packed_i8x);
849
+ }
850
+
851
+ for (nk_size_t column_within_tile = 0; column_within_tile < cols_remaining; column_within_tile++) {
852
+ nk_u32_t document_index = (nk_u32_t)(col_start + column_within_tile);
853
+ svint32_t column_dots_i32x = svread_ver_za32_s32_m(svdup_s32(NK_I32_MIN), predicate_all_b32x, 0,
854
+ column_within_tile);
855
+ svbool_t is_better_bx = svcmpgt_s32(predicate_all_b32x, column_dots_i32x, running_max_i32x);
856
+ running_max_i32x = svsel_s32(is_better_bx, column_dots_i32x, running_max_i32x);
857
+ running_argmax_u32x = svsel_u32(is_better_bx, svdup_u32(document_index), running_argmax_u32x);
858
+ }
859
+ }
860
+
861
+ // Refinement: tile-wide interleaved f64 dot products
862
+ nk_u32_t best_document_indices[64]; // max tile_dimension across all SVL values
863
+ svst1_u32(row_predicate_b32x, best_document_indices, running_argmax_u32x);
864
+
865
+ // Pointer setup: one (query, document) pair per row in the tile
866
+ nk_f32_t const *query_original_ptrs[64];
867
+ nk_f32_t const *document_original_ptrs[64];
868
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_remaining; row_in_tile++) {
869
+ nk_size_t query_index = row_start + row_in_tile;
870
+ nk_u32_t best_document_index = best_document_indices[row_in_tile];
871
+ query_original_ptrs[row_in_tile] = query_originals + query_index * query_original_stride_elements;
872
+ document_original_ptrs[row_in_tile] = document_originals +
873
+ best_document_index * document_original_stride_elements;
874
+ }
875
+
876
+ // Interleaved f64 dot products in batches of 4 (hides MLA 4-cycle latency)
877
+ nk_size_t row_batch_start = 0;
878
+
879
+ // Fast path: 4-wide batches
880
+ for (; row_batch_start + 4 <= rows_remaining; row_batch_start += 4) {
881
+ svfloat64_t accumulator_0_f64x = svdup_f64(0.0);
882
+ svfloat64_t accumulator_1_f64x = svdup_f64(0.0);
883
+ svfloat64_t accumulator_2_f64x = svdup_f64(0.0);
884
+ svfloat64_t accumulator_3_f64x = svdup_f64(0.0);
885
+ nk_size_t const depth_vector_length = svcntw();
886
+ nk_size_t const depth_half_length = svcntd();
887
+
888
+ for (nk_size_t depth_index = 0; depth_index < depth; depth_index += depth_vector_length) {
889
+ svbool_t predicate_depth_b32x = svwhilelt_b32_u64(depth_index, depth);
890
+ svbool_t predicate_even_b64x = svwhilelt_b64_u64(depth_index, depth);
891
+ svbool_t predicate_odd_b64x = svwhilelt_b64_u64(depth_index + depth_half_length, depth);
892
+
893
+ svfloat32_t query_values_0_f32x = svld1_f32(predicate_depth_b32x,
894
+ query_original_ptrs[row_batch_start + 0] + depth_index);
895
+ svfloat32_t document_values_0_f32x = svld1_f32(
896
+ predicate_depth_b32x, document_original_ptrs[row_batch_start + 0] + depth_index);
897
+ accumulator_0_f64x = svmla_f64_m(predicate_even_b64x, accumulator_0_f64x,
898
+ svcvt_f64_f32_x(predicate_even_b64x, query_values_0_f32x),
899
+ svcvt_f64_f32_x(predicate_even_b64x, document_values_0_f32x));
900
+ accumulator_0_f64x = svmla_f64_m(predicate_odd_b64x, accumulator_0_f64x,
901
+ svcvtlt_f64_f32_x(predicate_odd_b64x, query_values_0_f32x),
902
+ svcvtlt_f64_f32_x(predicate_odd_b64x, document_values_0_f32x));
903
+
904
+ svfloat32_t query_values_1_f32x = svld1_f32(predicate_depth_b32x,
905
+ query_original_ptrs[row_batch_start + 1] + depth_index);
906
+ svfloat32_t document_values_1_f32x = svld1_f32(
907
+ predicate_depth_b32x, document_original_ptrs[row_batch_start + 1] + depth_index);
908
+ accumulator_1_f64x = svmla_f64_m(predicate_even_b64x, accumulator_1_f64x,
909
+ svcvt_f64_f32_x(predicate_even_b64x, query_values_1_f32x),
910
+ svcvt_f64_f32_x(predicate_even_b64x, document_values_1_f32x));
911
+ accumulator_1_f64x = svmla_f64_m(predicate_odd_b64x, accumulator_1_f64x,
912
+ svcvtlt_f64_f32_x(predicate_odd_b64x, query_values_1_f32x),
913
+ svcvtlt_f64_f32_x(predicate_odd_b64x, document_values_1_f32x));
914
+
915
+ svfloat32_t query_values_2_f32x = svld1_f32(predicate_depth_b32x,
916
+ query_original_ptrs[row_batch_start + 2] + depth_index);
917
+ svfloat32_t document_values_2_f32x = svld1_f32(
918
+ predicate_depth_b32x, document_original_ptrs[row_batch_start + 2] + depth_index);
919
+ accumulator_2_f64x = svmla_f64_m(predicate_even_b64x, accumulator_2_f64x,
920
+ svcvt_f64_f32_x(predicate_even_b64x, query_values_2_f32x),
921
+ svcvt_f64_f32_x(predicate_even_b64x, document_values_2_f32x));
922
+ accumulator_2_f64x = svmla_f64_m(predicate_odd_b64x, accumulator_2_f64x,
923
+ svcvtlt_f64_f32_x(predicate_odd_b64x, query_values_2_f32x),
924
+ svcvtlt_f64_f32_x(predicate_odd_b64x, document_values_2_f32x));
925
+
926
+ svfloat32_t query_values_3_f32x = svld1_f32(predicate_depth_b32x,
927
+ query_original_ptrs[row_batch_start + 3] + depth_index);
928
+ svfloat32_t document_values_3_f32x = svld1_f32(
929
+ predicate_depth_b32x, document_original_ptrs[row_batch_start + 3] + depth_index);
930
+ accumulator_3_f64x = svmla_f64_m(predicate_even_b64x, accumulator_3_f64x,
931
+ svcvt_f64_f32_x(predicate_even_b64x, query_values_3_f32x),
932
+ svcvt_f64_f32_x(predicate_even_b64x, document_values_3_f32x));
933
+ accumulator_3_f64x = svmla_f64_m(predicate_odd_b64x, accumulator_3_f64x,
934
+ svcvtlt_f64_f32_x(predicate_odd_b64x, query_values_3_f32x),
935
+ svcvtlt_f64_f32_x(predicate_odd_b64x, document_values_3_f32x));
936
+ }
937
+
938
+ // Reduce SVE accumulators to scalars and compute angular distances
939
+ nk_f64_t dot_products_f64[4];
940
+ dot_products_f64[0] = nk_svaddv_f64_(svptrue_b64(), accumulator_0_f64x);
941
+ dot_products_f64[1] = nk_svaddv_f64_(svptrue_b64(), accumulator_1_f64x);
942
+ dot_products_f64[2] = nk_svaddv_f64_(svptrue_b64(), accumulator_2_f64x);
943
+ dot_products_f64[3] = nk_svaddv_f64_(svptrue_b64(), accumulator_3_f64x);
944
+ nk_f64_t batch_query_norms_f64[4], batch_document_norms_f64[4];
945
+ for (nk_size_t i = 0; i < 4; i++) {
946
+ batch_query_norms_f64[i] = (nk_f64_t)query_norms[row_start + row_batch_start + i];
947
+ batch_document_norms_f64[i] = (nk_f64_t)document_norms[best_document_indices[row_batch_start + i]];
948
+ }
949
+ total_angular_distance_f64 += nk_maxsim_angular_from_dots_ssve_(dot_products_f64, 4, batch_query_norms_f64,
950
+ batch_document_norms_f64);
951
+ }
952
+
953
+ // Remainder: compute dot products then batch the angular distance
954
+ nk_size_t remainder_count = rows_remaining - row_batch_start;
955
+ if (remainder_count > 0) {
956
+ nk_f64_t remainder_dot_products_f64[3];
957
+ nk_f64_t remainder_query_norms_f64[3], remainder_document_norms_f64[3];
958
+ for (nk_size_t i = 0; i < remainder_count; i++) {
959
+ remainder_dot_products_f64[i] = nk_maxsim_reduce_dot_f32_ssve_(
960
+ query_original_ptrs[row_batch_start + i], document_original_ptrs[row_batch_start + i], depth);
961
+ remainder_query_norms_f64[i] = (nk_f64_t)query_norms[row_start + row_batch_start + i];
962
+ remainder_document_norms_f64[i] = (nk_f64_t)document_norms[best_document_indices[row_batch_start + i]];
963
+ }
964
+ total_angular_distance_f64 += nk_maxsim_angular_from_dots_ssve_(
965
+ remainder_dot_products_f64, remainder_count, remainder_query_norms_f64, remainder_document_norms_f64);
966
+ }
967
+ }
968
+
969
+ *result = total_angular_distance_f64;
970
+ }
971
+
972
+ NK_PUBLIC void nk_maxsim_packed_f32_sme( //
973
+ void const *query_packed, void const *document_packed, nk_size_t query_count, nk_size_t document_count,
974
+ nk_size_t depth, nk_f64_t *result) {
975
+
976
+ nk_sme_start_streaming_();
977
+ nk_maxsim_packed_f32_streaming_(query_packed, document_packed, query_count, document_count, depth, result);
978
+ nk_sme_stop_streaming_();
979
+ }
980
+
981
+ #if defined(__clang__)
982
+ #pragma clang attribute pop
983
+ #elif defined(__GNUC__)
984
+ #pragma GCC pop_options
985
+ #endif
986
+
987
+ #if defined(__cplusplus)
988
+ } // extern "C"
989
+ #endif
990
+
991
+ #endif // NK_TARGET_SME
992
+ #endif // NK_TARGET_ARM64_
993
+ #endif // NK_MAXSIM_SME_H