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,1135 @@
1
+ /**
2
+ * @brief SIMD-accelerated Batched Set Distances for SME.
3
+ * @file include/numkong/sets/smebi32.h
4
+ * @author Ash Vardanian
5
+ * @date February 6, 2026
6
+ * @sa include/numkong/sets.h
7
+ *
8
+ * Uses ARM Scalable Matrix Extension (SME) for efficient binary set operations.
9
+ * Leverages streaming mode's wider vectors (512-bit on Apple M4) for fast
10
+ * XOR+POPCNT operations on binary vectors.
11
+ *
12
+ * @section smebi32_math Mathematical Foundation
13
+ *
14
+ * Hamming distance: popcount(a XOR b) = number of differing bits
15
+ *
16
+ * Jaccard distance using intersection:
17
+ * intersection = popcount(a AND b)
18
+ * union = popcount(a) + popcount(b) - intersection
19
+ * jaccard = 1 - intersection / union
20
+ *
21
+ * @section smebi32_tiles SME Dimensions (512-bit SVL)
22
+ *
23
+ * - svcntw(): 16 (number of 32-bit elements per vector)
24
+ * - svcntb(): 64 (number of bytes per SVE vector)
25
+ * - Tile blocking: 16x16 output tiles for cache efficiency
26
+ * - Depth processing: 64 bytes (512 bits) per iteration
27
+ *
28
+ * @section smebi32_perf Performance Characteristics (Apple M4)
29
+ *
30
+ * - SVL: 512 bits (64 bytes)
31
+ * - Streaming mode provides dedicated register file
32
+ * - Streaming mode overhead: ~50-100 cycles for SMSTART/SMSTOP
33
+ */
34
+
35
+ #ifndef NK_SETS_SMEBI32_H
36
+ #define NK_SETS_SMEBI32_H
37
+
38
+ #if NK_TARGET_ARM64_
39
+ #if NK_TARGET_SMEBI32
40
+
41
+ #include "numkong/types.h"
42
+ #include "numkong/set/serial.h"
43
+ #include "numkong/sets/serial.h"
44
+ #include "numkong/reduce/sve.h" // `nk_svaddv_f64_`
45
+ #include "numkong/reduce/neon.h" // `nk_reduce_moments_u1_neon`
46
+ #include "numkong/dots/sme.h" // `nk_sme_zero_za32_*`
47
+
48
+ #if defined(__cplusplus)
49
+ extern "C" {
50
+ #endif
51
+
52
+ /*
53
+ * Binary set operations using SME BMOPA instruction.
54
+ *
55
+ * BMOPA computes: ZA[i,j] += popcount(~(Zn[i] ^ Zm[j])) = popcount(XNOR)
56
+ * This counts matching bits. Hamming = depth_bits - matching.
57
+ *
58
+ * Tile layout (SVL=512, Apple M4):
59
+ * - ZA32 output tile: 16 × 16 u32 elements (1 KB)
60
+ * - Input vectors: 16 u32 elements (SVL/32)
61
+ * - Each BMOPA processes 32 bits (one u32) across 16×16 pairs
62
+ * - BMOPA predicates: b32 (u32 input granularity)
63
+ * - Packed kernel: 4-tile path (ZA0-ZA3) for 4 B-column tiles simultaneously
64
+ * - Unpacked kernel: ZA transpose (ZA0.S=staging, ZA1-3.S=accumulation, 3-tile fast path)
65
+ * - Packed format: column-major u32 within each tile
66
+ */
67
+
68
+ #if defined(__clang__)
69
+ #pragma clang attribute push(__attribute__((target("sme2"))), apply_to = function)
70
+ #elif defined(__GNUC__)
71
+ #pragma GCC push_options
72
+ #pragma GCC target("+sme2")
73
+ #endif
74
+
75
+ /* Read SVL in bytes from non-streaming context using RDSVL instruction. */
76
+ NK_INTERNAL nk_size_t nk_smebi32_svl_bytes_(void) {
77
+ nk_size_t svl_bytes;
78
+ __asm__ volatile("rdsvl %0, #1" : "=r"(svl_bytes));
79
+ return svl_bytes;
80
+ }
81
+
82
+ /* Get ZA32 tile dimension (number of f32/u32 elements per row). */
83
+ NK_INTERNAL nk_size_t nk_smebi32_tile_dim_(void) { return nk_smebi32_svl_bytes_() / sizeof(nk_u32_t); }
84
+
85
+ typedef struct {
86
+ nk_u32_t row_tile_count; // ceiling(rows / tile_dim)
87
+ nk_u32_t depth_tile_count; // ceiling(depth_bits / depth_tile_bits)
88
+ nk_u32_t rows; // actual row count
89
+ nk_u32_t depth_bits; // actual depth in bits
90
+ nk_u32_t svl_bytes; // SVL at pack time for validation
91
+ nk_u32_t norms_offset; // byte offset to norms (0 if none)
92
+ nk_u32_t reserved[10]; // padding to 64 bytes
93
+ } nk_sets_smebi32_packed_header_t;
94
+
95
+ /** Count total set bits across a byte vector using streaming SVE.
96
+ * Accumulates per-byte popcounts into u32 lanes via svdot; single horizontal reduction at end. */
97
+ NK_PUBLIC nk_u32_t nk_sets_reduce_sumsq_u1_streaming_(nk_u1x8_t const *data, nk_size_t n_bytes) NK_STREAMING_ {
98
+ svuint32_t acc_u32x = svdup_u32(0);
99
+ svuint8_t const ones_u8x = svdup_u8(1);
100
+ for (nk_size_t offset = 0; offset < n_bytes; offset += svcntb()) {
101
+ svbool_t predicate_b8x = svwhilelt_b8_u64(offset, n_bytes);
102
+ acc_u32x = svdot_u32(acc_u32x, svcnt_u8_z(predicate_b8x, svld1_u8(predicate_b8x, data + offset)), ones_u8x);
103
+ }
104
+ return (nk_u32_t)nk_svaddv_u32_(svptrue_b32(), acc_u32x);
105
+ }
106
+
107
+ #pragma region Hamming Distance
108
+
109
+ NK_PUBLIC nk_size_t nk_dots_packed_size_u1_smebi32(nk_size_t row_count, nk_size_t depth_bits) {
110
+ nk_size_t const tile_dim = nk_smebi32_tile_dim_(); // 16 rows per tile
111
+ nk_size_t const depth_tile_size = nk_smebi32_tile_dim_(); // 16 u32 per depth tile = 512 bits
112
+
113
+ nk_size_t const depth_u32 = nk_size_divide_round_up_(depth_bits, 32);
114
+ nk_size_t const row_tile_count = nk_size_divide_round_up_(row_count, tile_dim);
115
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth_u32, depth_tile_size);
116
+
117
+ nk_size_t const tile_elements = tile_dim * depth_tile_size; // 256 u32 per tile
118
+ nk_size_t size = sizeof(nk_sets_smebi32_packed_header_t);
119
+ size += row_tile_count * depth_tile_count * tile_elements * sizeof(nk_u32_t);
120
+ size += row_count * sizeof(nk_u32_t); // per-row population counts
121
+
122
+ return size;
123
+ }
124
+
125
+ NK_PUBLIC void nk_dots_pack_u1_smebi32(nk_u1x8_t const *b, nk_size_t row_count, nk_size_t depth_bits,
126
+ nk_size_t b_stride_in_bytes, void *b_packed) {
127
+ nk_size_t const svl_bytes = nk_smebi32_svl_bytes_();
128
+ nk_size_t const tile_dim = nk_smebi32_tile_dim_(); // 16 rows per tile
129
+ nk_size_t const depth_tile_size = nk_smebi32_tile_dim_(); // 16 u32 per depth tile
130
+ nk_size_t const tile_elements = tile_dim * depth_tile_size;
131
+ nk_size_t const depth_bytes = depth_bits / 8;
132
+
133
+ // BMOPA processes binary data in 32-bit words: each svbmopa_za32_u32_m step
134
+ // handles one u32 (32 bits) across all row×column pairs simultaneously.
135
+ nk_size_t const depth_words = nk_size_divide_round_up_(depth_bits, 32);
136
+ nk_size_t const row_tile_count = nk_size_divide_round_up_(row_count, tile_dim);
137
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth_words, depth_tile_size);
138
+ nk_size_t const total_tiles = row_tile_count * depth_tile_count;
139
+ nk_size_t const data_size = total_tiles * tile_elements * sizeof(nk_u32_t);
140
+
141
+ nk_sets_smebi32_packed_header_t *header = (nk_sets_smebi32_packed_header_t *)b_packed;
142
+ header->row_tile_count = (nk_u32_t)row_tile_count;
143
+ header->depth_tile_count = (nk_u32_t)depth_tile_count;
144
+ header->rows = (nk_u32_t)row_count;
145
+ header->depth_bits = (nk_u32_t)depth_bits;
146
+ header->svl_bytes = (nk_u32_t)svl_bytes;
147
+ header->norms_offset = (nk_u32_t)(sizeof(nk_sets_smebi32_packed_header_t) + data_size);
148
+
149
+ nk_u32_t *tiles_ptr = (nk_u32_t *)((char *)b_packed + sizeof(nk_sets_smebi32_packed_header_t));
150
+ nk_u32_t *norms_ptr = (nk_u32_t *)((char *)b_packed + header->norms_offset);
151
+
152
+ // Zero-initialize all tiles (partial tiles stay zero-padded for predicated loads)
153
+ for (nk_size_t i = 0; i < total_tiles * tile_elements; i++) tiles_ptr[i] = 0;
154
+
155
+ // Pack tiles: column-major u32 within each tile for efficient SVE loads
156
+ for (nk_size_t row_tile = 0; row_tile < row_tile_count; row_tile++) {
157
+ for (nk_size_t depth_tile = 0; depth_tile < depth_tile_count; depth_tile++) {
158
+ nk_size_t const tile_index = row_tile * depth_tile_count + depth_tile;
159
+ nk_u32_t *tile_output = tiles_ptr + tile_index * tile_elements;
160
+
161
+ nk_size_t const src_row_start = row_tile * tile_dim;
162
+ nk_size_t const src_u32_start = depth_tile * depth_tile_size;
163
+ nk_size_t const rows_to_pack = (src_row_start + tile_dim <= row_count) ? tile_dim
164
+ : (row_count - src_row_start);
165
+ nk_size_t const u32s_to_pack = (src_u32_start + depth_tile_size <= depth_words)
166
+ ? depth_tile_size
167
+ : (depth_words > src_u32_start ? depth_words - src_u32_start : 0);
168
+
169
+ // Column-major packing: tile_output[col * tile_dim + row]
170
+ // Copy byte-by-byte for the last u32 to avoid garbage bits when depth_bits % 32 != 0
171
+ nk_size_t const tail_bytes = depth_bytes % 4;
172
+ nk_size_t const last_col = u32s_to_pack > 0 ? u32s_to_pack - 1 : 0;
173
+ nk_size_t const is_last_depth_tile = (src_u32_start + u32s_to_pack >= depth_words);
174
+ for (nk_size_t row = 0; row < rows_to_pack; row++) {
175
+ nk_u32_t const *src_row = (nk_u32_t const *)((char const *)b +
176
+ (src_row_start + row) * b_stride_in_bytes);
177
+ for (nk_size_t col = 0; col < u32s_to_pack; col++) {
178
+ nk_size_t const dst_idx = col * tile_dim + row; // Column-major!
179
+ if (tail_bytes && is_last_depth_tile && col == last_col) {
180
+ nk_copy_bytes_(&tile_output[dst_idx], &src_row[src_u32_start + col], tail_bytes);
181
+ }
182
+ else { tile_output[dst_idx] = src_row[src_u32_start + col]; }
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ // Compute per-row population counts
189
+ for (nk_size_t row = 0; row < row_count; row++) {
190
+ nk_u1x8_t const *src_row = (nk_u1x8_t const *)((char const *)b + row * b_stride_in_bytes);
191
+ nk_u64_t nk_local_sum_, nk_local_sumsq_;
192
+ nk_reduce_moments_u1_neon(src_row, depth_bytes * 8, sizeof(nk_u1x8_t), &nk_local_sum_, &nk_local_sumsq_);
193
+ norms_ptr[row] = (nk_u32_t)nk_local_sum_;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * SME Hamming kernel using ZA transpose for unpacked A.
199
+ * ZA0.S = staging (A rows loaded horizontally, read vertically for BMOPA).
200
+ * ZA1-3.S = BMOPA accumulation (3 B column tiles in fast path).
201
+ *
202
+ * Each ZA0.S batch covers 16 depth u32 steps (one full depth tile).
203
+ * BMOPA expansion=1 for u32: each u32 contributes 32 bits via XNOR+POPCNT.
204
+ */
205
+ __arm_new("za") static void nk_hammings_packed_u1_smebi32_streaming_( //
206
+ nk_u1x8_t const *a, void const *b_packed, nk_u32_t *c, nk_size_t row_count_a, nk_size_t row_count_b,
207
+ nk_size_t depth_bits, nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) NK_STREAMING_ {
208
+
209
+ nk_sets_smebi32_packed_header_t const *header = (nk_sets_smebi32_packed_header_t const *)b_packed;
210
+ nk_size_t const row_tile_count_b = header->row_tile_count;
211
+ nk_size_t const depth_tile_count = header->depth_tile_count;
212
+
213
+ nk_size_t const tile_dim = svcntw(); // 16 for 512-bit SVL
214
+ nk_size_t const depth_tile_size = svcntw(); // 16 u32 per depth tile
215
+ nk_size_t const tile_elements = tile_dim * depth_tile_size;
216
+ // BMOPA processes binary data in 32-bit words: each svbmopa_za32_u32_m step
217
+ // handles one u32 (32 bits) across all row×column pairs simultaneously.
218
+ nk_size_t const depth_words = nk_size_divide_round_up_(depth_bits, 32);
219
+ nk_size_t const depth_bytes = depth_bits / 8;
220
+
221
+ nk_u32_t const *b_tiles = (nk_u32_t const *)((char const *)b_packed + sizeof(nk_sets_smebi32_packed_header_t));
222
+
223
+ svbool_t const predicate_all_b32x = svptrue_b32();
224
+ // Use padded depth (depth_words * 32) for BMOPA: zero-padded bits always match in XNOR,
225
+ // so the effective depth for the matching→hamming conversion is the rounded-up bit count.
226
+ svuint32_t const depth_u32x = svdup_u32((nk_u32_t)(depth_words * 32));
227
+ nk_size_t const row_tile_count_a = nk_size_divide_round_up_(row_count_a, tile_dim);
228
+
229
+ for (nk_size_t row_tile_a = 0; row_tile_a < row_tile_count_a; row_tile_a++) {
230
+ nk_size_t const row_start_a = row_tile_a * tile_dim;
231
+ nk_size_t const rows_a_remaining = (row_start_a + tile_dim <= row_count_a) ? tile_dim
232
+ : (row_count_a - row_start_a);
233
+ svbool_t const row_predicate_b32x = svwhilelt_b32_u64(0u, rows_a_remaining);
234
+
235
+ // Fast path: 3 B column tiles using ZA1-ZA3 (ZA0.S = staging)
236
+ nk_size_t row_tile_b = 0;
237
+ for (; row_tile_b + 3 <= row_tile_count_b; row_tile_b += 3) {
238
+ svzero_mask_za(nk_sme_zero_za32_tiles_123_);
239
+
240
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
241
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
242
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
243
+ ? depth_tile_size
244
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
245
+ if (u32s_this_tile == 0) break;
246
+
247
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
248
+
249
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
250
+
251
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
252
+
253
+ // Load A rows into ZA0.S, byte-predicated to zero garbage bits
254
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_a_remaining; row_in_tile++) {
255
+ nk_u8_t const *a_row = (nk_u8_t const *)a + (row_start_a + row_in_tile) * a_stride_in_bytes +
256
+ d_start_u32 * 4;
257
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
258
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
259
+ }
260
+
261
+ // B tile pointers for 3 column tiles
262
+ nk_u32_t const *b_tile0 = b_tiles + ((row_tile_b + 0) * depth_tile_count + d_tile) * tile_elements;
263
+ nk_u32_t const *b_tile1 = b_tiles + ((row_tile_b + 1) * depth_tile_count + d_tile) * tile_elements;
264
+ nk_u32_t const *b_tile2 = b_tiles + ((row_tile_b + 2) * depth_tile_count + d_tile) * tile_elements;
265
+
266
+ // Vertical read + BMOPA for each depth step
267
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
268
+ svuint32_t a_column_u32x = svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, step);
269
+
270
+ svbmopa_za32_u32_m(1, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
271
+ svld1_u32(predicate_all_b32x, b_tile0 + step * tile_dim));
272
+ svbmopa_za32_u32_m(2, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
273
+ svld1_u32(predicate_all_b32x, b_tile1 + step * tile_dim));
274
+ svbmopa_za32_u32_m(3, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
275
+ svld1_u32(predicate_all_b32x, b_tile2 + step * tile_dim));
276
+ }
277
+ }
278
+
279
+ // Extract from ZA1-3: Hamming = depth_bits - matching_bits
280
+ for (nk_size_t row = 0; row < rows_a_remaining; row++) {
281
+ nk_u32_t *c_row = (nk_u32_t *)((char *)c + (row_start_a + row) * c_stride_in_bytes);
282
+
283
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
284
+ svuint32_t za2_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 2, row);
285
+ svuint32_t za3_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 3, row);
286
+
287
+ svst1_u32(predicate_all_b32x, c_row + (row_tile_b + 0) * tile_dim,
288
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za1_u32x));
289
+ svst1_u32(predicate_all_b32x, c_row + (row_tile_b + 1) * tile_dim,
290
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za2_u32x));
291
+ svst1_u32(predicate_all_b32x, c_row + (row_tile_b + 2) * tile_dim,
292
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za3_u32x));
293
+ }
294
+ }
295
+
296
+ // Remainder: 1 B column tile at a time using ZA1
297
+ for (; row_tile_b < row_tile_count_b; row_tile_b++) {
298
+ nk_size_t const row_start_b = row_tile_b * tile_dim;
299
+ nk_size_t const rows_b_remaining = (row_start_b + tile_dim <= row_count_b) ? tile_dim
300
+ : (row_count_b - row_start_b);
301
+ svbool_t const column_predicate_b32x = svwhilelt_b32_u64(0u, rows_b_remaining);
302
+
303
+ svzero_mask_za(nk_sme_zero_za32_tile_1_);
304
+
305
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
306
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
307
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
308
+ ? depth_tile_size
309
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
310
+ if (u32s_this_tile == 0) break;
311
+
312
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
313
+
314
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
315
+
316
+ // Load A rows into ZA0.S horizontally
317
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
318
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_a_remaining; row_in_tile++) {
319
+ nk_u8_t const *a_row = (nk_u8_t const *)a + (row_start_a + row_in_tile) * a_stride_in_bytes +
320
+ d_start_u32 * 4;
321
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
322
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
323
+ }
324
+
325
+ nk_u32_t const *b_tile = b_tiles + (row_tile_b * depth_tile_count + d_tile) * tile_elements;
326
+
327
+ // Vertical read + BMOPA
328
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
329
+ svuint32_t a_column_u32x = svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, step);
330
+ svuint32_t b_u32x = svld1_u32(predicate_all_b32x, b_tile + step * tile_dim);
331
+ svbmopa_za32_u32_m(1, row_predicate_b32x, column_predicate_b32x, a_column_u32x, b_u32x);
332
+ }
333
+ }
334
+
335
+ // Extract from ZA1: Hamming = depth_bits - matching_bits
336
+ for (nk_size_t row = 0; row < rows_a_remaining; row++) {
337
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
338
+ svuint32_t hamming_u32x = svsub_u32_x(predicate_all_b32x, depth_u32x, za1_u32x);
339
+ nk_u32_t *c_row = (nk_u32_t *)((char *)c + (row_start_a + row) * c_stride_in_bytes);
340
+ svst1_u32(column_predicate_b32x, c_row + row_start_b, hamming_u32x);
341
+ }
342
+ }
343
+ }
344
+ }
345
+
346
+ NK_PUBLIC void nk_hammings_packed_u1_smebi32( //
347
+ nk_u1x8_t const *a, void const *b_packed, nk_u32_t *c, nk_size_t row_count_a, nk_size_t row_count_b,
348
+ nk_size_t depth_bits, nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) {
349
+ nk_sme_start_streaming_();
350
+ nk_hammings_packed_u1_smebi32_streaming_(a, b_packed, c, row_count_a, row_count_b, depth_bits, a_stride_in_bytes,
351
+ c_stride_in_bytes);
352
+ nk_sme_stop_streaming_();
353
+ }
354
+
355
+ /**
356
+ * Symmetric Hamming using ZA0 time-sharing + 3-tile fast path.
357
+ * ZA0.S = staging (A rows loaded horizontally, read vertically for BMOPA).
358
+ * ZA1-3.S = BMOPA accumulators (3 B column tiles in fast path).
359
+ * Mirrors the unpacked kernel nk_hammings_packed_u1_smebi32_streaming_ pattern.
360
+ */
361
+ __arm_new("za") static void nk_hammings_symmetric_u1_smebi32_streaming_( //
362
+ nk_u1x8_t const *vectors, nk_size_t vectors_count, nk_size_t depth_bits, nk_size_t stride_in_bytes,
363
+ nk_u32_t *result, nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) NK_STREAMING_ {
364
+
365
+ nk_size_t const tile_dim = svcntw(); // 16 for 512-bit SVL
366
+ nk_size_t const depth_tile_size = svcntw(); // 16 u32 per depth tile
367
+ // BMOPA processes binary data in 32-bit words: each svbmopa_za32_u32_m step
368
+ // handles one u32 (32 bits) across all row×column pairs simultaneously.
369
+ nk_size_t const depth_words = nk_size_divide_round_up_(depth_bits, 32);
370
+ nk_size_t const depth_bytes = depth_bits / 8;
371
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth_words, depth_tile_size);
372
+
373
+ svbool_t const predicate_all_b32x = svptrue_b32();
374
+ // Use padded depth (depth_words * 32) for BMOPA: zero-padded bits always match in XNOR,
375
+ // so the effective depth for the matching→hamming conversion is the rounded-up bit count.
376
+ svuint32_t const depth_u32x = svdup_u32((nk_u32_t)(depth_words * 32));
377
+
378
+ NK_ALIGN64 nk_u32_t a_buffer[16][16]; // Stack buffer for A column save
379
+
380
+ nk_size_t const row_end = row_start + row_count;
381
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(vectors_count, tile_dim);
382
+
383
+ for (nk_size_t row_tile_start = row_start; row_tile_start < row_end && row_tile_start < vectors_count;
384
+ row_tile_start += tile_dim) {
385
+ nk_size_t const rows_remaining = (row_tile_start + tile_dim <= row_end) ? tile_dim : (row_end - row_tile_start);
386
+ nk_size_t const rows_clamped = (row_tile_start + rows_remaining <= vectors_count)
387
+ ? rows_remaining
388
+ : (vectors_count - row_tile_start);
389
+ svbool_t const row_predicate_b32x = svwhilelt_b32_u64(0u, rows_clamped);
390
+
391
+ // Upper triangle: start from this row tile's column
392
+ nk_size_t column_tile_index = row_tile_start / tile_dim;
393
+
394
+ // Fast path: 3 column tiles using ZA1-ZA3 (ZA0 = staging)
395
+ for (; column_tile_index + 3 <= column_tile_count; column_tile_index += 3) {
396
+ svzero_mask_za(nk_sme_zero_za32_tiles_123_);
397
+
398
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
399
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
400
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
401
+ ? depth_tile_size
402
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
403
+ if (u32s_this_tile == 0) break;
404
+
405
+ // Load A rows into ZA0 horizontally
406
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
407
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
408
+
409
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
410
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_clamped; row_in_tile++) {
411
+ nk_u8_t const *a_row = (nk_u8_t const *)vectors + (row_tile_start + row_in_tile) * stride_in_bytes +
412
+ d_start_u32 * 4;
413
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
414
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
415
+ }
416
+
417
+ // Save A columns from ZA0 to stack buffer
418
+ for (nk_size_t s = 0; s < u32s_this_tile; s++)
419
+ svst1_u32(predicate_all_b32x, a_buffer[s],
420
+ svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, s));
421
+
422
+ // B column tile 0
423
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
424
+ for (nk_size_t col = 0; col < tile_dim; col++) {
425
+ nk_size_t const col_abs = (column_tile_index + 0) * tile_dim + col;
426
+ if (col_abs < vectors_count) {
427
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
428
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
429
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
430
+ }
431
+ }
432
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
433
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
434
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
435
+ svbmopa_za32_u32_m(1, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
436
+ }
437
+
438
+ // B column tile 1
439
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
440
+ for (nk_size_t col = 0; col < tile_dim; col++) {
441
+ nk_size_t const col_abs = (column_tile_index + 1) * tile_dim + col;
442
+ if (col_abs < vectors_count) {
443
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
444
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
445
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
446
+ }
447
+ }
448
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
449
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
450
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
451
+ svbmopa_za32_u32_m(2, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
452
+ }
453
+
454
+ // B column tile 2
455
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
456
+ for (nk_size_t col = 0; col < tile_dim; col++) {
457
+ nk_size_t const col_abs = (column_tile_index + 2) * tile_dim + col;
458
+ if (col_abs < vectors_count) {
459
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
460
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
461
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
462
+ }
463
+ }
464
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
465
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
466
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
467
+ svbmopa_za32_u32_m(3, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
468
+ }
469
+ }
470
+
471
+ // Extract ZA1-3: hamming = depth_bits - ZA[i][j]
472
+ for (nk_size_t row = 0; row < rows_clamped; row++) {
473
+ nk_u32_t *c_row = (nk_u32_t *)((char *)result + (row_tile_start + row) * result_stride_in_bytes);
474
+
475
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
476
+ svuint32_t za2_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 2, row);
477
+ svuint32_t za3_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 3, row);
478
+
479
+ svst1_u32(predicate_all_b32x, c_row + (column_tile_index + 0) * tile_dim,
480
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za1_u32x));
481
+ svst1_u32(predicate_all_b32x, c_row + (column_tile_index + 1) * tile_dim,
482
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za2_u32x));
483
+ svst1_u32(predicate_all_b32x, c_row + (column_tile_index + 2) * tile_dim,
484
+ svsub_u32_x(predicate_all_b32x, depth_u32x, za3_u32x));
485
+ }
486
+ }
487
+
488
+ // Remainder: 1 column tile at a time using ZA1
489
+ for (; column_tile_index < column_tile_count; column_tile_index++) {
490
+ nk_size_t const col_tile_start = column_tile_index * tile_dim;
491
+ nk_size_t const cols_remaining = (col_tile_start + tile_dim <= vectors_count)
492
+ ? tile_dim
493
+ : (vectors_count - col_tile_start);
494
+ svbool_t const column_predicate_b32x = svwhilelt_b32_u64(0u, cols_remaining);
495
+
496
+ svzero_mask_za(nk_sme_zero_za32_tile_1_);
497
+
498
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
499
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
500
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
501
+ ? depth_tile_size
502
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
503
+ if (u32s_this_tile == 0) break;
504
+
505
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
506
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
507
+
508
+ // Load A rows into ZA0 horizontally
509
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
510
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_clamped; row_in_tile++) {
511
+ nk_u8_t const *a_row = (nk_u8_t const *)vectors + (row_tile_start + row_in_tile) * stride_in_bytes +
512
+ d_start_u32 * 4;
513
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
514
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
515
+ }
516
+
517
+ // Save A columns from ZA0 to stack buffer
518
+ for (nk_size_t s = 0; s < u32s_this_tile; s++)
519
+ svst1_u32(predicate_all_b32x, a_buffer[s],
520
+ svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, s));
521
+
522
+ // Load B column tile into ZA0
523
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
524
+ for (nk_size_t col = 0; col < tile_dim; col++) {
525
+ nk_size_t const col_abs = col_tile_start + col;
526
+ if (col_abs < vectors_count) {
527
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
528
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
529
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
530
+ }
531
+ }
532
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
533
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
534
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), column_predicate_b32x, 0, step);
535
+ svbmopa_za32_u32_m(1, row_predicate_b32x, column_predicate_b32x, a_u32x, b_u32x);
536
+ }
537
+ }
538
+
539
+ for (nk_size_t row = 0; row < rows_clamped; row++) {
540
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
541
+ svuint32_t hamming_u32x = svsub_u32_x(predicate_all_b32x, depth_u32x, za1_u32x);
542
+ nk_u32_t *c_row = (nk_u32_t *)((char *)result + (row_tile_start + row) * result_stride_in_bytes);
543
+ svst1_u32(column_predicate_b32x, c_row + col_tile_start, hamming_u32x);
544
+ }
545
+ }
546
+ }
547
+ }
548
+
549
+ NK_PUBLIC void nk_hammings_symmetric_u1_smebi32( //
550
+ nk_u1x8_t const *vectors, nk_size_t vectors_count, nk_size_t depth_bits, nk_size_t stride_in_bytes,
551
+ nk_u32_t *result, nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) {
552
+ nk_sme_start_streaming_();
553
+ nk_hammings_symmetric_u1_smebi32_streaming_(vectors, vectors_count, depth_bits, stride_in_bytes, result,
554
+ result_stride_in_bytes, row_start, row_count);
555
+ nk_sme_stop_streaming_();
556
+ }
557
+
558
+ #pragma endregion Hamming Distance
559
+
560
+ /*
561
+ * Jaccard distance via BMOPA matching counts + algebraic normalization.
562
+ *
563
+ * BMOPA gives: matching = popcount(XNOR(a,b))
564
+ * Then:
565
+ * hamming = depth_bits - matching
566
+ * intersection = (norm_a + norm_b - hamming) / 2 = (norm_a + norm_b - depth_bits + matching) / 2
567
+ * union = (norm_a + norm_b + hamming) / 2 = sum_norms - intersection
568
+ * jaccard = 1 - intersection / union (1.0 when union == 0)
569
+ *
570
+ * Inner BMOPA loop is identical to Hamming; only the extraction phase differs.
571
+ * Packed format shares the Hamming tile layout for B operand, plus per-row norms.
572
+ */
573
+
574
+ #pragma region Jaccard Distance
575
+
576
+ /**
577
+ * SME Jaccard kernel using BMOPA for matching-bit counts.
578
+ * Mirrors nk_hammings_packed_u1_smebi32_streaming_ exactly in structure,
579
+ * but derives intersection/union algebraically from the matching counts:
580
+ * matching = popcount(XNOR(a,b)) (from BMOPA)
581
+ * hamming = depth_bits - matching
582
+ * intersection = (norm_a + norm_b - hamming) / 2
583
+ * union = (norm_a + norm_b + hamming) / 2
584
+ * jaccard = 1 - intersection / union (1.0 when union == 0)
585
+ */
586
+ __arm_new("za") static void nk_jaccards_packed_u1_smebi32_streaming_( //
587
+ nk_u1x8_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t row_count_a, nk_size_t row_count_b,
588
+ nk_size_t depth_bits, nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) NK_STREAMING_ {
589
+
590
+ nk_sets_smebi32_packed_header_t const *header = (nk_sets_smebi32_packed_header_t const *)b_packed;
591
+ nk_size_t const row_tile_count_b = header->row_tile_count;
592
+ nk_size_t const depth_tile_count = header->depth_tile_count;
593
+
594
+ nk_size_t const tile_dim = svcntw(); // 16 for 512-bit SVL
595
+ nk_size_t const depth_tile_size = svcntw(); // 16 u32 per depth tile
596
+ nk_size_t const tile_elements = tile_dim * depth_tile_size;
597
+ // BMOPA processes binary data in 32-bit words: each svbmopa_za32_u32_m step
598
+ // handles one u32 (32 bits) across all row×column pairs simultaneously.
599
+ nk_size_t const depth_words = nk_size_divide_round_up_(depth_bits, 32);
600
+ nk_size_t const depth_bytes = depth_bits / 8;
601
+
602
+ nk_u32_t const *b_tiles = (nk_u32_t const *)((char const *)b_packed + sizeof(nk_sets_smebi32_packed_header_t));
603
+ nk_u32_t const *b_norms = header->norms_offset ? (nk_u32_t const *)((char const *)b_packed + header->norms_offset)
604
+ : (nk_u32_t const *)0;
605
+
606
+ svbool_t const predicate_all_b32x = svptrue_b32();
607
+ svfloat32_t const depth_f32x = svdup_f32((nk_f32_t)(depth_words * 32));
608
+ svfloat32_t const half_f32x = svdup_f32(0.5f);
609
+ svfloat32_t const one_f32x = svdup_f32(1.0f);
610
+ svfloat32_t const zero_f32x = svdup_f32(0.0f);
611
+ nk_size_t const row_tile_count_a = nk_size_divide_round_up_(row_count_a, tile_dim);
612
+
613
+ for (nk_size_t row_tile_a = 0; row_tile_a < row_tile_count_a; row_tile_a++) {
614
+ nk_size_t const row_start_a = row_tile_a * tile_dim;
615
+ nk_size_t const rows_a_remaining = (row_start_a + tile_dim <= row_count_a) ? tile_dim
616
+ : (row_count_a - row_start_a);
617
+ svbool_t const row_predicate_b32x = svwhilelt_b32_u64(0u, rows_a_remaining);
618
+
619
+ // Compute A tile norms using streaming SVE popcount
620
+ NK_ALIGN64 nk_f32_t a_tile_norms[16];
621
+ for (nk_size_t r = 0; r < rows_a_remaining; r++) {
622
+ nk_u1x8_t const *a_row = (nk_u1x8_t const *)((char const *)a + (row_start_a + r) * a_stride_in_bytes);
623
+ a_tile_norms[r] = (nk_f32_t)nk_sets_reduce_sumsq_u1_streaming_(a_row, depth_bytes);
624
+ }
625
+
626
+ // Fast path: 3 B column tiles using ZA1-ZA3 (ZA0.S = staging)
627
+ nk_size_t row_tile_b = 0;
628
+ for (; row_tile_b + 3 <= row_tile_count_b; row_tile_b += 3) {
629
+ svzero_mask_za(nk_sme_zero_za32_tiles_123_);
630
+
631
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
632
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
633
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
634
+ ? depth_tile_size
635
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
636
+ if (u32s_this_tile == 0) break;
637
+
638
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
639
+
640
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
641
+
642
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
643
+
644
+ // Load A rows into ZA0.S, byte-predicated to zero garbage bits
645
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_a_remaining; row_in_tile++) {
646
+ nk_u8_t const *a_row = (nk_u8_t const *)a + (row_start_a + row_in_tile) * a_stride_in_bytes +
647
+ d_start_u32 * 4;
648
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
649
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
650
+ }
651
+
652
+ // B tile pointers for 3 column tiles
653
+ nk_u32_t const *b_tile0 = b_tiles + ((row_tile_b + 0) * depth_tile_count + d_tile) * tile_elements;
654
+ nk_u32_t const *b_tile1 = b_tiles + ((row_tile_b + 1) * depth_tile_count + d_tile) * tile_elements;
655
+ nk_u32_t const *b_tile2 = b_tiles + ((row_tile_b + 2) * depth_tile_count + d_tile) * tile_elements;
656
+
657
+ // Vertical read + BMOPA for each depth step
658
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
659
+ svuint32_t a_column_u32x = svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, step);
660
+
661
+ svbmopa_za32_u32_m(1, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
662
+ svld1_u32(predicate_all_b32x, b_tile0 + step * tile_dim));
663
+ svbmopa_za32_u32_m(2, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
664
+ svld1_u32(predicate_all_b32x, b_tile1 + step * tile_dim));
665
+ svbmopa_za32_u32_m(3, row_predicate_b32x, predicate_all_b32x, a_column_u32x,
666
+ svld1_u32(predicate_all_b32x, b_tile2 + step * tile_dim));
667
+ }
668
+ }
669
+
670
+ // Extract from ZA1-3: Jaccard normalization via streaming SVE
671
+ // Hoist B norms outside row loop (same for all A rows in this tile-pair)
672
+ svfloat32_t b_norms_0_f32x = svcvt_f32_u32_x(
673
+ predicate_all_b32x, svld1_u32(predicate_all_b32x, b_norms + (row_tile_b + 0) * tile_dim));
674
+ svfloat32_t b_norms_1_f32x = svcvt_f32_u32_x(
675
+ predicate_all_b32x, svld1_u32(predicate_all_b32x, b_norms + (row_tile_b + 1) * tile_dim));
676
+ svfloat32_t b_norms_2_f32x = svcvt_f32_u32_x(
677
+ predicate_all_b32x, svld1_u32(predicate_all_b32x, b_norms + (row_tile_b + 2) * tile_dim));
678
+
679
+ for (nk_size_t row = 0; row < rows_a_remaining; row++) {
680
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c + (row_start_a + row) * c_stride_in_bytes);
681
+ svfloat32_t norm_a_f32x = svdup_f32(a_tile_norms[row]);
682
+
683
+ // ZA1
684
+ {
685
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
686
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za1_u32x);
687
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_0_f32x);
688
+ svfloat32_t intersection_f32x = svmul_f32_x(
689
+ predicate_all_b32x,
690
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
691
+ matching_f32x),
692
+ half_f32x);
693
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
694
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
695
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
696
+ svfloat32_t jaccard_f32x = svsel_f32(
697
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
698
+ svst1_f32(predicate_all_b32x, c_row + (row_tile_b + 0) * tile_dim, jaccard_f32x);
699
+ }
700
+ // ZA2
701
+ {
702
+ svuint32_t za2_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 2, row);
703
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za2_u32x);
704
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_1_f32x);
705
+ svfloat32_t intersection_f32x = svmul_f32_x(
706
+ predicate_all_b32x,
707
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
708
+ matching_f32x),
709
+ half_f32x);
710
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
711
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
712
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
713
+ svfloat32_t jaccard_f32x = svsel_f32(
714
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
715
+ svst1_f32(predicate_all_b32x, c_row + (row_tile_b + 1) * tile_dim, jaccard_f32x);
716
+ }
717
+ // ZA3
718
+ {
719
+ svuint32_t za3_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 3, row);
720
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za3_u32x);
721
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_2_f32x);
722
+ svfloat32_t intersection_f32x = svmul_f32_x(
723
+ predicate_all_b32x,
724
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
725
+ matching_f32x),
726
+ half_f32x);
727
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
728
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
729
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
730
+ svfloat32_t jaccard_f32x = svsel_f32(
731
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
732
+ svst1_f32(predicate_all_b32x, c_row + (row_tile_b + 2) * tile_dim, jaccard_f32x);
733
+ }
734
+ }
735
+ }
736
+
737
+ // Remainder: 1 B column tile at a time using ZA1
738
+ for (; row_tile_b < row_tile_count_b; row_tile_b++) {
739
+ nk_size_t const row_start_b = row_tile_b * tile_dim;
740
+ nk_size_t const rows_b_remaining = (row_start_b + tile_dim <= row_count_b) ? tile_dim
741
+ : (row_count_b - row_start_b);
742
+ svbool_t const column_predicate_b32x = svwhilelt_b32_u64(0u, rows_b_remaining);
743
+
744
+ svzero_mask_za(nk_sme_zero_za32_tile_1_);
745
+
746
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
747
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
748
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
749
+ ? depth_tile_size
750
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
751
+ if (u32s_this_tile == 0) break;
752
+
753
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
754
+
755
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
756
+
757
+ // Load A rows into ZA0.S horizontally
758
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
759
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_a_remaining; row_in_tile++) {
760
+ nk_u8_t const *a_row = (nk_u8_t const *)a + (row_start_a + row_in_tile) * a_stride_in_bytes +
761
+ d_start_u32 * 4;
762
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
763
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
764
+ }
765
+
766
+ nk_u32_t const *b_tile = b_tiles + (row_tile_b * depth_tile_count + d_tile) * tile_elements;
767
+
768
+ // Vertical read + BMOPA
769
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
770
+ svuint32_t a_column_u32x = svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, step);
771
+ svuint32_t b_u32x = svld1_u32(predicate_all_b32x, b_tile + step * tile_dim);
772
+ svbmopa_za32_u32_m(1, row_predicate_b32x, column_predicate_b32x, a_column_u32x, b_u32x);
773
+ }
774
+ }
775
+
776
+ // Extract from ZA1: Jaccard normalization
777
+ svfloat32_t b_norms_f32x = svcvt_f32_u32_x(predicate_all_b32x,
778
+ svld1_u32(predicate_all_b32x, b_norms + row_start_b));
779
+ for (nk_size_t row = 0; row < rows_a_remaining; row++) {
780
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
781
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za1_u32x);
782
+ svfloat32_t norm_a_f32x = svdup_f32(a_tile_norms[row]);
783
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_f32x);
784
+ svfloat32_t intersection_f32x = svmul_f32_x(
785
+ predicate_all_b32x,
786
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
787
+ matching_f32x),
788
+ half_f32x);
789
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
790
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
791
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
792
+ svfloat32_t jaccard_f32x = svsel_f32(nonzero_b32x,
793
+ svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
794
+ nk_f32_t *c_row = (nk_f32_t *)((char *)c + (row_start_a + row) * c_stride_in_bytes);
795
+ svst1_f32(column_predicate_b32x, c_row + row_start_b, jaccard_f32x);
796
+ }
797
+ }
798
+ }
799
+ }
800
+
801
+ NK_PUBLIC void nk_jaccards_packed_u1_smebi32( //
802
+ nk_u1x8_t const *a, void const *b_packed, nk_f32_t *c, nk_size_t row_count_a, nk_size_t row_count_b,
803
+ nk_size_t depth_bits, nk_size_t a_stride_in_bytes, nk_size_t c_stride_in_bytes) {
804
+ nk_sme_start_streaming_();
805
+ nk_jaccards_packed_u1_smebi32_streaming_(a, b_packed, c, row_count_a, row_count_b, depth_bits, a_stride_in_bytes,
806
+ c_stride_in_bytes);
807
+ nk_sme_stop_streaming_();
808
+ }
809
+
810
+ /**
811
+ * Symmetric Jaccard kernel using ZA0 time-sharing + 3-tile fast path.
812
+ * Fills upper triangle only (column_tile >= row_tile); caller sees result[i][j] for j >= i.
813
+ * Norms computed on-the-fly using streaming SVE popcount.
814
+ */
815
+ __arm_new("za") static void nk_jaccards_symmetric_u1_smebi32_streaming_( //
816
+ nk_u1x8_t const *vectors, nk_size_t vectors_count, nk_size_t depth_bits, nk_size_t stride_in_bytes,
817
+ nk_f32_t *result, nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) NK_STREAMING_ {
818
+
819
+ nk_size_t const tile_dim = svcntw(); // 16 for 512-bit SVL
820
+ nk_size_t const depth_tile_size = svcntw(); // 16 u32 per depth tile
821
+ // BMOPA processes binary data in 32-bit words: each svbmopa_za32_u32_m step
822
+ // handles one u32 (32 bits) across all row×column pairs simultaneously.
823
+ nk_size_t const depth_words = nk_size_divide_round_up_(depth_bits, 32);
824
+ nk_size_t const depth_tile_count = nk_size_divide_round_up_(depth_words, depth_tile_size);
825
+ nk_size_t const depth_bytes = depth_bits / 8;
826
+
827
+ svbool_t const predicate_all_b32x = svptrue_b32();
828
+ svfloat32_t const depth_f32x = svdup_f32((nk_f32_t)(depth_words * 32));
829
+ svfloat32_t const half_f32x = svdup_f32(0.5f);
830
+ svfloat32_t const one_f32x = svdup_f32(1.0f);
831
+ svfloat32_t const zero_f32x = svdup_f32(0.0f);
832
+
833
+ NK_ALIGN64 nk_u32_t a_buffer[16][16]; // Stack buffer for A column save
834
+
835
+ nk_size_t const row_end = row_start + row_count;
836
+ nk_size_t const column_tile_count = nk_size_divide_round_up_(vectors_count, tile_dim);
837
+
838
+ for (nk_size_t row_tile_start = row_start; row_tile_start < row_end && row_tile_start < vectors_count;
839
+ row_tile_start += tile_dim) {
840
+ nk_size_t const rows_remaining = (row_tile_start + tile_dim <= row_end) ? tile_dim : (row_end - row_tile_start);
841
+ nk_size_t const rows_clamped = (row_tile_start + rows_remaining <= vectors_count)
842
+ ? rows_remaining
843
+ : (vectors_count - row_tile_start);
844
+ svbool_t const row_predicate_b32x = svwhilelt_b32_u64(0u, rows_clamped);
845
+
846
+ // Compute A tile norms
847
+ NK_ALIGN64 nk_f32_t a_tile_norms[16];
848
+ for (nk_size_t r = 0; r < rows_clamped; r++) {
849
+ nk_u1x8_t const *a_row = (nk_u1x8_t const *)((char const *)vectors +
850
+ (row_tile_start + r) * stride_in_bytes);
851
+ a_tile_norms[r] = (nk_f32_t)nk_sets_reduce_sumsq_u1_streaming_(a_row, depth_bytes);
852
+ }
853
+ for (nk_size_t r = rows_clamped; r < tile_dim; r++) a_tile_norms[r] = 0.0f;
854
+
855
+ // Upper triangle: start from this row tile's column
856
+ nk_size_t column_tile_index = row_tile_start / tile_dim;
857
+
858
+ // Fast path: 3 column tiles using ZA1-ZA3 (ZA0 = staging)
859
+ for (; column_tile_index + 3 <= column_tile_count; column_tile_index += 3) {
860
+ svzero_mask_za(nk_sme_zero_za32_tiles_123_);
861
+
862
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
863
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
864
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
865
+ ? depth_tile_size
866
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
867
+ if (u32s_this_tile == 0) break;
868
+
869
+ // Load A rows into ZA0 horizontally
870
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
871
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
872
+
873
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
874
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_clamped; row_in_tile++) {
875
+ nk_u8_t const *a_row = (nk_u8_t const *)vectors + (row_tile_start + row_in_tile) * stride_in_bytes +
876
+ d_start_u32 * 4;
877
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
878
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
879
+ }
880
+
881
+ // Save A columns from ZA0 to stack buffer
882
+ for (nk_size_t s = 0; s < u32s_this_tile; s++)
883
+ svst1_u32(predicate_all_b32x, a_buffer[s],
884
+ svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, s));
885
+
886
+ // B column tile 0
887
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
888
+ for (nk_size_t col = 0; col < tile_dim; col++) {
889
+ nk_size_t const col_abs = (column_tile_index + 0) * tile_dim + col;
890
+ if (col_abs < vectors_count) {
891
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
892
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
893
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
894
+ }
895
+ }
896
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
897
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
898
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
899
+ svbmopa_za32_u32_m(1, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
900
+ }
901
+
902
+ // B column tile 1
903
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
904
+ for (nk_size_t col = 0; col < tile_dim; col++) {
905
+ nk_size_t const col_abs = (column_tile_index + 1) * tile_dim + col;
906
+ if (col_abs < vectors_count) {
907
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
908
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
909
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
910
+ }
911
+ }
912
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
913
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
914
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
915
+ svbmopa_za32_u32_m(2, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
916
+ }
917
+
918
+ // B column tile 2
919
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
920
+ for (nk_size_t col = 0; col < tile_dim; col++) {
921
+ nk_size_t const col_abs = (column_tile_index + 2) * tile_dim + col;
922
+ if (col_abs < vectors_count) {
923
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
924
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
925
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
926
+ }
927
+ }
928
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
929
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
930
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), predicate_all_b32x, 0, step);
931
+ svbmopa_za32_u32_m(3, row_predicate_b32x, predicate_all_b32x, a_u32x, b_u32x);
932
+ }
933
+ }
934
+
935
+ // Compute B tile norms for 3 column tiles
936
+ NK_ALIGN64 nk_u32_t b_tile_norms_0[16];
937
+ NK_ALIGN64 nk_u32_t b_tile_norms_1[16];
938
+ NK_ALIGN64 nk_u32_t b_tile_norms_2[16];
939
+ for (nk_size_t col = 0; col < tile_dim; col++) {
940
+ nk_size_t const col_abs_0 = (column_tile_index + 0) * tile_dim + col;
941
+ nk_size_t const col_abs_1 = (column_tile_index + 1) * tile_dim + col;
942
+ nk_size_t const col_abs_2 = (column_tile_index + 2) * tile_dim + col;
943
+ b_tile_norms_0[col] =
944
+ (col_abs_0 < vectors_count)
945
+ ? nk_sets_reduce_sumsq_u1_streaming_(
946
+ (nk_u1x8_t const *)((char const *)vectors + col_abs_0 * stride_in_bytes), depth_bytes)
947
+ : 0;
948
+ b_tile_norms_1[col] =
949
+ (col_abs_1 < vectors_count)
950
+ ? nk_sets_reduce_sumsq_u1_streaming_(
951
+ (nk_u1x8_t const *)((char const *)vectors + col_abs_1 * stride_in_bytes), depth_bytes)
952
+ : 0;
953
+ b_tile_norms_2[col] =
954
+ (col_abs_2 < vectors_count)
955
+ ? nk_sets_reduce_sumsq_u1_streaming_(
956
+ (nk_u1x8_t const *)((char const *)vectors + col_abs_2 * stride_in_bytes), depth_bytes)
957
+ : 0;
958
+ }
959
+
960
+ // Extract ZA1-3: Jaccard normalization
961
+ svfloat32_t b_norms_0_f32x = svcvt_f32_u32_x(predicate_all_b32x,
962
+ svld1_u32(predicate_all_b32x, b_tile_norms_0));
963
+ svfloat32_t b_norms_1_f32x = svcvt_f32_u32_x(predicate_all_b32x,
964
+ svld1_u32(predicate_all_b32x, b_tile_norms_1));
965
+ svfloat32_t b_norms_2_f32x = svcvt_f32_u32_x(predicate_all_b32x,
966
+ svld1_u32(predicate_all_b32x, b_tile_norms_2));
967
+
968
+ for (nk_size_t row = 0; row < rows_clamped; row++) {
969
+ nk_f32_t *c_row = (nk_f32_t *)((char *)result + (row_tile_start + row) * result_stride_in_bytes);
970
+ svfloat32_t norm_a_f32x = svdup_f32(a_tile_norms[row]);
971
+
972
+ // ZA1
973
+ {
974
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
975
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za1_u32x);
976
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_0_f32x);
977
+ svfloat32_t intersection_f32x = svmul_f32_x(
978
+ predicate_all_b32x,
979
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
980
+ matching_f32x),
981
+ half_f32x);
982
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
983
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
984
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
985
+ svfloat32_t jaccard_f32x = svsel_f32(
986
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
987
+ svst1_f32(predicate_all_b32x, c_row + (column_tile_index + 0) * tile_dim, jaccard_f32x);
988
+ }
989
+ // ZA2
990
+ {
991
+ svuint32_t za2_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 2, row);
992
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za2_u32x);
993
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_1_f32x);
994
+ svfloat32_t intersection_f32x = svmul_f32_x(
995
+ predicate_all_b32x,
996
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
997
+ matching_f32x),
998
+ half_f32x);
999
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
1000
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
1001
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
1002
+ svfloat32_t jaccard_f32x = svsel_f32(
1003
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
1004
+ svst1_f32(predicate_all_b32x, c_row + (column_tile_index + 1) * tile_dim, jaccard_f32x);
1005
+ }
1006
+ // ZA3
1007
+ {
1008
+ svuint32_t za3_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 3, row);
1009
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za3_u32x);
1010
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_2_f32x);
1011
+ svfloat32_t intersection_f32x = svmul_f32_x(
1012
+ predicate_all_b32x,
1013
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
1014
+ matching_f32x),
1015
+ half_f32x);
1016
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
1017
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
1018
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
1019
+ svfloat32_t jaccard_f32x = svsel_f32(
1020
+ nonzero_b32x, svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
1021
+ svst1_f32(predicate_all_b32x, c_row + (column_tile_index + 2) * tile_dim, jaccard_f32x);
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ // Remainder: 1 column tile at a time using ZA1
1027
+ for (; column_tile_index < column_tile_count; column_tile_index++) {
1028
+ nk_size_t const col_tile_start = column_tile_index * tile_dim;
1029
+ nk_size_t const cols_remaining = (col_tile_start + tile_dim <= vectors_count)
1030
+ ? tile_dim
1031
+ : (vectors_count - col_tile_start);
1032
+ svbool_t const column_predicate_b32x = svwhilelt_b32_u64(0u, cols_remaining);
1033
+
1034
+ svzero_mask_za(nk_sme_zero_za32_tile_1_);
1035
+
1036
+ for (nk_size_t d_tile = 0; d_tile < depth_tile_count; d_tile++) {
1037
+ nk_size_t const d_start_u32 = d_tile * depth_tile_size;
1038
+ nk_size_t const u32s_this_tile = (d_start_u32 + depth_tile_size <= depth_words)
1039
+ ? depth_tile_size
1040
+ : (depth_words > d_start_u32 ? depth_words - d_start_u32 : 0);
1041
+ if (u32s_this_tile == 0) break;
1042
+
1043
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1044
+ svbool_t const batch_predicate_b32x = svwhilelt_b32_u64(0u, u32s_this_tile);
1045
+
1046
+ // Load A rows into ZA0 horizontally
1047
+ svbool_t const depth_predicate_b8x = svwhilelt_b8_u64(d_start_u32 * 4, depth_bytes);
1048
+ for (nk_size_t row_in_tile = 0; row_in_tile < rows_clamped; row_in_tile++) {
1049
+ nk_u8_t const *a_row = (nk_u8_t const *)vectors + (row_tile_start + row_in_tile) * stride_in_bytes +
1050
+ d_start_u32 * 4;
1051
+ svuint8_t row_u8x = svld1_u8(depth_predicate_b8x, a_row);
1052
+ svwrite_hor_za32_u32_m(0, row_in_tile, batch_predicate_b32x, svreinterpret_u32_u8(row_u8x));
1053
+ }
1054
+
1055
+ // Save A columns from ZA0 to stack buffer
1056
+ for (nk_size_t s = 0; s < u32s_this_tile; s++)
1057
+ svst1_u32(predicate_all_b32x, a_buffer[s],
1058
+ svread_ver_za32_u32_m(svdup_u32(0), row_predicate_b32x, 0, s));
1059
+
1060
+ // Load B column tile into ZA0
1061
+ svzero_mask_za(nk_sme_zero_za32_tile_0_);
1062
+ for (nk_size_t col = 0; col < tile_dim; col++) {
1063
+ nk_size_t const col_abs = col_tile_start + col;
1064
+ if (col_abs < vectors_count) {
1065
+ nk_u8_t const *b_row = (nk_u8_t const *)vectors + col_abs * stride_in_bytes + d_start_u32 * 4;
1066
+ svuint8_t col_u8x = svld1_u8(depth_predicate_b8x, b_row);
1067
+ svwrite_hor_za32_u32_m(0, col, batch_predicate_b32x, svreinterpret_u32_u8(col_u8x));
1068
+ }
1069
+ }
1070
+ for (nk_size_t step = 0; step < u32s_this_tile; step++) {
1071
+ svuint32_t a_u32x = svld1_u32(predicate_all_b32x, a_buffer[step]);
1072
+ svuint32_t b_u32x = svread_ver_za32_u32_m(svdup_u32(0), column_predicate_b32x, 0, step);
1073
+ svbmopa_za32_u32_m(1, row_predicate_b32x, column_predicate_b32x, a_u32x, b_u32x);
1074
+ }
1075
+ }
1076
+
1077
+ // Compute B tile norms for remainder tile
1078
+ NK_ALIGN64 nk_u32_t b_tile_norms[16];
1079
+ for (nk_size_t col = 0; col < tile_dim; col++) {
1080
+ nk_size_t const col_abs = col_tile_start + col;
1081
+ b_tile_norms[col] = (col_abs < vectors_count)
1082
+ ? nk_sets_reduce_sumsq_u1_streaming_(
1083
+ (nk_u1x8_t const *)((char const *)vectors + col_abs * stride_in_bytes),
1084
+ depth_bytes)
1085
+ : 0;
1086
+ }
1087
+
1088
+ svfloat32_t b_norms_f32x = svcvt_f32_u32_x(predicate_all_b32x, svld1_u32(predicate_all_b32x, b_tile_norms));
1089
+ for (nk_size_t row = 0; row < rows_clamped; row++) {
1090
+ svuint32_t za1_u32x = svread_hor_za32_u32_m(svdup_u32(0), predicate_all_b32x, 1, row);
1091
+ svfloat32_t matching_f32x = svcvt_f32_u32_x(predicate_all_b32x, za1_u32x);
1092
+ svfloat32_t norm_a_f32x = svdup_f32(a_tile_norms[row]);
1093
+ svfloat32_t sum_norms_f32x = svadd_f32_x(predicate_all_b32x, norm_a_f32x, b_norms_f32x);
1094
+ svfloat32_t intersection_f32x = svmul_f32_x(
1095
+ predicate_all_b32x,
1096
+ svadd_f32_x(predicate_all_b32x, svsub_f32_x(predicate_all_b32x, sum_norms_f32x, depth_f32x),
1097
+ matching_f32x),
1098
+ half_f32x);
1099
+ svfloat32_t union_val_f32x = svsub_f32_x(predicate_all_b32x, sum_norms_f32x, intersection_f32x);
1100
+ svbool_t nonzero_b32x = svcmpne_f32(predicate_all_b32x, union_val_f32x, zero_f32x);
1101
+ svfloat32_t ratio_f32x = svdiv_f32_x(predicate_all_b32x, intersection_f32x, union_val_f32x);
1102
+ svfloat32_t jaccard_f32x = svsel_f32(nonzero_b32x,
1103
+ svsub_f32_x(predicate_all_b32x, one_f32x, ratio_f32x), one_f32x);
1104
+ nk_f32_t *c_row = (nk_f32_t *)((char *)result + (row_tile_start + row) * result_stride_in_bytes);
1105
+ svst1_f32(column_predicate_b32x, c_row + col_tile_start, jaccard_f32x);
1106
+ }
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ NK_PUBLIC void nk_jaccards_symmetric_u1_smebi32( //
1112
+ nk_u1x8_t const *vectors, nk_size_t vectors_count, nk_size_t depth_bits, nk_size_t stride_in_bytes,
1113
+ nk_f32_t *result, nk_size_t result_stride_in_bytes, nk_size_t row_start, nk_size_t row_count) {
1114
+ nk_sme_start_streaming_();
1115
+ nk_jaccards_symmetric_u1_smebi32_streaming_(vectors, vectors_count, depth_bits, stride_in_bytes, result,
1116
+ result_stride_in_bytes, row_start, row_count);
1117
+ nk_sme_stop_streaming_();
1118
+ }
1119
+
1120
+ #pragma endregion Jaccard Distance
1121
+
1122
+ #if defined(__clang__)
1123
+ #pragma clang attribute pop
1124
+ #elif defined(__GNUC__)
1125
+ #pragma GCC pop_options
1126
+ #endif
1127
+
1128
+ #if defined(__cplusplus)
1129
+ } // extern "C"
1130
+ #endif
1131
+
1132
+ #endif // NK_TARGET_SMEBI32
1133
+ #endif // NK_TARGET_ARM64_
1134
+
1135
+ #endif // NK_SETS_SMEBI32_H