usearch 2.21.4 → 2.25.1

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 (604) hide show
  1. package/README.md +65 -69
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +8 -8
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +124 -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/README.md +1 -1
  16. package/javascript/dist/cjs/usearch.d.ts +8 -3
  17. package/javascript/dist/cjs/usearch.js +12 -4
  18. package/javascript/dist/esm/usearch.d.ts +8 -3
  19. package/javascript/dist/esm/usearch.js +12 -4
  20. package/javascript/lib.cpp +17 -0
  21. package/javascript/usearch.test.js +117 -94
  22. package/javascript/usearch.ts +14 -6
  23. package/numkong/.clang-tidy +30 -0
  24. package/{simsimd → numkong}/.cmake-format.py +18 -0
  25. package/numkong/.editorconfig +12 -0
  26. package/numkong/.git-blame-ignore-revs +123 -0
  27. package/numkong/.mailmap +15 -0
  28. package/numkong/.spi.yml +11 -0
  29. package/numkong/.swift-format +14 -0
  30. package/numkong/CITATION.cff +81 -0
  31. package/numkong/CONTRIBUTING.md +500 -0
  32. package/numkong/Cargo.toml +54 -0
  33. package/numkong/MANIFEST.in +8 -0
  34. package/numkong/Package.swift +84 -0
  35. package/numkong/README.md +569 -0
  36. package/numkong/bench/README.md +217 -0
  37. package/numkong/bench/bench-browser.html +323 -0
  38. package/numkong/bench/bench.cpp +344 -0
  39. package/numkong/bench/bench.hpp +816 -0
  40. package/numkong/bench/bench.js +148 -0
  41. package/numkong/bench/bench.mjs +616 -0
  42. package/numkong/bench/bench_attention.cpp +113 -0
  43. package/numkong/bench/bench_cast.cpp +264 -0
  44. package/numkong/bench/bench_cross_amx.cpp +62 -0
  45. package/numkong/bench/bench_cross_arm.cpp +245 -0
  46. package/numkong/bench/bench_cross_blas.cpp +309 -0
  47. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  48. package/numkong/bench/bench_cross_power.cpp +84 -0
  49. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  50. package/numkong/bench/bench_cross_serial.cpp +151 -0
  51. package/numkong/bench/bench_cross_sme.cpp +163 -0
  52. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  53. package/numkong/bench/bench_cross_x86.cpp +455 -0
  54. package/numkong/bench/bench_curved.cpp +210 -0
  55. package/numkong/bench/bench_dot.cpp +315 -0
  56. package/numkong/bench/bench_each.cpp +503 -0
  57. package/numkong/bench/bench_geospatial.cpp +112 -0
  58. package/numkong/bench/bench_maxsim.cpp +154 -0
  59. package/numkong/bench/bench_mesh.cpp +148 -0
  60. package/numkong/bench/bench_probability.cpp +60 -0
  61. package/numkong/bench/bench_reduce.cpp +371 -0
  62. package/numkong/bench/bench_set.cpp +73 -0
  63. package/numkong/bench/bench_sparse.cpp +198 -0
  64. package/numkong/bench/bench_spatial.cpp +347 -0
  65. package/numkong/bench/bench_trigonometry.cpp +137 -0
  66. package/numkong/bench/swift/Bench.swift +506 -0
  67. package/numkong/binding.gyp +169 -0
  68. package/numkong/build.rs +484 -0
  69. package/numkong/c/dispatch.h +512 -0
  70. package/numkong/c/dispatch_bf16.c +447 -0
  71. package/numkong/c/dispatch_bf16c.c +52 -0
  72. package/numkong/c/dispatch_e2m3.c +296 -0
  73. package/numkong/c/dispatch_e3m2.c +284 -0
  74. package/numkong/c/dispatch_e4m3.c +318 -0
  75. package/numkong/c/dispatch_e5m2.c +316 -0
  76. package/numkong/c/dispatch_f16.c +452 -0
  77. package/numkong/c/dispatch_f16c.c +58 -0
  78. package/numkong/c/dispatch_f32.c +434 -0
  79. package/numkong/c/dispatch_f32c.c +99 -0
  80. package/numkong/c/dispatch_f64.c +348 -0
  81. package/numkong/c/dispatch_f64c.c +98 -0
  82. package/numkong/c/dispatch_i16.c +96 -0
  83. package/numkong/c/dispatch_i32.c +89 -0
  84. package/numkong/c/dispatch_i4.c +153 -0
  85. package/numkong/c/dispatch_i64.c +86 -0
  86. package/numkong/c/dispatch_i8.c +348 -0
  87. package/numkong/c/dispatch_other.c +348 -0
  88. package/numkong/c/dispatch_u1.c +193 -0
  89. package/numkong/c/dispatch_u16.c +124 -0
  90. package/numkong/c/dispatch_u32.c +118 -0
  91. package/numkong/c/dispatch_u4.c +153 -0
  92. package/numkong/c/dispatch_u64.c +102 -0
  93. package/numkong/c/dispatch_u8.c +364 -0
  94. package/numkong/c/numkong.c +940 -0
  95. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  96. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  97. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_math_lib.cmake +46 -0
  99. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  100. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  101. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  102. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  103. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  104. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  105. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  106. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  107. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  108. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  109. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  110. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  111. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  112. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  113. package/numkong/include/README.md +643 -0
  114. package/numkong/include/module.modulemap +129 -0
  115. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  116. package/numkong/include/numkong/attention/sme.h +2075 -0
  117. package/numkong/include/numkong/attention.h +49 -0
  118. package/numkong/include/numkong/capabilities.h +930 -0
  119. package/numkong/include/numkong/cast/README.md +262 -0
  120. package/numkong/include/numkong/cast/diamond.h +64 -0
  121. package/numkong/include/numkong/cast/haswell.h +823 -0
  122. package/numkong/include/numkong/cast/icelake.h +475 -0
  123. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  124. package/numkong/include/numkong/cast/neon.h +1174 -0
  125. package/numkong/include/numkong/cast/powervsx.h +449 -0
  126. package/numkong/include/numkong/cast/rvv.h +970 -0
  127. package/numkong/include/numkong/cast/sapphire.h +262 -0
  128. package/numkong/include/numkong/cast/serial.h +2336 -0
  129. package/numkong/include/numkong/cast/skylake.h +915 -0
  130. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  131. package/numkong/include/numkong/cast.h +254 -0
  132. package/numkong/include/numkong/cast.hpp +44 -0
  133. package/numkong/include/numkong/curved/README.md +223 -0
  134. package/numkong/include/numkong/curved/genoa.h +182 -0
  135. package/numkong/include/numkong/curved/haswell.h +276 -0
  136. package/numkong/include/numkong/curved/neon.h +329 -0
  137. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  138. package/numkong/include/numkong/curved/rvv.h +305 -0
  139. package/numkong/include/numkong/curved/serial.h +207 -0
  140. package/numkong/include/numkong/curved/skylake.h +457 -0
  141. package/numkong/include/numkong/curved/smef64.h +530 -0
  142. package/numkong/include/numkong/curved.h +513 -0
  143. package/numkong/include/numkong/curved.hpp +144 -0
  144. package/numkong/include/numkong/dot/README.md +442 -0
  145. package/numkong/include/numkong/dot/alder.h +563 -0
  146. package/numkong/include/numkong/dot/diamond.h +158 -0
  147. package/numkong/include/numkong/dot/genoa.h +289 -0
  148. package/numkong/include/numkong/dot/haswell.h +1723 -0
  149. package/numkong/include/numkong/dot/icelake.h +997 -0
  150. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  151. package/numkong/include/numkong/dot/neon.h +869 -0
  152. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  153. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  154. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  155. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  156. package/numkong/include/numkong/dot/powervsx.h +752 -0
  157. package/numkong/include/numkong/dot/rvv.h +722 -0
  158. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  159. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  160. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  161. package/numkong/include/numkong/dot/sapphire.h +141 -0
  162. package/numkong/include/numkong/dot/serial.h +874 -0
  163. package/numkong/include/numkong/dot/sierra.h +405 -0
  164. package/numkong/include/numkong/dot/skylake.h +1116 -0
  165. package/numkong/include/numkong/dot/sve.h +420 -0
  166. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  167. package/numkong/include/numkong/dot/svehalf.h +168 -0
  168. package/numkong/include/numkong/dot/svesdot.h +90 -0
  169. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  170. package/numkong/include/numkong/dot.h +1136 -0
  171. package/numkong/include/numkong/dot.hpp +94 -0
  172. package/numkong/include/numkong/dots/README.md +496 -0
  173. package/numkong/include/numkong/dots/alder.h +117 -0
  174. package/numkong/include/numkong/dots/diamond.h +86 -0
  175. package/numkong/include/numkong/dots/genoa.h +100 -0
  176. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  177. package/numkong/include/numkong/dots/haswell.h +310 -0
  178. package/numkong/include/numkong/dots/icelake.h +180 -0
  179. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  180. package/numkong/include/numkong/dots/neon.h +123 -0
  181. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  182. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  183. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  184. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  185. package/numkong/include/numkong/dots/powervsx.h +194 -0
  186. package/numkong/include/numkong/dots/rvv.h +2593 -0
  187. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  188. package/numkong/include/numkong/dots/serial.h +2867 -0
  189. package/numkong/include/numkong/dots/sierra.h +100 -0
  190. package/numkong/include/numkong/dots/skylake.h +207 -0
  191. package/numkong/include/numkong/dots/sme.h +5041 -0
  192. package/numkong/include/numkong/dots/smebi32.h +477 -0
  193. package/numkong/include/numkong/dots/smef64.h +1328 -0
  194. package/numkong/include/numkong/dots/smehalf.h +47 -0
  195. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  196. package/numkong/include/numkong/dots.h +3110 -0
  197. package/numkong/include/numkong/dots.hpp +641 -0
  198. package/numkong/include/numkong/each/README.md +465 -0
  199. package/numkong/include/numkong/each/haswell.h +1658 -0
  200. package/numkong/include/numkong/each/icelake.h +272 -0
  201. package/numkong/include/numkong/each/neon.h +1138 -0
  202. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  203. package/numkong/include/numkong/each/neonhalf.h +318 -0
  204. package/numkong/include/numkong/each/rvv.h +1121 -0
  205. package/numkong/include/numkong/each/sapphire.h +343 -0
  206. package/numkong/include/numkong/each/serial.h +282 -0
  207. package/numkong/include/numkong/each/skylake.h +1562 -0
  208. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  209. package/numkong/include/numkong/each.h +2232 -0
  210. package/numkong/include/numkong/each.hpp +434 -0
  211. package/numkong/include/numkong/geospatial/README.md +147 -0
  212. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  213. package/numkong/include/numkong/geospatial/neon.h +615 -0
  214. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  215. package/numkong/include/numkong/geospatial/serial.h +309 -0
  216. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  217. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  218. package/numkong/include/numkong/geospatial.h +453 -0
  219. package/numkong/include/numkong/geospatial.hpp +235 -0
  220. package/numkong/include/numkong/matrix.hpp +336 -0
  221. package/numkong/include/numkong/maxsim/README.md +187 -0
  222. package/numkong/include/numkong/maxsim/alder.h +511 -0
  223. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  224. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  225. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  226. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  227. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  228. package/numkong/include/numkong/maxsim/serial.h +490 -0
  229. package/numkong/include/numkong/maxsim/sme.h +993 -0
  230. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  231. package/numkong/include/numkong/maxsim.h +571 -0
  232. package/numkong/include/numkong/maxsim.hpp +133 -0
  233. package/numkong/include/numkong/mesh/README.md +219 -0
  234. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  235. package/numkong/include/numkong/mesh/neon.h +1821 -0
  236. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  237. package/numkong/include/numkong/mesh/rvv.h +936 -0
  238. package/numkong/include/numkong/mesh/serial.h +721 -0
  239. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  240. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  241. package/numkong/include/numkong/mesh.h +666 -0
  242. package/numkong/include/numkong/mesh.hpp +701 -0
  243. package/numkong/include/numkong/numkong.h +78 -0
  244. package/numkong/include/numkong/numkong.hpp +58 -0
  245. package/numkong/include/numkong/probability/README.md +177 -0
  246. package/numkong/include/numkong/probability/haswell.h +267 -0
  247. package/numkong/include/numkong/probability/neon.h +255 -0
  248. package/numkong/include/numkong/probability/rvv.h +414 -0
  249. package/numkong/include/numkong/probability/serial.h +181 -0
  250. package/numkong/include/numkong/probability/skylake.h +324 -0
  251. package/numkong/include/numkong/probability.h +380 -0
  252. package/numkong/include/numkong/probability.hpp +120 -0
  253. package/numkong/include/numkong/random.h +50 -0
  254. package/numkong/include/numkong/random.hpp +285 -0
  255. package/numkong/include/numkong/reduce/README.md +552 -0
  256. package/numkong/include/numkong/reduce/alder.h +636 -0
  257. package/numkong/include/numkong/reduce/genoa.h +201 -0
  258. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  259. package/numkong/include/numkong/reduce/icelake.h +549 -0
  260. package/numkong/include/numkong/reduce/neon.h +3980 -0
  261. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  262. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  263. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  264. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  265. package/numkong/include/numkong/reduce/serial.h +753 -0
  266. package/numkong/include/numkong/reduce/sierra.h +338 -0
  267. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  268. package/numkong/include/numkong/reduce/sve.h +52 -0
  269. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  270. package/numkong/include/numkong/reduce.h +1582 -0
  271. package/numkong/include/numkong/reduce.hpp +778 -0
  272. package/numkong/include/numkong/scalar/README.md +89 -0
  273. package/numkong/include/numkong/scalar/haswell.h +122 -0
  274. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  275. package/numkong/include/numkong/scalar/neon.h +122 -0
  276. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  277. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  278. package/numkong/include/numkong/scalar/rvv.h +211 -0
  279. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  280. package/numkong/include/numkong/scalar/serial.h +332 -0
  281. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  282. package/numkong/include/numkong/scalar.h +696 -0
  283. package/numkong/include/numkong/set/README.md +179 -0
  284. package/numkong/include/numkong/set/haswell.h +334 -0
  285. package/numkong/include/numkong/set/icelake.h +485 -0
  286. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  287. package/numkong/include/numkong/set/neon.h +363 -0
  288. package/numkong/include/numkong/set/powervsx.h +326 -0
  289. package/numkong/include/numkong/set/rvv.h +226 -0
  290. package/numkong/include/numkong/set/rvvbb.h +117 -0
  291. package/numkong/include/numkong/set/serial.h +174 -0
  292. package/numkong/include/numkong/set/sve.h +187 -0
  293. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  294. package/numkong/include/numkong/set.h +471 -0
  295. package/numkong/include/numkong/set.hpp +114 -0
  296. package/numkong/include/numkong/sets/README.md +149 -0
  297. package/numkong/include/numkong/sets/haswell.h +63 -0
  298. package/numkong/include/numkong/sets/icelake.h +66 -0
  299. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  300. package/numkong/include/numkong/sets/neon.h +61 -0
  301. package/numkong/include/numkong/sets/powervsx.h +65 -0
  302. package/numkong/include/numkong/sets/serial.h +43 -0
  303. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  304. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  305. package/numkong/include/numkong/sets.h +382 -0
  306. package/numkong/include/numkong/sparse/README.md +156 -0
  307. package/numkong/include/numkong/sparse/icelake.h +463 -0
  308. package/numkong/include/numkong/sparse/neon.h +288 -0
  309. package/numkong/include/numkong/sparse/serial.h +117 -0
  310. package/numkong/include/numkong/sparse/sve2.h +504 -0
  311. package/numkong/include/numkong/sparse/turin.h +322 -0
  312. package/numkong/include/numkong/sparse.h +363 -0
  313. package/numkong/include/numkong/sparse.hpp +113 -0
  314. package/numkong/include/numkong/spatial/README.md +439 -0
  315. package/numkong/include/numkong/spatial/alder.h +604 -0
  316. package/numkong/include/numkong/spatial/diamond.h +240 -0
  317. package/numkong/include/numkong/spatial/genoa.h +222 -0
  318. package/numkong/include/numkong/spatial/haswell.h +979 -0
  319. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  320. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  321. package/numkong/include/numkong/spatial/neon.h +846 -0
  322. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  323. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  324. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  325. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  326. package/numkong/include/numkong/spatial/rvv.h +991 -0
  327. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  328. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  329. package/numkong/include/numkong/spatial/serial.h +347 -0
  330. package/numkong/include/numkong/spatial/sierra.h +516 -0
  331. package/numkong/include/numkong/spatial/skylake.h +605 -0
  332. package/numkong/include/numkong/spatial/sve.h +246 -0
  333. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  334. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  335. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  336. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  337. package/numkong/include/numkong/spatial.h +1681 -0
  338. package/numkong/include/numkong/spatial.hpp +183 -0
  339. package/numkong/include/numkong/spatials/README.md +580 -0
  340. package/numkong/include/numkong/spatials/alder.h +94 -0
  341. package/numkong/include/numkong/spatials/diamond.h +82 -0
  342. package/numkong/include/numkong/spatials/genoa.h +94 -0
  343. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  344. package/numkong/include/numkong/spatials/haswell.h +219 -0
  345. package/numkong/include/numkong/spatials/icelake.h +113 -0
  346. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  347. package/numkong/include/numkong/spatials/neon.h +109 -0
  348. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  349. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  350. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  351. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  352. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  353. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  354. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  355. package/numkong/include/numkong/spatials/serial.h +248 -0
  356. package/numkong/include/numkong/spatials/sierra.h +96 -0
  357. package/numkong/include/numkong/spatials/skylake.h +184 -0
  358. package/numkong/include/numkong/spatials/sme.h +1927 -0
  359. package/numkong/include/numkong/spatials/smef64.h +483 -0
  360. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  361. package/numkong/include/numkong/spatials.h +3374 -0
  362. package/numkong/include/numkong/spatials.hpp +509 -0
  363. package/numkong/include/numkong/tensor.hpp +1981 -0
  364. package/numkong/include/numkong/trigonometry/README.md +184 -0
  365. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  366. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  367. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  368. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  369. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  370. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  371. package/numkong/include/numkong/trigonometry.h +467 -0
  372. package/numkong/include/numkong/trigonometry.hpp +166 -0
  373. package/numkong/include/numkong/types.h +1695 -0
  374. package/numkong/include/numkong/types.hpp +6027 -0
  375. package/numkong/include/numkong/vector.hpp +766 -0
  376. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  377. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  378. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  380. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  381. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  382. package/numkong/javascript/README.md +254 -0
  383. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  384. package/numkong/javascript/numkong-browser.ts +40 -0
  385. package/numkong/javascript/numkong-wasm.ts +750 -0
  386. package/numkong/javascript/numkong.c +712 -0
  387. package/numkong/javascript/numkong.ts +613 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  389. package/numkong/javascript/tsconfig-cjs.json +13 -0
  390. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  391. package/numkong/javascript/types.ts +784 -0
  392. package/numkong/numkong.gypi +104 -0
  393. package/numkong/package.json +108 -0
  394. package/numkong/probes/arm_neon.c +8 -0
  395. package/numkong/probes/arm_neon_bfdot.c +9 -0
  396. package/numkong/probes/arm_neon_fhm.c +9 -0
  397. package/numkong/probes/arm_neon_half.c +8 -0
  398. package/numkong/probes/arm_neon_sdot.c +9 -0
  399. package/numkong/probes/arm_neonfp8.c +9 -0
  400. package/numkong/probes/arm_sme.c +16 -0
  401. package/numkong/probes/arm_sme2.c +16 -0
  402. package/numkong/probes/arm_sme2p1.c +16 -0
  403. package/numkong/probes/arm_sme_bf16.c +16 -0
  404. package/numkong/probes/arm_sme_bi32.c +16 -0
  405. package/numkong/probes/arm_sme_f64.c +16 -0
  406. package/numkong/probes/arm_sme_fa64.c +14 -0
  407. package/numkong/probes/arm_sme_half.c +16 -0
  408. package/numkong/probes/arm_sme_lut2.c +15 -0
  409. package/numkong/probes/arm_sve.c +18 -0
  410. package/numkong/probes/arm_sve2.c +20 -0
  411. package/numkong/probes/arm_sve2p1.c +18 -0
  412. package/numkong/probes/arm_sve_bfdot.c +20 -0
  413. package/numkong/probes/arm_sve_half.c +18 -0
  414. package/numkong/probes/arm_sve_sdot.c +21 -0
  415. package/numkong/probes/loongarch_lasx.c +12 -0
  416. package/numkong/probes/power_vsx.c +12 -0
  417. package/numkong/probes/probe.js +127 -0
  418. package/numkong/probes/riscv_rvv.c +14 -0
  419. package/numkong/probes/riscv_rvv_bb.c +15 -0
  420. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  421. package/numkong/probes/riscv_rvv_half.c +14 -0
  422. package/numkong/probes/wasm_v128relaxed.c +11 -0
  423. package/numkong/probes/x86_alder.c +17 -0
  424. package/numkong/probes/x86_diamond.c +17 -0
  425. package/numkong/probes/x86_genoa.c +17 -0
  426. package/numkong/probes/x86_graniteamx.c +19 -0
  427. package/numkong/probes/x86_haswell.c +11 -0
  428. package/numkong/probes/x86_icelake.c +17 -0
  429. package/numkong/probes/x86_sapphire.c +16 -0
  430. package/numkong/probes/x86_sapphireamx.c +18 -0
  431. package/numkong/probes/x86_sierra.c +17 -0
  432. package/numkong/probes/x86_skylake.c +15 -0
  433. package/numkong/probes/x86_turin.c +17 -0
  434. package/numkong/rust/README.md +698 -0
  435. package/numkong/rust/capabilities.rs +96 -0
  436. package/numkong/rust/cast.rs +314 -0
  437. package/numkong/rust/curved.rs +391 -0
  438. package/numkong/rust/each.rs +2721 -0
  439. package/numkong/rust/geospatial.rs +343 -0
  440. package/numkong/rust/matrix.rs +5424 -0
  441. package/numkong/rust/maxsim.rs +540 -0
  442. package/numkong/rust/mesh.rs +837 -0
  443. package/numkong/rust/numkong.rs +409 -0
  444. package/numkong/rust/probability.rs +272 -0
  445. package/numkong/rust/reduce.rs +1249 -0
  446. package/numkong/rust/set.rs +194 -0
  447. package/numkong/rust/sparse.rs +513 -0
  448. package/numkong/rust/spatial.rs +1815 -0
  449. package/numkong/rust/tensor.rs +7565 -0
  450. package/numkong/rust/types.rs +3666 -0
  451. package/numkong/rust/vector.rs +1899 -0
  452. package/numkong/rustfmt.toml +2 -0
  453. package/numkong/swift/Matrix.swift +1388 -0
  454. package/numkong/swift/MaxSim.swift +151 -0
  455. package/numkong/swift/NumKong.swift +283 -0
  456. package/numkong/swift/Numerics.swift +329 -0
  457. package/numkong/swift/README.md +433 -0
  458. package/numkong/swift/Scalar.swift +741 -0
  459. package/numkong/swift/Tensor.swift +209 -0
  460. package/numkong/test/README.md +315 -0
  461. package/numkong/test/playwright.config.ts +16 -0
  462. package/numkong/test/swift/Test.swift +534 -0
  463. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  464. package/numkong/test/test-browser-bundle.html +188 -0
  465. package/numkong/test/test-browser-runner.mjs +43 -0
  466. package/numkong/test/test-browser.html +305 -0
  467. package/numkong/test/test-dtypes.mjs +453 -0
  468. package/numkong/test/test-wasm.mjs +267 -0
  469. package/numkong/test/test.cpp +442 -0
  470. package/numkong/test/test.hpp +580 -0
  471. package/numkong/test/test.mjs +363 -0
  472. package/numkong/test/test_base.py +1041 -0
  473. package/numkong/test/test_cast.cpp +242 -0
  474. package/numkong/test/test_cross.hpp +551 -0
  475. package/numkong/test/test_cross.py +467 -0
  476. package/numkong/test/test_cross_amx.cpp +50 -0
  477. package/numkong/test/test_cross_arm.cpp +225 -0
  478. package/numkong/test/test_cross_blas.cpp +354 -0
  479. package/numkong/test/test_cross_loongarch.cpp +90 -0
  480. package/numkong/test/test_cross_power.cpp +73 -0
  481. package/numkong/test/test_cross_rvv.cpp +109 -0
  482. package/numkong/test/test_cross_serial.cpp +181 -0
  483. package/numkong/test/test_cross_sme.cpp +148 -0
  484. package/numkong/test/test_cross_wasm.cpp +141 -0
  485. package/numkong/test/test_cross_x86.cpp +442 -0
  486. package/numkong/test/test_curved.cpp +182 -0
  487. package/numkong/test/test_curved.py +207 -0
  488. package/numkong/test/test_dot.cpp +353 -0
  489. package/numkong/test/test_dot.py +296 -0
  490. package/numkong/test/test_each.cpp +437 -0
  491. package/numkong/test/test_each.py +756 -0
  492. package/numkong/test/test_geospatial.cpp +155 -0
  493. package/numkong/test/test_geospatial.py +241 -0
  494. package/numkong/test/test_maxsim.cpp +138 -0
  495. package/numkong/test/test_maxsim.py +209 -0
  496. package/numkong/test/test_mesh.cpp +203 -0
  497. package/numkong/test/test_mesh.py +394 -0
  498. package/numkong/test/test_probability.cpp +136 -0
  499. package/numkong/test/test_probability.py +166 -0
  500. package/numkong/test/test_reduce.cpp +563 -0
  501. package/numkong/test/test_reduce.py +781 -0
  502. package/numkong/test/test_set.cpp +151 -0
  503. package/numkong/test/test_set.py +120 -0
  504. package/numkong/test/test_similarities.py +579 -0
  505. package/numkong/test/test_sparse.cpp +121 -0
  506. package/numkong/test/test_sparse.py +118 -0
  507. package/numkong/test/test_spatial.cpp +518 -0
  508. package/numkong/test/test_spatial.py +269 -0
  509. package/numkong/test/test_tensor.cpp +884 -0
  510. package/numkong/test/test_tensor.py +1417 -0
  511. package/numkong/test/test_trigonometry.cpp +156 -0
  512. package/numkong/test/test_trigonometry.py +180 -0
  513. package/package.json +7 -8
  514. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  515. package/prebuilds/linux-arm64/usearch.node +0 -0
  516. package/prebuilds/linux-x64/usearch.node +0 -0
  517. package/fp16/CMakeLists.txt +0 -176
  518. package/fp16/LICENSE +0 -11
  519. package/fp16/README.md +0 -20
  520. package/fp16/bench/alt-element.cc +0 -89
  521. package/fp16/bench/from-alt-array.cc +0 -175
  522. package/fp16/bench/from-ieee-array.cc +0 -399
  523. package/fp16/bench/ieee-element.cc +0 -272
  524. package/fp16/bench/to-alt-array.cc +0 -91
  525. package/fp16/bench/to-ieee-array.cc +0 -303
  526. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  527. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  528. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  529. package/fp16/configure.py +0 -65
  530. package/fp16/confu.yaml +0 -8
  531. package/fp16/include/fp16/__init__.py +0 -0
  532. package/fp16/include/fp16/avx.py +0 -50
  533. package/fp16/include/fp16/avx2.py +0 -53
  534. package/fp16/include/fp16/bitcasts.h +0 -92
  535. package/fp16/include/fp16/fp16.h +0 -451
  536. package/fp16/include/fp16/psimd.h +0 -131
  537. package/fp16/include/fp16.h +0 -11
  538. package/fp16/jni/Android.mk +0 -6
  539. package/fp16/jni/Application.mk +0 -4
  540. package/fp16/test/alt-from-fp32-value.cc +0 -498
  541. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  542. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  543. package/fp16/test/alt-to-fp32-value.cc +0 -348
  544. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  545. package/fp16/test/bitcasts.cc +0 -56
  546. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  547. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  548. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  549. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  550. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  551. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  552. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  553. package/fp16/test/peachpy/stubs.py +0 -38
  554. package/fp16/test/tables.cc +0 -530
  555. package/fp16/test/tables.h +0 -13
  556. package/fp16/third-party/THHalf.h +0 -92
  557. package/fp16/third-party/eigen-half.h +0 -170
  558. package/fp16/third-party/float16-compressor.h +0 -80
  559. package/fp16/third-party/half.hpp +0 -2916
  560. package/fp16/third-party/npy-halffloat.h +0 -328
  561. package/simsimd/.git-blame-ignore-revs +0 -2
  562. package/simsimd/CONTRIBUTING.md +0 -321
  563. package/simsimd/Cargo.toml +0 -49
  564. package/simsimd/MANIFEST.in +0 -4
  565. package/simsimd/Package.swift +0 -28
  566. package/simsimd/README.md +0 -1493
  567. package/simsimd/binding.gyp +0 -35
  568. package/simsimd/build.rs +0 -70
  569. package/simsimd/c/lib.c +0 -374
  570. package/simsimd/include/module.modulemap +0 -4
  571. package/simsimd/include/simsimd/binary.h +0 -486
  572. package/simsimd/include/simsimd/curved.h +0 -1541
  573. package/simsimd/include/simsimd/dot.h +0 -1853
  574. package/simsimd/include/simsimd/elementwise.h +0 -2465
  575. package/simsimd/include/simsimd/geospatial.h +0 -43
  576. package/simsimd/include/simsimd/mesh.h +0 -69
  577. package/simsimd/include/simsimd/probability.h +0 -606
  578. package/simsimd/include/simsimd/simsimd.h +0 -2567
  579. package/simsimd/include/simsimd/sparse.h +0 -1384
  580. package/simsimd/include/simsimd/spatial.h +0 -2335
  581. package/simsimd/include/simsimd/types.h +0 -666
  582. package/simsimd/javascript/fallback.ts +0 -233
  583. package/simsimd/javascript/lib.c +0 -125
  584. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  585. package/simsimd/javascript/simsimd.ts +0 -179
  586. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  587. package/simsimd/package-lock.json +0 -1761
  588. package/simsimd/package.json +0 -61
  589. package/simsimd/rust/lib.rs +0 -1821
  590. package/simsimd/scripts/bench.cxx +0 -1170
  591. package/simsimd/scripts/bench.ipynb +0 -187
  592. package/simsimd/scripts/bench.js +0 -127
  593. package/simsimd/scripts/bench.rs +0 -876
  594. package/simsimd/scripts/bench_vectors.py +0 -734
  595. package/simsimd/scripts/bench_vectors_live.py +0 -213
  596. package/simsimd/scripts/test.c +0 -187
  597. package/simsimd/scripts/test.mjs +0 -220
  598. package/simsimd/scripts/test.py +0 -1505
  599. package/simsimd/swift/SimSIMD.swift +0 -149
  600. package/simsimd/swift/Test.swift +0 -128
  601. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  602. /package/{simsimd → numkong}/LICENSE +0 -0
  603. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  604. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,778 @@
1
+ /**
2
+ * @brief Reduction kernels: reduce_moments (sum + sum-of-squares), reduce_minmax (min + max with indices).
3
+ * @file include/numkong/reduce.hpp
4
+ * @author Ash Vardanian
5
+ * @date February 5, 2026
6
+ */
7
+ #ifndef NK_REDUCE_HPP
8
+ #define NK_REDUCE_HPP
9
+
10
+ #include <cstddef> // `std::byte`, `std::size_t`
11
+ #include <cstdint> // `std::uint32_t`
12
+ #include <memory> // `std::allocator_traits`
13
+ #include <type_traits> // `std::is_same_v`
14
+
15
+ #include "numkong/reduce.h"
16
+
17
+ #include "numkong/types.hpp"
18
+ #include "numkong/vector.hpp"
19
+
20
+ namespace ashvardanian::numkong {
21
+
22
+ /**
23
+ * @brief Compute sum and sum-of-squares in a single pass: sum = Sigma data_i, sumsq = Sigma data_i^2
24
+ * @param[in] data Input array
25
+ * @param[in] count Number of elements
26
+ * @param[in] stride_bytes Stride between elements in bytes (use sizeof(in_type_) for contiguous)
27
+ * @param[out] sum Output sum
28
+ * @param[out] sumsq Output sum of squares
29
+ *
30
+ * @tparam in_type_ Input vector element type
31
+ * @tparam sum_type_ Sum accumulator type, defaults to `in_type_::reduce_moments_sum_t` (often widened)
32
+ * @tparam sumsq_type_ Sum-of-squares accumulator type, defaults to `sum_type_`
33
+ * @tparam allow_simd_ Enable SIMD kernel dispatch when `prefer_simd_k`
34
+ */
35
+ template <numeric_dtype in_type_, numeric_dtype sum_type_ = typename in_type_::reduce_moments_sum_t,
36
+ numeric_dtype sumsq_type_ = typename in_type_::reduce_moments_sumsq_t,
37
+ allow_simd_t allow_simd_ = prefer_simd_k>
38
+ void reduce_moments(in_type_ const *data, std::size_t count, std::size_t stride_bytes, sum_type_ *sum,
39
+ sumsq_type_ *sumsq) noexcept {
40
+ constexpr bool simd = allow_simd_ == prefer_simd_k &&
41
+ std::is_same_v<sum_type_, typename in_type_::reduce_moments_sum_t> &&
42
+ std::is_same_v<sumsq_type_, typename in_type_::reduce_moments_sumsq_t>;
43
+
44
+ if constexpr (std::is_same_v<in_type_, f64_t> && simd)
45
+ nk_reduce_moments_f64(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
46
+ else if constexpr (std::is_same_v<in_type_, f32_t> && simd)
47
+ nk_reduce_moments_f32(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
48
+ else if constexpr (std::is_same_v<in_type_, f16_t> && simd)
49
+ nk_reduce_moments_f16(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
50
+ else if constexpr (std::is_same_v<in_type_, bf16_t> && simd)
51
+ nk_reduce_moments_bf16(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
52
+ else if constexpr (std::is_same_v<in_type_, e4m3_t> && simd)
53
+ nk_reduce_moments_e4m3(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
54
+ else if constexpr (std::is_same_v<in_type_, e5m2_t> && simd)
55
+ nk_reduce_moments_e5m2(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
56
+ else if constexpr (std::is_same_v<in_type_, e2m3_t> && simd)
57
+ nk_reduce_moments_e2m3(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
58
+ else if constexpr (std::is_same_v<in_type_, e3m2_t> && simd)
59
+ nk_reduce_moments_e3m2(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
60
+ else if constexpr (std::is_same_v<in_type_, i4x2_t> && simd)
61
+ nk_reduce_moments_i4(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
62
+ else if constexpr (std::is_same_v<in_type_, u4x2_t> && simd)
63
+ nk_reduce_moments_u4(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
64
+ else if constexpr (std::is_same_v<in_type_, u1x8_t> && simd)
65
+ nk_reduce_moments_u1(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
66
+ else if constexpr (std::is_same_v<in_type_, i8_t> && simd)
67
+ nk_reduce_moments_i8(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
68
+ else if constexpr (std::is_same_v<in_type_, u8_t> && simd)
69
+ nk_reduce_moments_u8(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
70
+ else if constexpr (std::is_same_v<in_type_, i16_t> && simd)
71
+ nk_reduce_moments_i16(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
72
+ else if constexpr (std::is_same_v<in_type_, u16_t> && simd)
73
+ nk_reduce_moments_u16(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
74
+ else if constexpr (std::is_same_v<in_type_, i32_t> && simd)
75
+ nk_reduce_moments_i32(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
76
+ else if constexpr (std::is_same_v<in_type_, u32_t> && simd)
77
+ nk_reduce_moments_u32(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
78
+ else if constexpr (std::is_same_v<in_type_, i64_t> && simd)
79
+ nk_reduce_moments_i64(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
80
+ else if constexpr (std::is_same_v<in_type_, u64_t> && simd)
81
+ nk_reduce_moments_u64(&data->raw_, count, stride_bytes, &sum->raw_, &sumsq->raw_);
82
+ // Scalar fallback
83
+ else {
84
+ sum_type_ running_sum {};
85
+ sumsq_type_ running_sumsq {};
86
+ vector_view<in_type_> values(reinterpret_cast<char const *>(data), count, stride_bytes);
87
+ for (std::size_t i = 0; i < count; ++i) {
88
+ auto val = values[i];
89
+ running_sum = saturating_add(running_sum, val);
90
+ running_sumsq = saturating_fma(val, val, running_sumsq);
91
+ }
92
+ *sum = running_sum;
93
+ *sumsq = running_sumsq;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @brief Find minimum and maximum elements with their indices in a single pass.
99
+ * @param[in] data Input array
100
+ * @param[in] count Number of elements
101
+ * @param[in] stride_bytes Stride between elements in bytes (use sizeof(in_type_) for contiguous)
102
+ * @param[out] min_value Output minimum value
103
+ * @param[out] min_index Output index of minimum value
104
+ * @param[out] max_value Output maximum value
105
+ * @param[out] max_index Output index of maximum value
106
+ *
107
+ * @tparam in_type_ Input vector element type
108
+ * @tparam minmax_type_ Result type for min/max values, defaults to `in_type_::reduce_minmax_value_t`
109
+ * @tparam allow_simd_ Enable SIMD kernel dispatch when `prefer_simd_k`
110
+ */
111
+ template <numeric_dtype in_type_, numeric_dtype minmax_type_ = typename in_type_::reduce_minmax_value_t,
112
+ allow_simd_t allow_simd_ = prefer_simd_k>
113
+ void reduce_minmax(in_type_ const *data, std::size_t count, std::size_t stride_bytes, minmax_type_ *min_value,
114
+ std::size_t *min_index, minmax_type_ *max_value, std::size_t *max_index) noexcept {
115
+ constexpr bool simd = allow_simd_ == prefer_simd_k &&
116
+ std::is_same_v<minmax_type_, typename in_type_::reduce_minmax_value_t>;
117
+ static_assert(sizeof(std::size_t) == sizeof(nk_size_t), "size_t and nk_size_t must have the same width");
118
+ nk_size_t min_offset = 0, max_offset = 0;
119
+
120
+ // For types where minmax_type_ matches the C function output type directly,
121
+ // dispatch to the C kernel and pass raw pointers through.
122
+ if constexpr (std::is_same_v<in_type_, f64_t> && simd)
123
+ nk_reduce_minmax_f64(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
124
+ &max_offset);
125
+ else if constexpr (std::is_same_v<in_type_, f32_t> && simd)
126
+ nk_reduce_minmax_f32(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
127
+ &max_offset);
128
+ else if constexpr (std::is_same_v<in_type_, i8_t> && simd)
129
+ nk_reduce_minmax_i8(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
130
+ &max_offset);
131
+ else if constexpr (std::is_same_v<in_type_, u8_t> && simd)
132
+ nk_reduce_minmax_u8(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
133
+ &max_offset);
134
+ else if constexpr (std::is_same_v<in_type_, i16_t> && simd)
135
+ nk_reduce_minmax_i16(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
136
+ &max_offset);
137
+ else if constexpr (std::is_same_v<in_type_, u16_t> && simd)
138
+ nk_reduce_minmax_u16(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
139
+ &max_offset);
140
+ else if constexpr (std::is_same_v<in_type_, i32_t> && simd)
141
+ nk_reduce_minmax_i32(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
142
+ &max_offset);
143
+ else if constexpr (std::is_same_v<in_type_, u32_t> && simd)
144
+ nk_reduce_minmax_u32(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
145
+ &max_offset);
146
+ else if constexpr (std::is_same_v<in_type_, i64_t> && simd)
147
+ nk_reduce_minmax_i64(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
148
+ &max_offset);
149
+ else if constexpr (std::is_same_v<in_type_, u64_t> && simd)
150
+ nk_reduce_minmax_u64(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
151
+ &max_offset);
152
+ else if constexpr (std::is_same_v<in_type_, e2m3_t> && simd)
153
+ nk_reduce_minmax_e2m3(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
154
+ &max_offset);
155
+ else if constexpr (std::is_same_v<in_type_, e3m2_t> && simd)
156
+ nk_reduce_minmax_e3m2(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
157
+ &max_offset);
158
+ else if constexpr (std::is_same_v<in_type_, f16_t> && simd)
159
+ nk_reduce_minmax_f16(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
160
+ &max_offset);
161
+ else if constexpr (std::is_same_v<in_type_, bf16_t> && simd)
162
+ nk_reduce_minmax_bf16(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
163
+ &max_offset);
164
+ else if constexpr (std::is_same_v<in_type_, e4m3_t> && simd)
165
+ nk_reduce_minmax_e4m3(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
166
+ &max_offset);
167
+ else if constexpr (std::is_same_v<in_type_, e5m2_t> && simd)
168
+ nk_reduce_minmax_e5m2(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
169
+ &max_offset);
170
+ else if constexpr (std::is_same_v<in_type_, i4x2_t> && simd)
171
+ nk_reduce_minmax_i4(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
172
+ &max_offset);
173
+ else if constexpr (std::is_same_v<in_type_, u4x2_t> && simd)
174
+ nk_reduce_minmax_u4(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
175
+ &max_offset);
176
+ else if constexpr (std::is_same_v<in_type_, u1x8_t> && simd)
177
+ nk_reduce_minmax_u1(&data->raw_, count, stride_bytes, &min_value->raw_, &min_offset, &max_value->raw_,
178
+ &max_offset);
179
+ // Scalar fallback
180
+ else {
181
+ minmax_type_ best_min = finite_max<minmax_type_>();
182
+ minmax_type_ best_max = finite_min<minmax_type_>();
183
+ vector_view<in_type_> values(reinterpret_cast<char const *>(data), count, stride_bytes);
184
+ for (nk_size_t i = 0; i < count; ++i) {
185
+ minmax_type_ v = minmax_type_(values[i]);
186
+ if (v < best_min) best_min = v, min_offset = i;
187
+ if (v > best_max) best_max = v, max_offset = i;
188
+ }
189
+ *min_value = best_min, *max_value = best_max;
190
+ }
191
+ if (min_index) *min_index = static_cast<std::size_t>(min_offset);
192
+ if (max_index) *max_index = static_cast<std::size_t>(max_offset);
193
+ }
194
+
195
+ /** @brief Compute sum and sum-of-squares over a vector view. */
196
+ template <numeric_dtype in_type_, numeric_dtype sum_type_ = typename in_type_::reduce_moments_sum_t,
197
+ numeric_dtype sumsq_type_ = typename in_type_::reduce_moments_sumsq_t,
198
+ allow_simd_t allow_simd_ = prefer_simd_k>
199
+ void reduce_moments(vector_view<in_type_> input, sum_type_ *sum, sumsq_type_ *sumsq) noexcept {
200
+ reduce_moments<in_type_, sum_type_, sumsq_type_, allow_simd_>(
201
+ input.data(), input.size(), static_cast<std::size_t>(input.stride_bytes()), sum, sumsq);
202
+ }
203
+
204
+ /** @brief Find minimum and maximum elements with their indices over a vector view. */
205
+ template <numeric_dtype in_type_, numeric_dtype minmax_type_ = typename in_type_::reduce_minmax_value_t,
206
+ allow_simd_t allow_simd_ = prefer_simd_k>
207
+ void reduce_minmax(vector_view<in_type_> input, minmax_type_ *min_value, std::size_t *min_index,
208
+ minmax_type_ *max_value, std::size_t *max_index) noexcept {
209
+ reduce_minmax<in_type_, minmax_type_, allow_simd_>(input.data(), input.size(),
210
+ static_cast<std::size_t>(input.stride_bytes()), min_value,
211
+ min_index, max_value, max_index);
212
+ }
213
+
214
+ } // namespace ashvardanian::numkong
215
+
216
+ #include "numkong/tensor.hpp"
217
+
218
+ namespace ashvardanian::numkong {
219
+
220
+ #pragma region Tensor Reduction Helpers
221
+
222
+ /** @brief Result of detecting how many trailing dimensions form a single arithmetic progression. */
223
+ struct uniform_stride_tail_result_t_ {
224
+ std::size_t tail_dims; ///< Number of collapsible trailing dimensions.
225
+ std::size_t element_count; ///< Product of collapsed extents.
226
+ std::size_t stride_bytes; ///< Absolute stride of the innermost collapsed dimension.
227
+ };
228
+
229
+ /** @brief Detect trailing dimensions where stride[i] == stride[i+1] * extent[i+1].
230
+ * When this holds, the tail is a single strided sequence and can be passed to a SIMD
231
+ * kernel in one call with (element_count, stride_bytes). */
232
+ template <typename value_type_, std::size_t max_rank_>
233
+ uniform_stride_tail_result_t_ uniform_stride_tail_(tensor_view<value_type_, max_rank_> input) noexcept {
234
+ if constexpr (dimensions_per_value<value_type_>() > 1) return {0, 0, 0};
235
+ auto rank = input.rank();
236
+ if (rank == 0) return {0, 1, sizeof(value_type_)};
237
+ std::size_t tail = 1;
238
+ auto innermost_stride = input.stride_bytes(rank - 1);
239
+ auto expected_stride = innermost_stride;
240
+ for (std::size_t i = rank - 1; i > 0; --i) {
241
+ expected_stride *= static_cast<std::ptrdiff_t>(input.extent(i));
242
+ if (input.stride_bytes(i - 1) != expected_stride) break;
243
+ ++tail;
244
+ }
245
+ std::size_t count = 1;
246
+ for (std::size_t i = rank - tail; i < rank; ++i) count *= input.extent(i);
247
+ return {tail, count, static_cast<std::size_t>(innermost_stride < 0 ? -innermost_stride : innermost_stride)};
248
+ }
249
+
250
+ /** @brief Collapse the trailing `tail.tail_dims` dimensions into one, preserving outer dims and strides. */
251
+ template <typename value_type_, std::size_t max_rank_>
252
+ tensor_view<value_type_, max_rank_> collapse_uniform_tail_(tensor_view<value_type_, max_rank_> input,
253
+ uniform_stride_tail_result_t_ const &tail) noexcept {
254
+ shape_storage_<max_rank_> s;
255
+ s.rank = input.rank() - tail.tail_dims + 1;
256
+ for (std::size_t i = 0; i + tail.tail_dims < input.rank(); ++i) {
257
+ s.extents[i] = input.extent(i);
258
+ s.strides[i] = input.stride_bytes(i);
259
+ }
260
+ s.extents[s.rank - 1] = tail.element_count;
261
+ s.strides[s.rank - 1] = input.stride_bytes(input.rank() - 1);
262
+ return {input.byte_data(), s};
263
+ }
264
+
265
+ /** @brief Normalize a fully-collapsed tail for SIMD kernel consumption, handling negative strides. */
266
+ template <typename value_type_, std::size_t max_rank_>
267
+ normalized_rank1_lane_<value_type_, max_rank_> normalize_rank1_lane_from_tail_(
268
+ tensor_view<value_type_, max_rank_> input, uniform_stride_tail_result_t_ const &tail) noexcept {
269
+ normalized_rank1_lane_<value_type_, max_rank_> lane;
270
+ lane.count = tail.element_count;
271
+ lane.stride_bytes = tail.stride_bytes;
272
+ auto innermost_stride = input.stride_bytes(input.rank() - 1);
273
+ if (innermost_stride >= 0) {
274
+ lane.data = input.data();
275
+ lane.reversed = false;
276
+ }
277
+ else {
278
+ lane.data = reinterpret_cast<value_type_ const *>(
279
+ input.byte_data() + static_cast<std::ptrdiff_t>(lane.count - 1) * innermost_stride);
280
+ lane.reversed = true;
281
+ }
282
+ return lane;
283
+ }
284
+
285
+ template <numeric_dtype value_type_, std::size_t max_rank_>
286
+ bool reduce_rank1_moments_(tensor_view<value_type_, max_rank_> input, typename value_type_::reduce_moments_sum_t &sum,
287
+ typename value_type_::reduce_moments_sumsq_t &sumsq) noexcept {
288
+ using sum_t = typename value_type_::reduce_moments_sum_t;
289
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
290
+ if (input.rank() != 1 || !tensor_layout_supported_(input) || input.byte_data() == nullptr) return false;
291
+ if (can_reduce_rank1_with_kernel_(input)) {
292
+ auto lane = normalize_rank1_lane_(input);
293
+ numkong::reduce_moments<value_type_>(lane.data, lane.count, lane.stride_bytes, &sum, &sumsq);
294
+ return true;
295
+ }
296
+ auto values = input.as_vector();
297
+ sum = sum_t {};
298
+ sumsq = sumsq_t {};
299
+ for (std::size_t i = 0; i < values.size(); ++i) {
300
+ auto value = values[i];
301
+ sum = saturating_add(sum, value);
302
+ sumsq = saturating_fma(value, value, sumsq);
303
+ }
304
+ return true;
305
+ }
306
+
307
+ template <numeric_dtype value_type_, std::size_t max_rank_>
308
+ bool reduce_rank1_minmax_(tensor_view<value_type_, max_rank_> input,
309
+ minmax_result<typename value_type_::reduce_minmax_value_t> &result) noexcept {
310
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
311
+ if (input.rank() != 1 || !tensor_layout_supported_(input) || input.byte_data() == nullptr) return false;
312
+ if (can_reduce_rank1_with_kernel_(input)) {
313
+ auto lane = normalize_rank1_lane_(input);
314
+ numkong::reduce_minmax<value_type_>(lane.data, lane.count, lane.stride_bytes, &result.min_value,
315
+ &result.min_index, &result.max_value, &result.max_index);
316
+ if (lane.reversed) {
317
+ result.min_index = lane.count - 1 - result.min_index;
318
+ result.max_index = lane.count - 1 - result.max_index;
319
+ }
320
+ return true;
321
+ }
322
+ auto values = input.as_vector();
323
+ result.min_value = finite_max<minmax_t>();
324
+ result.max_value = finite_min<minmax_t>();
325
+ result.min_index = 0;
326
+ result.max_index = 0;
327
+ for (std::size_t i = 0; i < values.size(); ++i) {
328
+ minmax_t value = minmax_t(values[i]);
329
+ if (value < result.min_value) result.min_value = value, result.min_index = i;
330
+ if (value > result.max_value) result.max_value = value, result.max_index = i;
331
+ }
332
+ return true;
333
+ }
334
+
335
+ template <numeric_dtype value_type_, std::size_t max_rank_>
336
+ bool accumulate_moments_tensor_(tensor_view<value_type_, max_rank_> input,
337
+ tensor_span<typename value_type_::reduce_moments_sum_t, max_rank_> sums,
338
+ tensor_span<typename value_type_::reduce_moments_sumsq_t, max_rank_> sumsqs) noexcept {
339
+ using sum_t = typename value_type_::reduce_moments_sum_t;
340
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
341
+ if (!tensor_layout_supported_(input) || !shapes_match_out_(input, sums) || !shapes_match_out_(input, sumsqs))
342
+ return false;
343
+ if (input.rank() == 1) {
344
+ auto src = input.as_vector();
345
+ auto dst_sum = sums.as_vector();
346
+ auto dst_sumsq = sumsqs.as_vector();
347
+ for (std::size_t i = 0; i < src.size(); ++i) {
348
+ auto value = src[i];
349
+ dst_sum[i] = saturating_add(dst_sum[i], sum_t(value));
350
+ dst_sumsq[i] = saturating_fma(value, value, sumsq_t(dst_sumsq[i]));
351
+ }
352
+ return true;
353
+ }
354
+ for (std::size_t i = 0; i < input.extent(0); ++i) {
355
+ if (!accumulate_moments_tensor_(input.slice_leading(i), sums.slice_leading(i), sumsqs.slice_leading(i)))
356
+ return false;
357
+ }
358
+ return true;
359
+ }
360
+
361
+ template <numeric_dtype value_type_, std::size_t max_rank_>
362
+ bool update_minmax_tensor_(tensor_view<value_type_, max_rank_> input,
363
+ tensor_span<typename value_type_::reduce_minmax_value_t, max_rank_> mins,
364
+ tensor_span<typename value_type_::reduce_minmax_value_t, max_rank_> maxs) noexcept {
365
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
366
+ if (!tensor_layout_supported_(input) || !shapes_match_out_(input, mins) || !shapes_match_out_(input, maxs))
367
+ return false;
368
+ if (input.rank() == 1) {
369
+ auto src = input.as_vector();
370
+ auto dst_min = mins.as_vector();
371
+ auto dst_max = maxs.as_vector();
372
+ for (std::size_t i = 0; i < src.size(); ++i) {
373
+ minmax_t value = minmax_t(src[i]);
374
+ if (value < dst_min[i]) dst_min[i] = value;
375
+ if (value > dst_max[i]) dst_max[i] = value;
376
+ }
377
+ return true;
378
+ }
379
+ for (std::size_t i = 0; i < input.extent(0); ++i) {
380
+ if (!update_minmax_tensor_(input.slice_leading(i), mins.slice_leading(i), maxs.slice_leading(i))) return false;
381
+ }
382
+ return true;
383
+ }
384
+
385
+ template <numeric_dtype value_type_, std::size_t max_rank_>
386
+ bool reduce_moments_axis_(tensor_view<value_type_, max_rank_> input, std::size_t axis,
387
+ typename value_type_::reduce_moments_sum_t *sums,
388
+ typename value_type_::reduce_moments_sumsq_t *sumsqs) noexcept {
389
+ return for_each_axis_lane_(input, axis,
390
+ [&](tensor_view<value_type_, max_rank_> lane, std::size_t output_index) noexcept {
391
+ typename value_type_::reduce_moments_sum_t sum {};
392
+ typename value_type_::reduce_moments_sumsq_t sumsq {};
393
+ if (!reduce_rank1_moments_(lane, sum, sumsq)) return false;
394
+ if (sums) sums[output_index] = sum;
395
+ if (sumsqs) sumsqs[output_index] = sumsq;
396
+ return true;
397
+ });
398
+ }
399
+
400
+ template <numeric_dtype value_type_, std::size_t max_rank_>
401
+ bool reduce_moments_axis_packed_(tensor_view<value_type_, max_rank_> input, std::size_t axis,
402
+ tensor_span<typename value_type_::reduce_moments_sum_t, max_rank_> sums,
403
+ tensor_span<typename value_type_::reduce_moments_sumsq_t, max_rank_> sumsqs,
404
+ keep_dims_t keep_dims) noexcept {
405
+ using sum_t = typename value_type_::reduce_moments_sum_t;
406
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
407
+ if (!tensor_layout_supported_(input) || axis >= input.rank()) return false;
408
+ if (axis == 0) {
409
+ auto sum_target = keep_dims ? sums.slice_leading(0) : sums;
410
+ auto sumsq_target = keep_dims ? sumsqs.slice_leading(0) : sumsqs;
411
+ if (input.rank() == 1) {
412
+ sum_t sum {};
413
+ sumsq_t sumsq {};
414
+ if (!reduce_rank1_moments_(input, sum, sumsq)) return false;
415
+ sum_target.scalar_ref() = sum;
416
+ sumsq_target.scalar_ref() = sumsq;
417
+ return true;
418
+ }
419
+ for (std::size_t i = 0; i < input.extent(0); ++i)
420
+ if (!accumulate_moments_tensor_(input.slice_leading(i), sum_target, sumsq_target)) return false;
421
+ return true;
422
+ }
423
+ if (input.rank() == 1) return false;
424
+ for (std::size_t i = 0; i < input.extent(0); ++i)
425
+ if (!reduce_moments_axis_packed_(input.slice_leading(i), axis - 1, sums.slice_leading(i),
426
+ sumsqs.slice_leading(i), keep_dims))
427
+ return false;
428
+ return true;
429
+ }
430
+
431
+ template <numeric_dtype value_type_, std::size_t max_rank_>
432
+ bool reduce_minmax_axis_(tensor_view<value_type_, max_rank_> input, std::size_t axis,
433
+ typename value_type_::reduce_minmax_value_t *mins, std::size_t *argmins,
434
+ typename value_type_::reduce_minmax_value_t *maxs, std::size_t *argmaxs) noexcept {
435
+ return for_each_axis_lane_(input, axis,
436
+ [&](tensor_view<value_type_, max_rank_> lane, std::size_t output_index) noexcept {
437
+ minmax_result<typename value_type_::reduce_minmax_value_t> result {};
438
+ if (!reduce_rank1_minmax_(lane, result)) return false;
439
+ if (mins) mins[output_index] = result.min_value;
440
+ if (argmins) argmins[output_index] = result.min_index;
441
+ if (maxs) maxs[output_index] = result.max_value;
442
+ if (argmaxs) argmaxs[output_index] = result.max_index;
443
+ return true;
444
+ });
445
+ }
446
+
447
+ template <numeric_dtype value_type_, std::size_t max_rank_>
448
+ bool reduce_minmax_axis_packed_(tensor_view<value_type_, max_rank_> input, std::size_t axis,
449
+ tensor_span<typename value_type_::reduce_minmax_value_t, max_rank_> mins,
450
+ tensor_span<typename value_type_::reduce_minmax_value_t, max_rank_> maxs,
451
+ keep_dims_t keep_dims) noexcept {
452
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
453
+ if (!tensor_layout_supported_(input) || axis >= input.rank()) return false;
454
+ if (axis == 0) {
455
+ auto min_target = keep_dims ? mins.slice_leading(0) : mins;
456
+ auto max_target = keep_dims ? maxs.slice_leading(0) : maxs;
457
+ if (input.rank() == 1) {
458
+ minmax_result<minmax_t> result {};
459
+ if (!reduce_rank1_minmax_(input, result)) return false;
460
+ min_target.scalar_ref() = result.min_value;
461
+ max_target.scalar_ref() = result.max_value;
462
+ return true;
463
+ }
464
+ for (std::size_t i = 0; i < input.extent(0); ++i)
465
+ if (!update_minmax_tensor_(input.slice_leading(i), min_target, max_target)) return false;
466
+ return true;
467
+ }
468
+ if (input.rank() == 1) return false;
469
+ for (std::size_t i = 0; i < input.extent(0); ++i)
470
+ if (!reduce_minmax_axis_packed_(input.slice_leading(i), axis - 1, mins.slice_leading(i), maxs.slice_leading(i),
471
+ keep_dims))
472
+ return false;
473
+ return true;
474
+ }
475
+
476
+ #pragma endregion Tensor Reduction Helpers
477
+
478
+ #pragma region Scalar Reductions
479
+
480
+ /** @brief Compute Σxᵢ and Σxᵢ² in a single pass. Returns zeroed result for empty tensors. */
481
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
482
+ moments_result<typename value_type_::reduce_moments_sum_t, typename value_type_::reduce_moments_sumsq_t> moments(
483
+ tensor_view<value_type_, max_rank_> input) noexcept {
484
+ using sum_t = typename value_type_::reduce_moments_sum_t;
485
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
486
+ moments_result<sum_t, sumsq_t> result {};
487
+ if (input.empty() || input.numel() == 0 || !tensor_layout_supported_(input)) return result;
488
+ auto tail = uniform_stride_tail_(input);
489
+ if (tail.tail_dims == input.rank()) {
490
+ auto lane = normalize_rank1_lane_from_tail_<value_type_, max_rank_>(input, tail);
491
+ numkong::reduce_moments<value_type_>(lane.data, lane.count, lane.stride_bytes, &result.sum, &result.sumsq);
492
+ return result;
493
+ }
494
+ if (tail.tail_dims >= 2) return moments<value_type_, max_rank_>(collapse_uniform_tail_(input, tail));
495
+ // Sub-byte rank-1 fallback: uniform_stride_tail_ returns {0,0,0} for packed types.
496
+ if (input.rank() == 1) {
497
+ reduce_rank1_moments_(input, result.sum, result.sumsq);
498
+ return result;
499
+ }
500
+ for (std::size_t i = 0; i < input.extent(0); ++i) {
501
+ auto slice_result = moments<value_type_, max_rank_>(input.slice_leading(static_cast<std::ptrdiff_t>(i)));
502
+ result.sum = saturating_add(result.sum, slice_result.sum);
503
+ result.sumsq = saturating_add(result.sumsq, slice_result.sumsq);
504
+ }
505
+ return result;
506
+ }
507
+
508
+ /** @brief Find min and max values with their flat indices. */
509
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
510
+ minmax_result<typename value_type_::reduce_minmax_value_t> minmax(tensor_view<value_type_, max_rank_> input) noexcept {
511
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
512
+ minmax_result<minmax_t> result {};
513
+ if (input.empty() || input.numel() == 0 || !tensor_layout_supported_(input)) return result;
514
+ auto tail = uniform_stride_tail_(input);
515
+ if (tail.tail_dims == input.rank()) {
516
+ auto lane = normalize_rank1_lane_from_tail_<value_type_, max_rank_>(input, tail);
517
+ numkong::reduce_minmax<value_type_>(lane.data, lane.count, lane.stride_bytes, &result.min_value,
518
+ &result.min_index, &result.max_value, &result.max_index);
519
+ if (lane.reversed) {
520
+ result.min_index = tail.element_count - 1 - result.min_index;
521
+ result.max_index = tail.element_count - 1 - result.max_index;
522
+ }
523
+ return result;
524
+ }
525
+ if (tail.tail_dims >= 2) return minmax<value_type_, max_rank_>(collapse_uniform_tail_(input, tail));
526
+ // Sub-byte rank-1 fallback.
527
+ if (input.rank() == 1) {
528
+ reduce_rank1_minmax_(input, result);
529
+ return result;
530
+ }
531
+ result.min_value = finite_max<minmax_t>();
532
+ result.max_value = finite_min<minmax_t>();
533
+ std::size_t base = 0;
534
+ for (std::size_t i = 0; i < input.extent(0); ++i) {
535
+ auto slice = input.slice_leading(static_cast<std::ptrdiff_t>(i));
536
+ auto slice_result = minmax<value_type_, max_rank_>(slice);
537
+ if (slice_result.min_value < result.min_value) {
538
+ result.min_value = slice_result.min_value;
539
+ result.min_index = base + slice_result.min_index;
540
+ }
541
+ if (slice_result.max_value > result.max_value) {
542
+ result.max_value = slice_result.max_value;
543
+ result.max_index = base + slice_result.max_index;
544
+ }
545
+ base += slice.numel();
546
+ }
547
+ return result;
548
+ }
549
+
550
+ /** @brief Σ of all elements. */
551
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
552
+ typename value_type_::reduce_moments_sum_t sum(tensor_view<value_type_, max_rank_> input) noexcept {
553
+ return moments(input).sum;
554
+ }
555
+
556
+ /** @brief Find the minimum element value. */
557
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
558
+ typename value_type_::reduce_minmax_value_t min(tensor_view<value_type_, max_rank_> input) noexcept {
559
+ return minmax(input).min_value;
560
+ }
561
+
562
+ /** @brief Find the maximum element value. */
563
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
564
+ typename value_type_::reduce_minmax_value_t max(tensor_view<value_type_, max_rank_> input) noexcept {
565
+ return minmax(input).max_value;
566
+ }
567
+
568
+ /** @brief Index of the minimum element (flat). */
569
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
570
+ std::size_t argmin(tensor_view<value_type_, max_rank_> input) noexcept {
571
+ return minmax(input).min_index;
572
+ }
573
+
574
+ /** @brief Index of the maximum element (flat). */
575
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8>
576
+ std::size_t argmax(tensor_view<value_type_, max_rank_> input) noexcept {
577
+ return minmax(input).max_index;
578
+ }
579
+
580
+ /** @brief Compute Σxᵢ and Σxᵢ² over a vector view. */
581
+ template <numeric_dtype value_type_>
582
+ moments_result<typename value_type_::reduce_moments_sum_t, typename value_type_::reduce_moments_sumsq_t> moments(
583
+ vector_view<value_type_> input) noexcept {
584
+ using sum_t = typename value_type_::reduce_moments_sum_t;
585
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
586
+ moments_result<sum_t, sumsq_t> result {};
587
+ if (input.size() == 0) return result;
588
+ reduce_moments<value_type_>(input, &result.sum, &result.sumsq);
589
+ return result;
590
+ }
591
+
592
+ /** @brief Find min and max values with their indices over a vector view. */
593
+ template <numeric_dtype value_type_>
594
+ minmax_result<typename value_type_::reduce_minmax_value_t> minmax(vector_view<value_type_> input) noexcept {
595
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
596
+ minmax_result<minmax_t> result {};
597
+ if (input.size() == 0) return result;
598
+ reduce_minmax<value_type_>(input, &result.min_value, &result.min_index, &result.max_value, &result.max_index);
599
+ return result;
600
+ }
601
+
602
+ /** @brief Σ of all elements in a vector view. */
603
+ template <numeric_dtype value_type_>
604
+ typename value_type_::reduce_moments_sum_t sum(vector_view<value_type_> input) noexcept {
605
+ return moments(input).sum;
606
+ }
607
+
608
+ /** @brief Find the minimum element value in a vector view. */
609
+ template <numeric_dtype value_type_>
610
+ typename value_type_::reduce_minmax_value_t min(vector_view<value_type_> input) noexcept {
611
+ return minmax(input).min_value;
612
+ }
613
+
614
+ /** @brief Find the maximum element value in a vector view. */
615
+ template <numeric_dtype value_type_>
616
+ typename value_type_::reduce_minmax_value_t max(vector_view<value_type_> input) noexcept {
617
+ return minmax(input).max_value;
618
+ }
619
+
620
+ /** @brief Index of the minimum element in a vector view. */
621
+ template <numeric_dtype value_type_>
622
+ std::size_t argmin(vector_view<value_type_> input) noexcept {
623
+ return minmax(input).min_index;
624
+ }
625
+
626
+ /** @brief Index of the maximum element in a vector view. */
627
+ template <numeric_dtype value_type_>
628
+ std::size_t argmax(vector_view<value_type_> input) noexcept {
629
+ return minmax(input).max_index;
630
+ }
631
+
632
+ #pragma endregion Scalar Reductions
633
+
634
+ #pragma region Axis Reductions
635
+
636
+ /** @brief Σ along a single axis. Returns empty tensor on failure. */
637
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
638
+ typename allocator_type_ = aligned_allocator<typename value_type_::reduce_moments_sum_t>>
639
+ tensor<typename value_type_::reduce_moments_sum_t, allocator_type_, max_rank_> try_sum(
640
+ tensor_view<value_type_, max_rank_> input, std::size_t axis, keep_dims_t keep_dims = collapse_dims_k) noexcept {
641
+ using sum_t = typename value_type_::reduce_moments_sum_t;
642
+ using sum_tensor_t = tensor<sum_t, allocator_type_, max_rank_>;
643
+
644
+ if (input.empty() || axis >= input.rank() || !tensor_layout_supported_(input)) return sum_tensor_t {};
645
+
646
+ auto out_shape = reduced_shape_<sum_t>(input.shape(), axis, keep_dims);
647
+ auto sums = sum_tensor_t::try_zeros(out_shape.extents, out_shape.rank);
648
+ if (sums.empty() || !shape_matches_(out_shape, sums.span())) return sum_tensor_t {};
649
+ if constexpr (dimensions_per_value<value_type_>() > 1) {
650
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
651
+ using sumsq_alloc_t = typename std::allocator_traits<allocator_type_>::template rebind_alloc<sumsq_t>;
652
+ using sumsq_tensor_t = tensor<sumsq_t, sumsq_alloc_t, max_rank_>;
653
+ auto scratch = sumsq_tensor_t::try_zeros(out_shape.extents, out_shape.rank);
654
+ if (scratch.empty() || !shape_matches_(reduced_shape_<sumsq_t>(input.shape(), axis, keep_dims), scratch.span()))
655
+ return sum_tensor_t {};
656
+ if (!reduce_moments_axis_packed_(input, axis, sums.span(), scratch.span(), keep_dims)) return sum_tensor_t {};
657
+ }
658
+ else if (!reduce_moments_axis_(input, axis, sums.data(), nullptr)) return sum_tensor_t {};
659
+ return sums;
660
+ }
661
+
662
+ /** @brief Moments along an axis (Σxᵢ and Σxᵢ² per slice). */
663
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
664
+ typename allocator_type_ = aligned_allocator<typename value_type_::reduce_moments_sum_t>>
665
+ moments_result<tensor<typename value_type_::reduce_moments_sum_t, allocator_type_, max_rank_>,
666
+ tensor<typename value_type_::reduce_moments_sumsq_t,
667
+ typename std::allocator_traits<allocator_type_>::template rebind_alloc<
668
+ typename value_type_::reduce_moments_sumsq_t>,
669
+ max_rank_>>
670
+ try_moments(tensor_view<value_type_, max_rank_> input, std::size_t axis,
671
+ keep_dims_t keep_dims = collapse_dims_k) noexcept {
672
+ using sum_t = typename value_type_::reduce_moments_sum_t;
673
+ using sumsq_t = typename value_type_::reduce_moments_sumsq_t;
674
+ using sum_tensor_t = tensor<sum_t, allocator_type_, max_rank_>;
675
+ using sumsq_alloc_t = typename std::allocator_traits<allocator_type_>::template rebind_alloc<sumsq_t>;
676
+ using sumsq_tensor_t = tensor<sumsq_t, sumsq_alloc_t, max_rank_>;
677
+
678
+ if (input.empty() || axis >= input.rank() || !tensor_layout_supported_(input))
679
+ return {sum_tensor_t {}, sumsq_tensor_t {}};
680
+
681
+ auto out_shape_sum = reduced_shape_<sum_t>(input.shape(), axis, keep_dims);
682
+ auto out_shape_sq = reduced_shape_<sumsq_t>(input.shape(), axis, keep_dims);
683
+
684
+ auto sums = sum_tensor_t::try_zeros(out_shape_sum.extents, out_shape_sum.rank);
685
+ auto sumsqs = sumsq_tensor_t::try_zeros(out_shape_sq.extents, out_shape_sq.rank);
686
+ if (sums.empty() || sumsqs.empty() || !shape_matches_(out_shape_sum, sums.span()) ||
687
+ !shape_matches_(out_shape_sq, sumsqs.span()))
688
+ return {sum_tensor_t {}, sumsq_tensor_t {}};
689
+
690
+ if constexpr (dimensions_per_value<value_type_>() > 1) {
691
+ if (!reduce_moments_axis_packed_(input, axis, sums.span(), sumsqs.span(), keep_dims))
692
+ return {sum_tensor_t {}, sumsq_tensor_t {}};
693
+ }
694
+ else if (!reduce_moments_axis_(input, axis, sums.data(), sumsqs.data()))
695
+ return {sum_tensor_t {}, sumsq_tensor_t {}};
696
+
697
+ return {std::move(sums), std::move(sumsqs)};
698
+ }
699
+
700
+ /** @brief Min and max along an axis. */
701
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
702
+ typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
703
+ minmax_result<tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_>> try_minmax(
704
+ tensor_view<value_type_, max_rank_> input, std::size_t axis, keep_dims_t keep_dims = collapse_dims_k) noexcept {
705
+ using minmax_t = typename value_type_::reduce_minmax_value_t;
706
+ using out_tensor_t = tensor<minmax_t, allocator_type_, max_rank_>;
707
+ if (input.empty() || axis >= input.rank() || !tensor_layout_supported_(input))
708
+ return {out_tensor_t {}, 0, out_tensor_t {}, 0};
709
+
710
+ auto out_shape = reduced_shape_<minmax_t>(input.shape(), axis, keep_dims);
711
+ auto mins = out_tensor_t::try_full(out_shape.extents, out_shape.rank, finite_max<minmax_t>());
712
+ auto maxs = out_tensor_t::try_full(out_shape.extents, out_shape.rank, finite_min<minmax_t>());
713
+ if (mins.empty() || maxs.empty() || !shape_matches_(out_shape, mins.span()) ||
714
+ !shape_matches_(out_shape, maxs.span()))
715
+ return {out_tensor_t {}, 0, out_tensor_t {}, 0};
716
+
717
+ if constexpr (dimensions_per_value<value_type_>() > 1) {
718
+ if (!reduce_minmax_axis_packed_(input, axis, mins.span(), maxs.span(), keep_dims))
719
+ return {out_tensor_t {}, 0, out_tensor_t {}, 0};
720
+ }
721
+ else if (!reduce_minmax_axis_(input, axis, mins.data(), nullptr, maxs.data(), nullptr))
722
+ return {out_tensor_t {}, 0, out_tensor_t {}, 0};
723
+ return {std::move(mins), 0, std::move(maxs), 0};
724
+ }
725
+
726
+ /** @brief Argmin along an axis. */
727
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
728
+ typename allocator_type_ = aligned_allocator<std::size_t>>
729
+ tensor<std::size_t, allocator_type_, max_rank_> try_argmin(tensor_view<value_type_, max_rank_> input, std::size_t axis,
730
+ keep_dims_t keep_dims = collapse_dims_k) noexcept {
731
+ using out_tensor_t = tensor<std::size_t, allocator_type_, max_rank_>;
732
+ if (input.empty() || axis >= input.rank() || !tensor_layout_supported_(input)) return out_tensor_t {};
733
+ if constexpr (dimensions_per_value<value_type_>() > 1) return out_tensor_t {};
734
+
735
+ auto out_shape = reduced_shape_<std::size_t>(input.shape(), axis, keep_dims);
736
+ auto indices = out_tensor_t::try_zeros(out_shape.extents, out_shape.rank);
737
+ if (indices.empty() || !shape_matches_(out_shape, indices.span())) return out_tensor_t {};
738
+ if (!reduce_minmax_axis_(input, axis, nullptr, indices.data(), nullptr, nullptr)) return out_tensor_t {};
739
+ return indices;
740
+ }
741
+
742
+ /** @brief Argmax along an axis. */
743
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
744
+ typename allocator_type_ = aligned_allocator<std::size_t>>
745
+ tensor<std::size_t, allocator_type_, max_rank_> try_argmax(tensor_view<value_type_, max_rank_> input, std::size_t axis,
746
+ keep_dims_t keep_dims = collapse_dims_k) noexcept {
747
+ using out_tensor_t = tensor<std::size_t, allocator_type_, max_rank_>;
748
+ if (input.empty() || axis >= input.rank() || !tensor_layout_supported_(input)) return out_tensor_t {};
749
+ if constexpr (dimensions_per_value<value_type_>() > 1) return out_tensor_t {};
750
+
751
+ auto out_shape = reduced_shape_<std::size_t>(input.shape(), axis, keep_dims);
752
+ auto indices = out_tensor_t::try_zeros(out_shape.extents, out_shape.rank);
753
+ if (indices.empty() || !shape_matches_(out_shape, indices.span())) return out_tensor_t {};
754
+ if (!reduce_minmax_axis_(input, axis, nullptr, nullptr, nullptr, indices.data())) return out_tensor_t {};
755
+ return indices;
756
+ }
757
+
758
+ /** @brief Min along an axis. */
759
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
760
+ typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
761
+ tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_> try_min(
762
+ tensor_view<value_type_, max_rank_> input, std::size_t axis, keep_dims_t keep_dims = collapse_dims_k) noexcept {
763
+ return try_minmax<value_type_, max_rank_, allocator_type_>(input, axis, keep_dims).min_value;
764
+ }
765
+
766
+ /** @brief Max along an axis. */
767
+ template <numeric_dtype value_type_, std::size_t max_rank_ = 8,
768
+ typename allocator_type_ = aligned_allocator<typename value_type_::reduce_minmax_value_t>>
769
+ tensor<typename value_type_::reduce_minmax_value_t, allocator_type_, max_rank_> try_max(
770
+ tensor_view<value_type_, max_rank_> input, std::size_t axis, keep_dims_t keep_dims = collapse_dims_k) noexcept {
771
+ return try_minmax<value_type_, max_rank_, allocator_type_>(input, axis, keep_dims).max_value;
772
+ }
773
+
774
+ #pragma endregion Axis Reductions
775
+
776
+ } // namespace ashvardanian::numkong
777
+
778
+ #endif // NK_REDUCE_HPP