usearch 2.21.3 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +235 -52
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,329 @@
1
+ /**
2
+ * @brief SIMD-accelerated Curved Space Similarity for NEON.
3
+ * @file include/numkong/curved/neon.h
4
+ * @author Ash Vardanian
5
+ * @date January 14, 2026
6
+ *
7
+ * @sa include/numkong/curved.h
8
+ *
9
+ * Implements f32 bilinear forms and Mahalanobis distance using ARM NEON SIMD.
10
+ * Accumulates f32 inputs in f64 precision to avoid catastrophic cancellation.
11
+ *
12
+ * @section neon_curved_instructions Key NEON Instructions
13
+ *
14
+ * Intrinsic Instruction A76 M5
15
+ * vfmaq_f64 FMLA (V.2D, V.2D, V.2D) 4cy @ 2p 3cy @ 4p
16
+ * vcvt_f64_f32 FCVTL (V.2D, V.2S) 3cy @ 2p 3cy @ 4p
17
+ * vaddvq_f64 FADDP (V.2D to scalar) 3cy @ 1p 3cy @ 2p
18
+ * vld1_f32 LD1 ({Vt.2S}, [Xn]) 4cy @ 2p 4cy @ 3p
19
+ * vld2_f32 LD2 ({Vt.2S, Vt2.2S}, [Xn]) 4cy @ 1p 4cy @ 1p
20
+ *
21
+ * For f32 bilinear and Mahalanobis, we upcast to f64 for accumulation to preserve
22
+ * precision and avoid catastrophic cancellation in large-magnitude sums.
23
+ */
24
+ #ifndef NK_CURVED_NEON_H
25
+ #define NK_CURVED_NEON_H
26
+
27
+ #if NK_TARGET_ARM64_
28
+ #if NK_TARGET_NEON
29
+
30
+ #include "numkong/types.h"
31
+ #include "numkong/spatial/neon.h" // nk_f64_sqrt_neon
32
+
33
+ #if defined(__cplusplus)
34
+ extern "C" {
35
+ #endif
36
+
37
+ #if defined(__clang__)
38
+ #pragma clang attribute push(__attribute__((target("arch=armv8-a+simd"))), apply_to = function)
39
+ #elif defined(__GNUC__)
40
+ #pragma GCC push_options
41
+ #pragma GCC target("arch=armv8-a+simd")
42
+ #endif
43
+
44
+ NK_PUBLIC void nk_bilinear_f32_neon(nk_f32_t const *a, nk_f32_t const *b, nk_f32_t const *c, nk_size_t n,
45
+ nk_f64_t *result) {
46
+ nk_f64_t outer_sum_f64 = 0;
47
+
48
+ for (nk_size_t i = 0; i != n; ++i) {
49
+ // Convert a[i] to f64 for precision
50
+ nk_f64_t a_i_f64 = (nk_f64_t)a[i];
51
+
52
+ // Inner loop: accumulate Σⱼ cᵢⱼ × bⱼ in f64
53
+ float64x2_t inner_sum_f64x2 = vdupq_n_f64(0);
54
+ nk_size_t j = 0;
55
+
56
+ // Vectorized inner loop: process 2 elements at a time
57
+ for (; j + 2 <= n; j += 2) {
58
+ // Load b[j:j+2] as f32, upcast to f64
59
+ float32x2_t b_f32x2 = vld1_f32(b + j);
60
+ float64x2_t b_f64x2 = vcvt_f64_f32(b_f32x2);
61
+
62
+ // Load c[i*n+j : i*n+j+2] as f32, upcast to f64
63
+ float32x2_t c_f32x2 = vld1_f32(c + i * n + j);
64
+ float64x2_t c_f64x2 = vcvt_f64_f32(c_f32x2);
65
+
66
+ // FMA: inner_sum += c × b
67
+ inner_sum_f64x2 = vfmaq_f64(inner_sum_f64x2, c_f64x2, b_f64x2);
68
+ }
69
+
70
+ // Reduce the f64x2 accumulator to scalar
71
+ nk_f64_t inner_sum_f64 = vaddvq_f64(inner_sum_f64x2);
72
+
73
+ // Handle tail elements
74
+ for (; j < n; ++j) { inner_sum_f64 += (nk_f64_t)c[i * n + j] * (nk_f64_t)b[j]; }
75
+
76
+ // Outer accumulation: outer_sum += aᵢ × inner_sum
77
+ outer_sum_f64 += a_i_f64 * inner_sum_f64;
78
+ }
79
+
80
+ *result = outer_sum_f64;
81
+ }
82
+
83
+ NK_PUBLIC void nk_mahalanobis_f32_neon(nk_f32_t const *a, nk_f32_t const *b, nk_f32_t const *c, nk_size_t n,
84
+ nk_f64_t *result) {
85
+ nk_f64_t outer_sum_f64 = 0;
86
+
87
+ for (nk_size_t i = 0; i != n; ++i) {
88
+ // Compute difference (aᵢ - bᵢ) in f64 for precision
89
+ nk_f64_t diff_i_f64 = (nk_f64_t)a[i] - (nk_f64_t)b[i];
90
+
91
+ // Inner loop: accumulate Σⱼ cᵢⱼ × (aⱼ - bⱼ) in f64
92
+ float64x2_t inner_sum_f64x2 = vdupq_n_f64(0);
93
+ nk_size_t j = 0;
94
+
95
+ // Vectorized inner loop: process 2 elements at a time
96
+ for (; j + 2 <= n; j += 2) {
97
+ // Load a[j:j+2] and b[j:j+2] as f32
98
+ float32x2_t a_f32x2 = vld1_f32(a + j);
99
+ float32x2_t b_f32x2 = vld1_f32(b + j);
100
+
101
+ // Compute difference in f32, then upcast to f64
102
+ float32x2_t diff_f32x2 = vsub_f32(a_f32x2, b_f32x2);
103
+ float64x2_t diff_f64x2 = vcvt_f64_f32(diff_f32x2);
104
+
105
+ // Load c[i*n+j : i*n+j+2] as f32, upcast to f64
106
+ float32x2_t c_f32x2 = vld1_f32(c + i * n + j);
107
+ float64x2_t c_f64x2 = vcvt_f64_f32(c_f32x2);
108
+
109
+ // FMA: inner_sum += c × diff
110
+ inner_sum_f64x2 = vfmaq_f64(inner_sum_f64x2, c_f64x2, diff_f64x2);
111
+ }
112
+
113
+ // Reduce the f64x2 accumulator to scalar
114
+ nk_f64_t inner_sum_f64 = vaddvq_f64(inner_sum_f64x2);
115
+
116
+ // Handle tail elements
117
+ for (; j < n; ++j) {
118
+ nk_f64_t diff_j_f64 = (nk_f64_t)a[j] - (nk_f64_t)b[j];
119
+ inner_sum_f64 += (nk_f64_t)c[i * n + j] * diff_j_f64;
120
+ }
121
+
122
+ // Outer accumulation: outer_sum += diff_i × inner_sum
123
+ outer_sum_f64 += diff_i_f64 * inner_sum_f64;
124
+ }
125
+
126
+ // Take square root of the result (clamp to 0 for numerical stability)
127
+ *result = nk_f64_sqrt_neon(outer_sum_f64 > 0 ? outer_sum_f64 : 0);
128
+ }
129
+
130
+ NK_PUBLIC void nk_bilinear_f32c_neon(nk_f32c_t const *a_pairs, nk_f32c_t const *b_pairs, nk_f32c_t const *c_pairs,
131
+ nk_size_t n, nk_f64c_t *results) {
132
+ // ARMv8.3-A FCMLA (`vcmlaq_f32`) was benchmarked for this complex inner loop.
133
+ // The deinterleave+4FMA pattern is 2.3x faster on Apple M4 — see `dot/neon.h` comment.
134
+ nk_f64_t outer_sum_real_f64 = 0;
135
+ nk_f64_t outer_sum_imag_f64 = 0;
136
+
137
+ for (nk_size_t i = 0; i != n; ++i) {
138
+ // Convert a[i] to f64 for precision
139
+ nk_f64_t a_real_f64 = (nk_f64_t)a_pairs[i].real;
140
+ nk_f64_t a_imag_f64 = (nk_f64_t)a_pairs[i].imag;
141
+
142
+ // Inner loop: accumulate Σⱼ cᵢⱼ × bⱼ in f64
143
+ float64x2_t inner_sum_real_f64x2 = vdupq_n_f64(0);
144
+ float64x2_t inner_sum_imag_f64x2 = vdupq_n_f64(0);
145
+ nk_size_t j = 0;
146
+
147
+ // Vectorized inner loop: process 2 complex elements at a time
148
+ for (; j + 2 <= n; j += 2) {
149
+ // Load b[j:j+2] as interleaved complex pairs (real, imag, real, imag)
150
+ float32x2x2_t b_f32x2x2 = vld2_f32((nk_f32_t const *)(b_pairs + j));
151
+ float64x2_t b_real_f64x2 = vcvt_f64_f32(b_f32x2x2.val[0]);
152
+ float64x2_t b_imag_f64x2 = vcvt_f64_f32(b_f32x2x2.val[1]);
153
+
154
+ // Load c[i*n+j : i*n+j+2] as interleaved complex pairs
155
+ float32x2x2_t c_f32x2x2 = vld2_f32((nk_f32_t const *)(c_pairs + i * n + j));
156
+ float64x2_t c_real_f64x2 = vcvt_f64_f32(c_f32x2x2.val[0]);
157
+ float64x2_t c_imag_f64x2 = vcvt_f64_f32(c_f32x2x2.val[1]);
158
+
159
+ // Complex multiply
160
+ inner_sum_real_f64x2 = vfmaq_f64(inner_sum_real_f64x2, c_real_f64x2, b_real_f64x2);
161
+ inner_sum_real_f64x2 = vfmsq_f64(inner_sum_real_f64x2, c_imag_f64x2, b_imag_f64x2);
162
+
163
+ // Imaginary part: c_real×b_imag + c_imag×b_real
164
+ inner_sum_imag_f64x2 = vfmaq_f64(inner_sum_imag_f64x2, c_real_f64x2, b_imag_f64x2);
165
+ inner_sum_imag_f64x2 = vfmaq_f64(inner_sum_imag_f64x2, c_imag_f64x2, b_real_f64x2);
166
+ }
167
+
168
+ // Reduce the f64x2 accumulators to scalars
169
+ nk_f64_t inner_sum_real_f64 = vaddvq_f64(inner_sum_real_f64x2);
170
+ nk_f64_t inner_sum_imag_f64 = vaddvq_f64(inner_sum_imag_f64x2);
171
+
172
+ // Handle tail elements
173
+ for (; j < n; ++j) {
174
+ nk_f64_t b_real = (nk_f64_t)b_pairs[j].real;
175
+ nk_f64_t b_imag = (nk_f64_t)b_pairs[j].imag;
176
+ nk_f64_t c_real = (nk_f64_t)c_pairs[i * n + j].real;
177
+ nk_f64_t c_imag = (nk_f64_t)c_pairs[i * n + j].imag;
178
+ // Complex multiply: c × b
179
+ inner_sum_real_f64 += c_real * b_real - c_imag * b_imag;
180
+ inner_sum_imag_f64 += c_real * b_imag + c_imag * b_real;
181
+ }
182
+
183
+ // Outer accumulation
184
+ outer_sum_real_f64 += a_real_f64 * inner_sum_real_f64 - a_imag_f64 * inner_sum_imag_f64;
185
+ outer_sum_imag_f64 += a_real_f64 * inner_sum_imag_f64 + a_imag_f64 * inner_sum_real_f64;
186
+ }
187
+
188
+ results->real = outer_sum_real_f64;
189
+ results->imag = outer_sum_imag_f64;
190
+ }
191
+
192
+ NK_PUBLIC void nk_bilinear_f16_neon(nk_f16_t const *a, nk_f16_t const *b, nk_f16_t const *c, nk_size_t n,
193
+ nk_f32_t *result) {
194
+ nk_f32_t outer_sum = 0;
195
+ for (nk_size_t row = 0; row != n; ++row) {
196
+ nk_f16_t const *c_row = c + row * n;
197
+ nk_f32_t a_row;
198
+ nk_f16_to_f32_serial(a + row, &a_row);
199
+ float32x4_t inner_sum_f32x4 = vdupq_n_f32(0);
200
+ nk_size_t column = 0;
201
+ for (; column + 8 <= n; column += 8) {
202
+ float16x8_t b_f16x8 = vreinterpretq_f16_u16(vld1q_u16((nk_u16_t const *)(b + column)));
203
+ float16x8_t c_f16x8 = vreinterpretq_f16_u16(vld1q_u16((nk_u16_t const *)(c_row + column)));
204
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
205
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
206
+ float32x4_t c_low_f32x4 = vcvt_f32_f16(vget_low_f16(c_f16x8));
207
+ float32x4_t c_high_f32x4 = vcvt_high_f32_f16(c_f16x8);
208
+ inner_sum_f32x4 = vfmaq_f32(inner_sum_f32x4, c_low_f32x4, b_low_f32x4);
209
+ inner_sum_f32x4 = vfmaq_f32(inner_sum_f32x4, c_high_f32x4, b_high_f32x4);
210
+ }
211
+ nk_f32_t inner_sum = vaddvq_f32(inner_sum_f32x4);
212
+ for (; column < n; ++column) {
213
+ nk_f32_t b_val, c_val;
214
+ nk_f16_to_f32_serial(b + column, &b_val);
215
+ nk_f16_to_f32_serial(c_row + column, &c_val);
216
+ inner_sum += c_val * b_val;
217
+ }
218
+ outer_sum += a_row * inner_sum;
219
+ }
220
+ *result = outer_sum;
221
+ }
222
+
223
+ NK_PUBLIC void nk_mahalanobis_f16_neon(nk_f16_t const *a, nk_f16_t const *b, nk_f16_t const *c, nk_size_t n,
224
+ nk_f32_t *result) {
225
+ nk_f32_t outer_sum = 0;
226
+ for (nk_size_t row = 0; row != n; ++row) {
227
+ nk_f16_t const *c_row = c + row * n;
228
+ nk_f32_t a_row, b_row;
229
+ nk_f16_to_f32_serial(a + row, &a_row);
230
+ nk_f16_to_f32_serial(b + row, &b_row);
231
+ nk_f32_t diff_row = a_row - b_row;
232
+ float32x4_t inner_sum_f32x4 = vdupq_n_f32(0);
233
+ nk_size_t column = 0;
234
+ for (; column + 8 <= n; column += 8) {
235
+ float16x8_t a_f16x8 = vreinterpretq_f16_u16(vld1q_u16((nk_u16_t const *)(a + column)));
236
+ float16x8_t b_f16x8 = vreinterpretq_f16_u16(vld1q_u16((nk_u16_t const *)(b + column)));
237
+ float16x8_t c_f16x8 = vreinterpretq_f16_u16(vld1q_u16((nk_u16_t const *)(c_row + column)));
238
+ float32x4_t a_low_f32x4 = vcvt_f32_f16(vget_low_f16(a_f16x8));
239
+ float32x4_t a_high_f32x4 = vcvt_high_f32_f16(a_f16x8);
240
+ float32x4_t b_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_f16x8));
241
+ float32x4_t b_high_f32x4 = vcvt_high_f32_f16(b_f16x8);
242
+ float32x4_t c_low_f32x4 = vcvt_f32_f16(vget_low_f16(c_f16x8));
243
+ float32x4_t c_high_f32x4 = vcvt_high_f32_f16(c_f16x8);
244
+ float32x4_t diff_low_f32x4 = vsubq_f32(a_low_f32x4, b_low_f32x4);
245
+ float32x4_t diff_high_f32x4 = vsubq_f32(a_high_f32x4, b_high_f32x4);
246
+ inner_sum_f32x4 = vfmaq_f32(inner_sum_f32x4, c_low_f32x4, diff_low_f32x4);
247
+ inner_sum_f32x4 = vfmaq_f32(inner_sum_f32x4, c_high_f32x4, diff_high_f32x4);
248
+ }
249
+ nk_f32_t inner_sum = vaddvq_f32(inner_sum_f32x4);
250
+ for (; column < n; ++column) {
251
+ nk_f32_t a_val, b_val, c_val;
252
+ nk_f16_to_f32_serial(a + column, &a_val);
253
+ nk_f16_to_f32_serial(b + column, &b_val);
254
+ nk_f16_to_f32_serial(c_row + column, &c_val);
255
+ inner_sum += c_val * (a_val - b_val);
256
+ }
257
+ outer_sum += diff_row * inner_sum;
258
+ }
259
+ nk_f32_t quadratic = outer_sum;
260
+ *result = nk_f32_sqrt_neon(quadratic > 0 ? quadratic : 0);
261
+ }
262
+
263
+ NK_PUBLIC void nk_bilinear_f16c_neon(nk_f16c_t const *a_pairs, nk_f16c_t const *b_pairs, nk_f16c_t const *c_pairs,
264
+ nk_size_t n, nk_f32c_t *results) {
265
+ nk_f32_t outer_sum_real = 0;
266
+ nk_f32_t outer_sum_imag = 0;
267
+ for (nk_size_t row = 0; row != n; ++row) {
268
+ nk_f16c_t const *c_row = c_pairs + row * n;
269
+ nk_f32_t a_real, a_imag;
270
+ nk_f16_to_f32_serial(&(a_pairs + row)->real, &a_real);
271
+ nk_f16_to_f32_serial(&(a_pairs + row)->imag, &a_imag);
272
+ float32x4_t inner_sum_real_f32x4 = vdupq_n_f32(0);
273
+ float32x4_t inner_sum_imag_f32x4 = vdupq_n_f32(0);
274
+ nk_size_t column = 0;
275
+ for (; column + 8 <= n; column += 8) {
276
+ int16x8x2_t b_i16x8x2 = vld2q_s16((short const *)(b_pairs + column));
277
+ int16x8x2_t c_i16x8x2 = vld2q_s16((short const *)(c_row + column));
278
+ float16x8_t b_real_f16x8 = vreinterpretq_f16_s16(b_i16x8x2.val[0]);
279
+ float16x8_t b_imag_f16x8 = vreinterpretq_f16_s16(b_i16x8x2.val[1]);
280
+ float16x8_t c_real_f16x8 = vreinterpretq_f16_s16(c_i16x8x2.val[0]);
281
+ float16x8_t c_imag_f16x8 = vreinterpretq_f16_s16(c_i16x8x2.val[1]);
282
+ float32x4_t b_real_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_real_f16x8));
283
+ float32x4_t b_real_high_f32x4 = vcvt_high_f32_f16(b_real_f16x8);
284
+ float32x4_t b_imag_low_f32x4 = vcvt_f32_f16(vget_low_f16(b_imag_f16x8));
285
+ float32x4_t b_imag_high_f32x4 = vcvt_high_f32_f16(b_imag_f16x8);
286
+ float32x4_t c_real_low_f32x4 = vcvt_f32_f16(vget_low_f16(c_real_f16x8));
287
+ float32x4_t c_real_high_f32x4 = vcvt_high_f32_f16(c_real_f16x8);
288
+ float32x4_t c_imag_low_f32x4 = vcvt_f32_f16(vget_low_f16(c_imag_f16x8));
289
+ float32x4_t c_imag_high_f32x4 = vcvt_high_f32_f16(c_imag_f16x8);
290
+ inner_sum_real_f32x4 = vfmaq_f32(inner_sum_real_f32x4, c_real_low_f32x4, b_real_low_f32x4);
291
+ inner_sum_real_f32x4 = vfmsq_f32(inner_sum_real_f32x4, c_imag_low_f32x4, b_imag_low_f32x4);
292
+ inner_sum_real_f32x4 = vfmaq_f32(inner_sum_real_f32x4, c_real_high_f32x4, b_real_high_f32x4);
293
+ inner_sum_real_f32x4 = vfmsq_f32(inner_sum_real_f32x4, c_imag_high_f32x4, b_imag_high_f32x4);
294
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_real_low_f32x4, b_imag_low_f32x4);
295
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_imag_low_f32x4, b_real_low_f32x4);
296
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_real_high_f32x4, b_imag_high_f32x4);
297
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_imag_high_f32x4, b_real_high_f32x4);
298
+ }
299
+ nk_f32_t inner_sum_real = vaddvq_f32(inner_sum_real_f32x4);
300
+ nk_f32_t inner_sum_imag = vaddvq_f32(inner_sum_imag_f32x4);
301
+ for (; column < n; ++column) {
302
+ nk_f32_t b_real, b_imag, c_real, c_imag;
303
+ nk_f16_to_f32_serial(&(b_pairs + column)->real, &b_real);
304
+ nk_f16_to_f32_serial(&(b_pairs + column)->imag, &b_imag);
305
+ nk_f16_to_f32_serial(&(c_row + column)->real, &c_real);
306
+ nk_f16_to_f32_serial(&(c_row + column)->imag, &c_imag);
307
+ inner_sum_real += c_real * b_real - c_imag * b_imag;
308
+ inner_sum_imag += c_real * b_imag + c_imag * b_real;
309
+ }
310
+ outer_sum_real += a_real * inner_sum_real - a_imag * inner_sum_imag;
311
+ outer_sum_imag += a_real * inner_sum_imag + a_imag * inner_sum_real;
312
+ }
313
+ results->real = outer_sum_real;
314
+ results->imag = outer_sum_imag;
315
+ }
316
+
317
+ #if defined(__clang__)
318
+ #pragma clang attribute pop
319
+ #elif defined(__GNUC__)
320
+ #pragma GCC pop_options
321
+ #endif
322
+
323
+ #if defined(__cplusplus)
324
+ } // extern "C"
325
+ #endif
326
+
327
+ #endif // NK_TARGET_NEON
328
+ #endif // NK_TARGET_ARM64_
329
+ #endif // NK_CURVED_NEON_H
@@ -0,0 +1,211 @@
1
+ /**
2
+ * @brief SIMD-accelerated Curved Space Similarity for NEON BF16.
3
+ * @file include/numkong/curved/neonbfdot.h
4
+ * @author Ash Vardanian
5
+ * @date January 14, 2026
6
+ *
7
+ * @sa include/numkong/curved.h
8
+ *
9
+ * Implements bf16 bilinear forms and Mahalanobis distance using ARM NEON with BF16 extensions.
10
+ *
11
+ * @section curved_neonbfdot_instructions ARM NEON BF16 Instructions (ARMv8.6-BF16)
12
+ *
13
+ * Intrinsic Instruction A76 M5
14
+ * vbfdotq_f32 BFDOT (V.4S, V.8H, V.8H) 3cy @ 2p 2cy @ 1p
15
+ * vcvt_f32_bf16 BFCVTN (V.4H, V.4S) 3cy @ 2p 3cy @ 4p
16
+ * vld1q_bf16 LD1 (V.8H) 4cy @ 2p 4cy @ 3p
17
+ * vaddvq_f32 FADDP+FADDP (V.4S) 5cy @ 1p 8cy @ 1p
18
+ * vfmaq_f32 FMLA (V.4S, V.4S, V.4S) 4cy @ 2p 3cy @ 4p
19
+ *
20
+ * For bilinear forms, BFDOT enables efficient inner-product computation by processing 8 bf16
21
+ * pairs into 4 f32 results per instruction. For Mahalanobis distance, bf16 inputs are converted
22
+ * to f32 for subtraction, then accumulated using FMA for numerical stability.
23
+ */
24
+ #ifndef NK_CURVED_NEONBFDOT_H
25
+ #define NK_CURVED_NEONBFDOT_H
26
+
27
+ #if NK_TARGET_ARM64_
28
+ #if NK_TARGET_NEONBFDOT
29
+
30
+ #include "numkong/types.h" // `nk_bf16_t`
31
+ #include "numkong/spatial/neon.h" // `nk_f32_sqrt_neon`
32
+ #include "numkong/cast/serial.h" // `nk_bf16_to_f32_serial`
33
+
34
+ #if defined(__cplusplus)
35
+ extern "C" {
36
+ #endif
37
+
38
+ #if defined(__clang__)
39
+ #pragma clang attribute push(__attribute__((target("arch=armv8.6-a+simd+bf16"))), apply_to = function)
40
+ #elif defined(__GNUC__)
41
+ #pragma GCC push_options
42
+ #pragma GCC target("arch=armv8.6-a+simd+bf16")
43
+ #endif
44
+
45
+ NK_PUBLIC void nk_bilinear_bf16_neonbfdot(nk_bf16_t const *a, nk_bf16_t const *b, nk_bf16_t const *c, nk_size_t n,
46
+ nk_f32_t *result) {
47
+ float32x4_t outer_sum_f32x4 = vdupq_n_f32(0);
48
+
49
+ for (nk_size_t i = 0; i != n; ++i) {
50
+ // Load a[i] and broadcast to f32
51
+ nk_f32_t a_i_f32;
52
+ nk_bf16_to_f32_serial(a + i, &a_i_f32);
53
+ float32x4_t a_i_f32x4 = vdupq_n_f32(a_i_f32);
54
+
55
+ // Inner sum
56
+ float32x4_t inner_sum_f32x4 = vdupq_n_f32(0);
57
+ nk_size_t j = 0;
58
+
59
+ // Process 8 elements at a time using BFDOT
60
+ for (; j + 8 <= n; j += 8) {
61
+ bfloat16x8_t b_bf16x8 = vld1q_bf16((nk_bf16_for_arm_simd_t const *)(b + j));
62
+ bfloat16x8_t c_bf16x8 = vld1q_bf16((nk_bf16_for_arm_simd_t const *)(c + i * n + j));
63
+ inner_sum_f32x4 = vbfdotq_f32(inner_sum_f32x4, c_bf16x8, b_bf16x8);
64
+ }
65
+
66
+ // Handle tail elements (less than 8)
67
+ if (j < n) {
68
+ nk_b128_vec_t b_vec, c_vec;
69
+ nk_partial_load_b16x8_serial_(b + j, &b_vec, n - j);
70
+ nk_partial_load_b16x8_serial_(c + i * n + j, &c_vec, n - j);
71
+ bfloat16x8_t b_bf16x8 = vreinterpretq_bf16_u16(b_vec.u16x8);
72
+ bfloat16x8_t c_bf16x8 = vreinterpretq_bf16_u16(c_vec.u16x8);
73
+ inner_sum_f32x4 = vbfdotq_f32(inner_sum_f32x4, c_bf16x8, b_bf16x8);
74
+ }
75
+
76
+ // Accumulate: outer_sum += a[i] * inner_sum
77
+ outer_sum_f32x4 = vfmaq_f32(outer_sum_f32x4, a_i_f32x4, inner_sum_f32x4);
78
+ }
79
+
80
+ *result = vaddvq_f32(outer_sum_f32x4);
81
+ }
82
+
83
+ NK_PUBLIC void nk_mahalanobis_bf16_neonbfdot(nk_bf16_t const *a, nk_bf16_t const *b, nk_bf16_t const *c, nk_size_t n,
84
+ nk_f32_t *result) {
85
+ nk_f32_t outer_sum = 0;
86
+
87
+ for (nk_size_t i = 0; i != n; ++i) {
88
+ // Compute diff_i = a[i] - b[i] in f32
89
+ nk_f32_t a_i_f32, b_i_f32;
90
+ nk_bf16_to_f32_serial(a + i, &a_i_f32);
91
+ nk_bf16_to_f32_serial(b + i, &b_i_f32);
92
+ nk_f32_t diff_i = a_i_f32 - b_i_f32;
93
+
94
+ // Inner sum
95
+ float32x4_t inner_sum_f32x4 = vdupq_n_f32(0);
96
+ nk_size_t j = 0;
97
+
98
+ // Process 4 elements at a time (convert bf16->f32, subtract, then FMA)
99
+ for (; j + 4 <= n; j += 4) {
100
+ bfloat16x4_t a_j_bf16x4 = vld1_bf16((nk_bf16_for_arm_simd_t const *)(a + j));
101
+ bfloat16x4_t b_j_bf16x4 = vld1_bf16((nk_bf16_for_arm_simd_t const *)(b + j));
102
+ bfloat16x4_t c_bf16x4 = vld1_bf16((nk_bf16_for_arm_simd_t const *)(c + i * n + j));
103
+
104
+ float32x4_t a_j_f32x4 = vcvt_f32_bf16(a_j_bf16x4);
105
+ float32x4_t b_j_f32x4 = vcvt_f32_bf16(b_j_bf16x4);
106
+ float32x4_t c_f32x4 = vcvt_f32_bf16(c_bf16x4);
107
+
108
+ float32x4_t diff_j_f32x4 = vsubq_f32(a_j_f32x4, b_j_f32x4);
109
+ inner_sum_f32x4 = vfmaq_f32(inner_sum_f32x4, c_f32x4, diff_j_f32x4);
110
+ }
111
+
112
+ // Handle tail elements
113
+ nk_f32_t inner_sum_tail = 0;
114
+ for (; j < n; ++j) {
115
+ nk_f32_t a_j_f32, b_j_f32, c_f32;
116
+ nk_bf16_to_f32_serial(a + j, &a_j_f32);
117
+ nk_bf16_to_f32_serial(b + j, &b_j_f32);
118
+ nk_bf16_to_f32_serial(c + i * n + j, &c_f32);
119
+ inner_sum_tail += c_f32 * (a_j_f32 - b_j_f32);
120
+ }
121
+
122
+ // Reduce inner sum and add tail
123
+ nk_f32_t inner_sum = vaddvq_f32(inner_sum_f32x4) + inner_sum_tail;
124
+
125
+ // Accumulate: outer_sum += diff_i * inner_sum
126
+ outer_sum += diff_i * inner_sum;
127
+ }
128
+
129
+ nk_f32_t quadratic = outer_sum;
130
+ *result = nk_f32_sqrt_neon(quadratic > 0 ? quadratic : 0);
131
+ }
132
+
133
+ NK_PUBLIC void nk_bilinear_bf16c_neonbfdot(nk_bf16c_t const *a_pairs, nk_bf16c_t const *b_pairs,
134
+ nk_bf16c_t const *c_pairs, nk_size_t n, nk_f32c_t *result) {
135
+ // ARMv8.3-A FCMLA was benchmarked for this complex multiply pattern.
136
+ // The deinterleave+4FMA approach is 2.3x faster on Apple M4 — see `dot/neon.h` comment.
137
+ nk_f32_t outer_sum_real = 0;
138
+ nk_f32_t outer_sum_imag = 0;
139
+
140
+ for (nk_size_t i = 0; i != n; ++i) {
141
+ // Load a[i] as complex (real, imag) and convert to f32
142
+ nk_f32_t a_real, a_imag;
143
+ nk_bf16_to_f32_serial(&a_pairs[i].real, &a_real);
144
+ nk_bf16_to_f32_serial(&a_pairs[i].imag, &a_imag);
145
+
146
+ // Inner sums for real and imaginary parts of c[i,j] * b[j]
147
+ float32x4_t inner_sum_real_f32x4 = vdupq_n_f32(0);
148
+ float32x4_t inner_sum_imag_f32x4 = vdupq_n_f32(0);
149
+ nk_size_t j = 0;
150
+
151
+ // Process 4 complex pairs at a time
152
+ for (; j + 4 <= n; j += 4) {
153
+ // Deinterleave load: separate real and imaginary parts
154
+ // MSVC doesn't support vld2_bf16, so load as s16 and reinterpret
155
+ int16x4x2_t b_i16x4x2 = vld2_s16((short const *)(b_pairs + j));
156
+ int16x4x2_t c_i16x4x2 = vld2_s16((short const *)(c_pairs + i * n + j));
157
+
158
+ float32x4_t b_real_f32x4 = vcvt_f32_bf16(vreinterpret_bf16_s16(b_i16x4x2.val[0]));
159
+ float32x4_t b_imag_f32x4 = vcvt_f32_bf16(vreinterpret_bf16_s16(b_i16x4x2.val[1]));
160
+ float32x4_t c_real_f32x4 = vcvt_f32_bf16(vreinterpret_bf16_s16(c_i16x4x2.val[0]));
161
+ float32x4_t c_imag_f32x4 = vcvt_f32_bf16(vreinterpret_bf16_s16(c_i16x4x2.val[1]));
162
+
163
+ // Complex multiply: c * b = (c_real*b_real - c_imag*b_imag) + (c_real*b_imag + c_imag*b_real)*i
164
+ // Real part: c_real*b_real - c_imag*b_imag
165
+ inner_sum_real_f32x4 = vfmaq_f32(inner_sum_real_f32x4, c_real_f32x4, b_real_f32x4);
166
+ inner_sum_real_f32x4 = vfmsq_f32(inner_sum_real_f32x4, c_imag_f32x4, b_imag_f32x4);
167
+ // Imaginary part: c_real*b_imag + c_imag*b_real
168
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_real_f32x4, b_imag_f32x4);
169
+ inner_sum_imag_f32x4 = vfmaq_f32(inner_sum_imag_f32x4, c_imag_f32x4, b_real_f32x4);
170
+ }
171
+
172
+ // Handle tail elements
173
+ nk_f32_t inner_sum_real_tail = 0, inner_sum_imag_tail = 0;
174
+ for (; j < n; ++j) {
175
+ nk_f32_t b_real, b_imag, c_real, c_imag;
176
+ nk_bf16_to_f32_serial(&b_pairs[j].real, &b_real);
177
+ nk_bf16_to_f32_serial(&b_pairs[j].imag, &b_imag);
178
+ nk_bf16_to_f32_serial(&c_pairs[i * n + j].real, &c_real);
179
+ nk_bf16_to_f32_serial(&c_pairs[i * n + j].imag, &c_imag);
180
+ // Complex multiply: c * b
181
+ inner_sum_real_tail += c_real * b_real - c_imag * b_imag;
182
+ inner_sum_imag_tail += c_real * b_imag + c_imag * b_real;
183
+ }
184
+
185
+ // Reduce inner sums
186
+ nk_f32_t inner_sum_real = vaddvq_f32(inner_sum_real_f32x4) + inner_sum_real_tail;
187
+ nk_f32_t inner_sum_imag = vaddvq_f32(inner_sum_imag_f32x4) + inner_sum_imag_tail;
188
+
189
+ // Complex multiply: a * inner_sum = (a_real*inner_real - a_imag*inner_imag) + (a_real*inner_imag +
190
+ // a_imag*inner_real)*i
191
+ outer_sum_real += a_real * inner_sum_real - a_imag * inner_sum_imag;
192
+ outer_sum_imag += a_real * inner_sum_imag + a_imag * inner_sum_real;
193
+ }
194
+
195
+ result->real = outer_sum_real;
196
+ result->imag = outer_sum_imag;
197
+ }
198
+
199
+ #if defined(__clang__)
200
+ #pragma clang attribute pop
201
+ #elif defined(__GNUC__)
202
+ #pragma GCC pop_options
203
+ #endif
204
+
205
+ #if defined(__cplusplus)
206
+ } // extern "C"
207
+ #endif
208
+
209
+ #endif // NK_TARGET_NEONBFDOT
210
+ #endif // NK_TARGET_ARM64_
211
+ #endif // NK_CURVED_NEONBFDOT_H