usearch 2.21.4 → 2.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (602) hide show
  1. package/README.md +24 -24
  2. package/binding.gyp +15 -37
  3. package/c/CMakeLists.txt +4 -5
  4. package/c/README.md +7 -7
  5. package/c/lib.cpp +32 -14
  6. package/c/test.c +83 -1
  7. package/c/usearch.h +24 -3
  8. package/cpp/CMakeLists.txt +4 -0
  9. package/cpp/README.md +5 -5
  10. package/cpp/bench.cpp +97 -116
  11. package/cpp/test.cpp +117 -20
  12. package/include/usearch/index.hpp +224 -48
  13. package/include/usearch/index_dense.hpp +105 -40
  14. package/include/usearch/index_plugins.hpp +1387 -204
  15. package/javascript/dist/cjs/usearch.d.ts +8 -3
  16. package/javascript/dist/cjs/usearch.js +12 -4
  17. package/javascript/dist/esm/usearch.d.ts +8 -3
  18. package/javascript/dist/esm/usearch.js +12 -4
  19. package/javascript/lib.cpp +17 -0
  20. package/javascript/usearch.ts +14 -6
  21. package/numkong/.clang-tidy +30 -0
  22. package/{simsimd → numkong}/.cmake-format.py +18 -0
  23. package/numkong/.editorconfig +12 -0
  24. package/numkong/.git-blame-ignore-revs +123 -0
  25. package/numkong/.mailmap +15 -0
  26. package/numkong/.spi.yml +11 -0
  27. package/numkong/.swift-format +14 -0
  28. package/numkong/CITATION.cff +81 -0
  29. package/numkong/CONTRIBUTING.md +500 -0
  30. package/numkong/Cargo.toml +54 -0
  31. package/numkong/MANIFEST.in +8 -0
  32. package/numkong/Package.swift +84 -0
  33. package/numkong/README.md +569 -0
  34. package/numkong/bench/README.md +217 -0
  35. package/numkong/bench/bench-browser.html +323 -0
  36. package/numkong/bench/bench.cpp +344 -0
  37. package/numkong/bench/bench.hpp +816 -0
  38. package/numkong/bench/bench.js +148 -0
  39. package/numkong/bench/bench.mjs +616 -0
  40. package/numkong/bench/bench_attention.cpp +113 -0
  41. package/numkong/bench/bench_cast.cpp +264 -0
  42. package/numkong/bench/bench_cross_amx.cpp +62 -0
  43. package/numkong/bench/bench_cross_arm.cpp +245 -0
  44. package/numkong/bench/bench_cross_blas.cpp +309 -0
  45. package/numkong/bench/bench_cross_loongarch.cpp +106 -0
  46. package/numkong/bench/bench_cross_power.cpp +84 -0
  47. package/numkong/bench/bench_cross_rvv.cpp +121 -0
  48. package/numkong/bench/bench_cross_serial.cpp +151 -0
  49. package/numkong/bench/bench_cross_sme.cpp +163 -0
  50. package/numkong/bench/bench_cross_wasm.cpp +142 -0
  51. package/numkong/bench/bench_cross_x86.cpp +455 -0
  52. package/numkong/bench/bench_curved.cpp +210 -0
  53. package/numkong/bench/bench_dot.cpp +315 -0
  54. package/numkong/bench/bench_each.cpp +503 -0
  55. package/numkong/bench/bench_geospatial.cpp +112 -0
  56. package/numkong/bench/bench_maxsim.cpp +154 -0
  57. package/numkong/bench/bench_mesh.cpp +148 -0
  58. package/numkong/bench/bench_probability.cpp +60 -0
  59. package/numkong/bench/bench_reduce.cpp +371 -0
  60. package/numkong/bench/bench_set.cpp +73 -0
  61. package/numkong/bench/bench_sparse.cpp +198 -0
  62. package/numkong/bench/bench_spatial.cpp +347 -0
  63. package/numkong/bench/bench_trigonometry.cpp +137 -0
  64. package/numkong/bench/swift/Bench.swift +506 -0
  65. package/numkong/binding.gyp +169 -0
  66. package/numkong/build.rs +484 -0
  67. package/numkong/c/dispatch.h +512 -0
  68. package/numkong/c/dispatch_bf16.c +447 -0
  69. package/numkong/c/dispatch_bf16c.c +52 -0
  70. package/numkong/c/dispatch_e2m3.c +296 -0
  71. package/numkong/c/dispatch_e3m2.c +284 -0
  72. package/numkong/c/dispatch_e4m3.c +318 -0
  73. package/numkong/c/dispatch_e5m2.c +316 -0
  74. package/numkong/c/dispatch_f16.c +452 -0
  75. package/numkong/c/dispatch_f16c.c +58 -0
  76. package/numkong/c/dispatch_f32.c +434 -0
  77. package/numkong/c/dispatch_f32c.c +99 -0
  78. package/numkong/c/dispatch_f64.c +348 -0
  79. package/numkong/c/dispatch_f64c.c +98 -0
  80. package/numkong/c/dispatch_i16.c +96 -0
  81. package/numkong/c/dispatch_i32.c +89 -0
  82. package/numkong/c/dispatch_i4.c +153 -0
  83. package/numkong/c/dispatch_i64.c +86 -0
  84. package/numkong/c/dispatch_i8.c +348 -0
  85. package/numkong/c/dispatch_other.c +348 -0
  86. package/numkong/c/dispatch_u1.c +193 -0
  87. package/numkong/c/dispatch_u16.c +124 -0
  88. package/numkong/c/dispatch_u32.c +118 -0
  89. package/numkong/c/dispatch_u4.c +153 -0
  90. package/numkong/c/dispatch_u64.c +102 -0
  91. package/numkong/c/dispatch_u8.c +364 -0
  92. package/numkong/c/numkong.c +940 -0
  93. package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
  94. package/numkong/cmake/nk_isa_probe.cmake +90 -0
  95. package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
  96. package/numkong/cmake/nk_math_lib.cmake +46 -0
  97. package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
  98. package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
  99. package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
  100. package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
  101. package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
  102. package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
  103. package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
  104. package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
  105. package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
  106. package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
  107. package/numkong/cmake/toolchain-wasi.cmake +77 -0
  108. package/numkong/cmake/toolchain-wasm.cmake +70 -0
  109. package/numkong/cmake/toolchain-wasm64.cmake +62 -0
  110. package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
  111. package/numkong/include/README.md +643 -0
  112. package/numkong/include/module.modulemap +129 -0
  113. package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
  114. package/numkong/include/numkong/attention/sme.h +2075 -0
  115. package/numkong/include/numkong/attention.h +49 -0
  116. package/numkong/include/numkong/capabilities.h +930 -0
  117. package/numkong/include/numkong/cast/README.md +262 -0
  118. package/numkong/include/numkong/cast/diamond.h +64 -0
  119. package/numkong/include/numkong/cast/haswell.h +823 -0
  120. package/numkong/include/numkong/cast/icelake.h +475 -0
  121. package/numkong/include/numkong/cast/loongsonasx.h +252 -0
  122. package/numkong/include/numkong/cast/neon.h +1174 -0
  123. package/numkong/include/numkong/cast/powervsx.h +449 -0
  124. package/numkong/include/numkong/cast/rvv.h +970 -0
  125. package/numkong/include/numkong/cast/sapphire.h +262 -0
  126. package/numkong/include/numkong/cast/serial.h +2336 -0
  127. package/numkong/include/numkong/cast/skylake.h +915 -0
  128. package/numkong/include/numkong/cast/v128relaxed.h +537 -0
  129. package/numkong/include/numkong/cast.h +254 -0
  130. package/numkong/include/numkong/cast.hpp +44 -0
  131. package/numkong/include/numkong/curved/README.md +223 -0
  132. package/numkong/include/numkong/curved/genoa.h +182 -0
  133. package/numkong/include/numkong/curved/haswell.h +276 -0
  134. package/numkong/include/numkong/curved/neon.h +329 -0
  135. package/numkong/include/numkong/curved/neonbfdot.h +211 -0
  136. package/numkong/include/numkong/curved/rvv.h +305 -0
  137. package/numkong/include/numkong/curved/serial.h +207 -0
  138. package/numkong/include/numkong/curved/skylake.h +457 -0
  139. package/numkong/include/numkong/curved/smef64.h +530 -0
  140. package/numkong/include/numkong/curved.h +513 -0
  141. package/numkong/include/numkong/curved.hpp +144 -0
  142. package/numkong/include/numkong/dot/README.md +442 -0
  143. package/numkong/include/numkong/dot/alder.h +563 -0
  144. package/numkong/include/numkong/dot/diamond.h +158 -0
  145. package/numkong/include/numkong/dot/genoa.h +289 -0
  146. package/numkong/include/numkong/dot/haswell.h +1723 -0
  147. package/numkong/include/numkong/dot/icelake.h +997 -0
  148. package/numkong/include/numkong/dot/loongsonasx.h +671 -0
  149. package/numkong/include/numkong/dot/neon.h +869 -0
  150. package/numkong/include/numkong/dot/neonbfdot.h +243 -0
  151. package/numkong/include/numkong/dot/neonfhm.h +358 -0
  152. package/numkong/include/numkong/dot/neonfp8.h +323 -0
  153. package/numkong/include/numkong/dot/neonsdot.h +622 -0
  154. package/numkong/include/numkong/dot/powervsx.h +752 -0
  155. package/numkong/include/numkong/dot/rvv.h +722 -0
  156. package/numkong/include/numkong/dot/rvvbb.h +72 -0
  157. package/numkong/include/numkong/dot/rvvbf16.h +123 -0
  158. package/numkong/include/numkong/dot/rvvhalf.h +129 -0
  159. package/numkong/include/numkong/dot/sapphire.h +141 -0
  160. package/numkong/include/numkong/dot/serial.h +874 -0
  161. package/numkong/include/numkong/dot/sierra.h +405 -0
  162. package/numkong/include/numkong/dot/skylake.h +1116 -0
  163. package/numkong/include/numkong/dot/sve.h +420 -0
  164. package/numkong/include/numkong/dot/svebfdot.h +75 -0
  165. package/numkong/include/numkong/dot/svehalf.h +168 -0
  166. package/numkong/include/numkong/dot/svesdot.h +90 -0
  167. package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
  168. package/numkong/include/numkong/dot.h +1136 -0
  169. package/numkong/include/numkong/dot.hpp +94 -0
  170. package/numkong/include/numkong/dots/README.md +496 -0
  171. package/numkong/include/numkong/dots/alder.h +117 -0
  172. package/numkong/include/numkong/dots/diamond.h +86 -0
  173. package/numkong/include/numkong/dots/genoa.h +100 -0
  174. package/numkong/include/numkong/dots/graniteamx.h +733 -0
  175. package/numkong/include/numkong/dots/haswell.h +310 -0
  176. package/numkong/include/numkong/dots/icelake.h +180 -0
  177. package/numkong/include/numkong/dots/loongsonasx.h +176 -0
  178. package/numkong/include/numkong/dots/neon.h +123 -0
  179. package/numkong/include/numkong/dots/neonbfdot.h +59 -0
  180. package/numkong/include/numkong/dots/neonfhm.h +96 -0
  181. package/numkong/include/numkong/dots/neonfp8.h +99 -0
  182. package/numkong/include/numkong/dots/neonsdot.h +144 -0
  183. package/numkong/include/numkong/dots/powervsx.h +194 -0
  184. package/numkong/include/numkong/dots/rvv.h +2593 -0
  185. package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
  186. package/numkong/include/numkong/dots/serial.h +2867 -0
  187. package/numkong/include/numkong/dots/sierra.h +100 -0
  188. package/numkong/include/numkong/dots/skylake.h +207 -0
  189. package/numkong/include/numkong/dots/sme.h +5041 -0
  190. package/numkong/include/numkong/dots/smebi32.h +477 -0
  191. package/numkong/include/numkong/dots/smef64.h +1328 -0
  192. package/numkong/include/numkong/dots/smehalf.h +47 -0
  193. package/numkong/include/numkong/dots/v128relaxed.h +317 -0
  194. package/numkong/include/numkong/dots.h +3110 -0
  195. package/numkong/include/numkong/dots.hpp +641 -0
  196. package/numkong/include/numkong/each/README.md +465 -0
  197. package/numkong/include/numkong/each/haswell.h +1658 -0
  198. package/numkong/include/numkong/each/icelake.h +272 -0
  199. package/numkong/include/numkong/each/neon.h +1138 -0
  200. package/numkong/include/numkong/each/neonbfdot.h +211 -0
  201. package/numkong/include/numkong/each/neonhalf.h +318 -0
  202. package/numkong/include/numkong/each/rvv.h +1121 -0
  203. package/numkong/include/numkong/each/sapphire.h +343 -0
  204. package/numkong/include/numkong/each/serial.h +282 -0
  205. package/numkong/include/numkong/each/skylake.h +1562 -0
  206. package/numkong/include/numkong/each/v128relaxed.h +562 -0
  207. package/numkong/include/numkong/each.h +2232 -0
  208. package/numkong/include/numkong/each.hpp +434 -0
  209. package/numkong/include/numkong/geospatial/README.md +147 -0
  210. package/numkong/include/numkong/geospatial/haswell.h +633 -0
  211. package/numkong/include/numkong/geospatial/neon.h +615 -0
  212. package/numkong/include/numkong/geospatial/rvv.h +701 -0
  213. package/numkong/include/numkong/geospatial/serial.h +309 -0
  214. package/numkong/include/numkong/geospatial/skylake.h +613 -0
  215. package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
  216. package/numkong/include/numkong/geospatial.h +453 -0
  217. package/numkong/include/numkong/geospatial.hpp +235 -0
  218. package/numkong/include/numkong/matrix.hpp +336 -0
  219. package/numkong/include/numkong/maxsim/README.md +187 -0
  220. package/numkong/include/numkong/maxsim/alder.h +511 -0
  221. package/numkong/include/numkong/maxsim/genoa.h +115 -0
  222. package/numkong/include/numkong/maxsim/haswell.h +553 -0
  223. package/numkong/include/numkong/maxsim/icelake.h +480 -0
  224. package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
  225. package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
  226. package/numkong/include/numkong/maxsim/serial.h +490 -0
  227. package/numkong/include/numkong/maxsim/sme.h +993 -0
  228. package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
  229. package/numkong/include/numkong/maxsim.h +571 -0
  230. package/numkong/include/numkong/maxsim.hpp +133 -0
  231. package/numkong/include/numkong/mesh/README.md +219 -0
  232. package/numkong/include/numkong/mesh/haswell.h +2013 -0
  233. package/numkong/include/numkong/mesh/neon.h +1821 -0
  234. package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
  235. package/numkong/include/numkong/mesh/rvv.h +936 -0
  236. package/numkong/include/numkong/mesh/serial.h +721 -0
  237. package/numkong/include/numkong/mesh/skylake.h +2344 -0
  238. package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
  239. package/numkong/include/numkong/mesh.h +666 -0
  240. package/numkong/include/numkong/mesh.hpp +701 -0
  241. package/numkong/include/numkong/numkong.h +78 -0
  242. package/numkong/include/numkong/numkong.hpp +58 -0
  243. package/numkong/include/numkong/probability/README.md +177 -0
  244. package/numkong/include/numkong/probability/haswell.h +267 -0
  245. package/numkong/include/numkong/probability/neon.h +255 -0
  246. package/numkong/include/numkong/probability/rvv.h +414 -0
  247. package/numkong/include/numkong/probability/serial.h +181 -0
  248. package/numkong/include/numkong/probability/skylake.h +324 -0
  249. package/numkong/include/numkong/probability.h +380 -0
  250. package/numkong/include/numkong/probability.hpp +120 -0
  251. package/numkong/include/numkong/random.h +50 -0
  252. package/numkong/include/numkong/random.hpp +285 -0
  253. package/numkong/include/numkong/reduce/README.md +552 -0
  254. package/numkong/include/numkong/reduce/alder.h +636 -0
  255. package/numkong/include/numkong/reduce/genoa.h +201 -0
  256. package/numkong/include/numkong/reduce/haswell.h +3785 -0
  257. package/numkong/include/numkong/reduce/icelake.h +549 -0
  258. package/numkong/include/numkong/reduce/neon.h +3980 -0
  259. package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
  260. package/numkong/include/numkong/reduce/neonfhm.h +293 -0
  261. package/numkong/include/numkong/reduce/neonsdot.h +357 -0
  262. package/numkong/include/numkong/reduce/rvv.h +3533 -0
  263. package/numkong/include/numkong/reduce/serial.h +753 -0
  264. package/numkong/include/numkong/reduce/sierra.h +338 -0
  265. package/numkong/include/numkong/reduce/skylake.h +3794 -0
  266. package/numkong/include/numkong/reduce/sve.h +52 -0
  267. package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
  268. package/numkong/include/numkong/reduce.h +1582 -0
  269. package/numkong/include/numkong/reduce.hpp +778 -0
  270. package/numkong/include/numkong/scalar/README.md +89 -0
  271. package/numkong/include/numkong/scalar/haswell.h +122 -0
  272. package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
  273. package/numkong/include/numkong/scalar/neon.h +122 -0
  274. package/numkong/include/numkong/scalar/neonhalf.h +70 -0
  275. package/numkong/include/numkong/scalar/powervsx.h +96 -0
  276. package/numkong/include/numkong/scalar/rvv.h +211 -0
  277. package/numkong/include/numkong/scalar/sapphire.h +74 -0
  278. package/numkong/include/numkong/scalar/serial.h +332 -0
  279. package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
  280. package/numkong/include/numkong/scalar.h +696 -0
  281. package/numkong/include/numkong/set/README.md +179 -0
  282. package/numkong/include/numkong/set/haswell.h +334 -0
  283. package/numkong/include/numkong/set/icelake.h +485 -0
  284. package/numkong/include/numkong/set/loongsonasx.h +181 -0
  285. package/numkong/include/numkong/set/neon.h +363 -0
  286. package/numkong/include/numkong/set/powervsx.h +326 -0
  287. package/numkong/include/numkong/set/rvv.h +226 -0
  288. package/numkong/include/numkong/set/rvvbb.h +117 -0
  289. package/numkong/include/numkong/set/serial.h +174 -0
  290. package/numkong/include/numkong/set/sve.h +187 -0
  291. package/numkong/include/numkong/set/v128relaxed.h +240 -0
  292. package/numkong/include/numkong/set.h +471 -0
  293. package/numkong/include/numkong/set.hpp +114 -0
  294. package/numkong/include/numkong/sets/README.md +149 -0
  295. package/numkong/include/numkong/sets/haswell.h +63 -0
  296. package/numkong/include/numkong/sets/icelake.h +66 -0
  297. package/numkong/include/numkong/sets/loongsonasx.h +52 -0
  298. package/numkong/include/numkong/sets/neon.h +61 -0
  299. package/numkong/include/numkong/sets/powervsx.h +65 -0
  300. package/numkong/include/numkong/sets/serial.h +43 -0
  301. package/numkong/include/numkong/sets/smebi32.h +1135 -0
  302. package/numkong/include/numkong/sets/v128relaxed.h +58 -0
  303. package/numkong/include/numkong/sets.h +382 -0
  304. package/numkong/include/numkong/sparse/README.md +156 -0
  305. package/numkong/include/numkong/sparse/icelake.h +463 -0
  306. package/numkong/include/numkong/sparse/neon.h +288 -0
  307. package/numkong/include/numkong/sparse/serial.h +117 -0
  308. package/numkong/include/numkong/sparse/sve2.h +504 -0
  309. package/numkong/include/numkong/sparse/turin.h +322 -0
  310. package/numkong/include/numkong/sparse.h +363 -0
  311. package/numkong/include/numkong/sparse.hpp +113 -0
  312. package/numkong/include/numkong/spatial/README.md +439 -0
  313. package/numkong/include/numkong/spatial/alder.h +604 -0
  314. package/numkong/include/numkong/spatial/diamond.h +240 -0
  315. package/numkong/include/numkong/spatial/genoa.h +222 -0
  316. package/numkong/include/numkong/spatial/haswell.h +979 -0
  317. package/numkong/include/numkong/spatial/icelake.h +1067 -0
  318. package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
  319. package/numkong/include/numkong/spatial/neon.h +846 -0
  320. package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
  321. package/numkong/include/numkong/spatial/neonfp8.h +258 -0
  322. package/numkong/include/numkong/spatial/neonsdot.h +429 -0
  323. package/numkong/include/numkong/spatial/powervsx.h +738 -0
  324. package/numkong/include/numkong/spatial/rvv.h +991 -0
  325. package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
  326. package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
  327. package/numkong/include/numkong/spatial/serial.h +347 -0
  328. package/numkong/include/numkong/spatial/sierra.h +516 -0
  329. package/numkong/include/numkong/spatial/skylake.h +605 -0
  330. package/numkong/include/numkong/spatial/sve.h +246 -0
  331. package/numkong/include/numkong/spatial/svebfdot.h +125 -0
  332. package/numkong/include/numkong/spatial/svehalf.h +136 -0
  333. package/numkong/include/numkong/spatial/svesdot.h +143 -0
  334. package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
  335. package/numkong/include/numkong/spatial.h +1681 -0
  336. package/numkong/include/numkong/spatial.hpp +183 -0
  337. package/numkong/include/numkong/spatials/README.md +580 -0
  338. package/numkong/include/numkong/spatials/alder.h +94 -0
  339. package/numkong/include/numkong/spatials/diamond.h +82 -0
  340. package/numkong/include/numkong/spatials/genoa.h +94 -0
  341. package/numkong/include/numkong/spatials/graniteamx.h +173 -0
  342. package/numkong/include/numkong/spatials/haswell.h +219 -0
  343. package/numkong/include/numkong/spatials/icelake.h +113 -0
  344. package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
  345. package/numkong/include/numkong/spatials/neon.h +109 -0
  346. package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
  347. package/numkong/include/numkong/spatials/neonfhm.h +92 -0
  348. package/numkong/include/numkong/spatials/neonfp8.h +111 -0
  349. package/numkong/include/numkong/spatials/neonsdot.h +143 -0
  350. package/numkong/include/numkong/spatials/powervsx.h +153 -0
  351. package/numkong/include/numkong/spatials/rvv.h +1976 -0
  352. package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
  353. package/numkong/include/numkong/spatials/serial.h +248 -0
  354. package/numkong/include/numkong/spatials/sierra.h +96 -0
  355. package/numkong/include/numkong/spatials/skylake.h +184 -0
  356. package/numkong/include/numkong/spatials/sme.h +1927 -0
  357. package/numkong/include/numkong/spatials/smef64.h +483 -0
  358. package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
  359. package/numkong/include/numkong/spatials.h +3374 -0
  360. package/numkong/include/numkong/spatials.hpp +509 -0
  361. package/numkong/include/numkong/tensor.hpp +1981 -0
  362. package/numkong/include/numkong/trigonometry/README.md +184 -0
  363. package/numkong/include/numkong/trigonometry/haswell.h +653 -0
  364. package/numkong/include/numkong/trigonometry/neon.h +638 -0
  365. package/numkong/include/numkong/trigonometry/rvv.h +700 -0
  366. package/numkong/include/numkong/trigonometry/serial.h +703 -0
  367. package/numkong/include/numkong/trigonometry/skylake.h +725 -0
  368. package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
  369. package/numkong/include/numkong/trigonometry.h +467 -0
  370. package/numkong/include/numkong/trigonometry.hpp +166 -0
  371. package/numkong/include/numkong/types.h +1695 -0
  372. package/numkong/include/numkong/types.hpp +6027 -0
  373. package/numkong/include/numkong/vector.hpp +766 -0
  374. package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
  375. package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
  376. package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
  377. package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
  378. package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
  379. package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
  380. package/numkong/javascript/README.md +254 -0
  381. package/numkong/javascript/node-gyp-build.d.ts +4 -0
  382. package/numkong/javascript/numkong-browser.ts +40 -0
  383. package/numkong/javascript/numkong-wasm.ts +750 -0
  384. package/numkong/javascript/numkong.c +712 -0
  385. package/numkong/javascript/numkong.ts +613 -0
  386. package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
  387. package/numkong/javascript/tsconfig-cjs.json +13 -0
  388. package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
  389. package/numkong/javascript/types.ts +784 -0
  390. package/numkong/numkong.gypi +104 -0
  391. package/numkong/package.json +108 -0
  392. package/numkong/probes/arm_neon.c +8 -0
  393. package/numkong/probes/arm_neon_bfdot.c +9 -0
  394. package/numkong/probes/arm_neon_fhm.c +9 -0
  395. package/numkong/probes/arm_neon_half.c +8 -0
  396. package/numkong/probes/arm_neon_sdot.c +9 -0
  397. package/numkong/probes/arm_neonfp8.c +9 -0
  398. package/numkong/probes/arm_sme.c +16 -0
  399. package/numkong/probes/arm_sme2.c +16 -0
  400. package/numkong/probes/arm_sme2p1.c +16 -0
  401. package/numkong/probes/arm_sme_bf16.c +16 -0
  402. package/numkong/probes/arm_sme_bi32.c +16 -0
  403. package/numkong/probes/arm_sme_f64.c +16 -0
  404. package/numkong/probes/arm_sme_fa64.c +14 -0
  405. package/numkong/probes/arm_sme_half.c +16 -0
  406. package/numkong/probes/arm_sme_lut2.c +15 -0
  407. package/numkong/probes/arm_sve.c +18 -0
  408. package/numkong/probes/arm_sve2.c +20 -0
  409. package/numkong/probes/arm_sve2p1.c +18 -0
  410. package/numkong/probes/arm_sve_bfdot.c +20 -0
  411. package/numkong/probes/arm_sve_half.c +18 -0
  412. package/numkong/probes/arm_sve_sdot.c +21 -0
  413. package/numkong/probes/loongarch_lasx.c +12 -0
  414. package/numkong/probes/power_vsx.c +12 -0
  415. package/numkong/probes/probe.js +127 -0
  416. package/numkong/probes/riscv_rvv.c +14 -0
  417. package/numkong/probes/riscv_rvv_bb.c +15 -0
  418. package/numkong/probes/riscv_rvv_bf16.c +17 -0
  419. package/numkong/probes/riscv_rvv_half.c +14 -0
  420. package/numkong/probes/wasm_v128relaxed.c +11 -0
  421. package/numkong/probes/x86_alder.c +17 -0
  422. package/numkong/probes/x86_diamond.c +17 -0
  423. package/numkong/probes/x86_genoa.c +17 -0
  424. package/numkong/probes/x86_graniteamx.c +19 -0
  425. package/numkong/probes/x86_haswell.c +11 -0
  426. package/numkong/probes/x86_icelake.c +17 -0
  427. package/numkong/probes/x86_sapphire.c +16 -0
  428. package/numkong/probes/x86_sapphireamx.c +18 -0
  429. package/numkong/probes/x86_sierra.c +17 -0
  430. package/numkong/probes/x86_skylake.c +15 -0
  431. package/numkong/probes/x86_turin.c +17 -0
  432. package/numkong/rust/README.md +698 -0
  433. package/numkong/rust/capabilities.rs +96 -0
  434. package/numkong/rust/cast.rs +314 -0
  435. package/numkong/rust/curved.rs +391 -0
  436. package/numkong/rust/each.rs +2721 -0
  437. package/numkong/rust/geospatial.rs +343 -0
  438. package/numkong/rust/matrix.rs +5424 -0
  439. package/numkong/rust/maxsim.rs +540 -0
  440. package/numkong/rust/mesh.rs +837 -0
  441. package/numkong/rust/numkong.rs +409 -0
  442. package/numkong/rust/probability.rs +272 -0
  443. package/numkong/rust/reduce.rs +1249 -0
  444. package/numkong/rust/set.rs +194 -0
  445. package/numkong/rust/sparse.rs +513 -0
  446. package/numkong/rust/spatial.rs +1815 -0
  447. package/numkong/rust/tensor.rs +7565 -0
  448. package/numkong/rust/types.rs +3666 -0
  449. package/numkong/rust/vector.rs +1899 -0
  450. package/numkong/rustfmt.toml +2 -0
  451. package/numkong/swift/Matrix.swift +1388 -0
  452. package/numkong/swift/MaxSim.swift +151 -0
  453. package/numkong/swift/NumKong.swift +283 -0
  454. package/numkong/swift/Numerics.swift +329 -0
  455. package/numkong/swift/README.md +433 -0
  456. package/numkong/swift/Scalar.swift +741 -0
  457. package/numkong/swift/Tensor.swift +209 -0
  458. package/numkong/test/README.md +315 -0
  459. package/numkong/test/playwright.config.ts +16 -0
  460. package/numkong/test/swift/Test.swift +534 -0
  461. package/numkong/test/test-browser-bundle-runner.mjs +36 -0
  462. package/numkong/test/test-browser-bundle.html +188 -0
  463. package/numkong/test/test-browser-runner.mjs +43 -0
  464. package/numkong/test/test-browser.html +305 -0
  465. package/numkong/test/test-dtypes.mjs +453 -0
  466. package/numkong/test/test-wasm.mjs +267 -0
  467. package/numkong/test/test.cpp +442 -0
  468. package/numkong/test/test.hpp +580 -0
  469. package/numkong/test/test.mjs +363 -0
  470. package/numkong/test/test_base.py +1041 -0
  471. package/numkong/test/test_cast.cpp +242 -0
  472. package/numkong/test/test_cross.hpp +551 -0
  473. package/numkong/test/test_cross.py +467 -0
  474. package/numkong/test/test_cross_amx.cpp +50 -0
  475. package/numkong/test/test_cross_arm.cpp +225 -0
  476. package/numkong/test/test_cross_blas.cpp +354 -0
  477. package/numkong/test/test_cross_loongarch.cpp +90 -0
  478. package/numkong/test/test_cross_power.cpp +73 -0
  479. package/numkong/test/test_cross_rvv.cpp +109 -0
  480. package/numkong/test/test_cross_serial.cpp +181 -0
  481. package/numkong/test/test_cross_sme.cpp +148 -0
  482. package/numkong/test/test_cross_wasm.cpp +141 -0
  483. package/numkong/test/test_cross_x86.cpp +442 -0
  484. package/numkong/test/test_curved.cpp +182 -0
  485. package/numkong/test/test_curved.py +207 -0
  486. package/numkong/test/test_dot.cpp +353 -0
  487. package/numkong/test/test_dot.py +296 -0
  488. package/numkong/test/test_each.cpp +437 -0
  489. package/numkong/test/test_each.py +756 -0
  490. package/numkong/test/test_geospatial.cpp +155 -0
  491. package/numkong/test/test_geospatial.py +241 -0
  492. package/numkong/test/test_maxsim.cpp +138 -0
  493. package/numkong/test/test_maxsim.py +209 -0
  494. package/numkong/test/test_mesh.cpp +203 -0
  495. package/numkong/test/test_mesh.py +394 -0
  496. package/numkong/test/test_probability.cpp +136 -0
  497. package/numkong/test/test_probability.py +166 -0
  498. package/numkong/test/test_reduce.cpp +563 -0
  499. package/numkong/test/test_reduce.py +781 -0
  500. package/numkong/test/test_set.cpp +151 -0
  501. package/numkong/test/test_set.py +120 -0
  502. package/numkong/test/test_similarities.py +579 -0
  503. package/numkong/test/test_sparse.cpp +121 -0
  504. package/numkong/test/test_sparse.py +118 -0
  505. package/numkong/test/test_spatial.cpp +518 -0
  506. package/numkong/test/test_spatial.py +269 -0
  507. package/numkong/test/test_tensor.cpp +884 -0
  508. package/numkong/test/test_tensor.py +1417 -0
  509. package/numkong/test/test_trigonometry.cpp +156 -0
  510. package/numkong/test/test_trigonometry.py +180 -0
  511. package/package.json +7 -8
  512. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  513. package/prebuilds/linux-arm64/usearch.node +0 -0
  514. package/prebuilds/linux-x64/usearch.node +0 -0
  515. package/fp16/CMakeLists.txt +0 -176
  516. package/fp16/LICENSE +0 -11
  517. package/fp16/README.md +0 -20
  518. package/fp16/bench/alt-element.cc +0 -89
  519. package/fp16/bench/from-alt-array.cc +0 -175
  520. package/fp16/bench/from-ieee-array.cc +0 -399
  521. package/fp16/bench/ieee-element.cc +0 -272
  522. package/fp16/bench/to-alt-array.cc +0 -91
  523. package/fp16/bench/to-ieee-array.cc +0 -303
  524. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  525. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  526. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  527. package/fp16/configure.py +0 -65
  528. package/fp16/confu.yaml +0 -8
  529. package/fp16/include/fp16/__init__.py +0 -0
  530. package/fp16/include/fp16/avx.py +0 -50
  531. package/fp16/include/fp16/avx2.py +0 -53
  532. package/fp16/include/fp16/bitcasts.h +0 -92
  533. package/fp16/include/fp16/fp16.h +0 -451
  534. package/fp16/include/fp16/psimd.h +0 -131
  535. package/fp16/include/fp16.h +0 -11
  536. package/fp16/jni/Android.mk +0 -6
  537. package/fp16/jni/Application.mk +0 -4
  538. package/fp16/test/alt-from-fp32-value.cc +0 -498
  539. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  540. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  541. package/fp16/test/alt-to-fp32-value.cc +0 -348
  542. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  543. package/fp16/test/bitcasts.cc +0 -56
  544. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  545. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  546. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  547. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  548. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  549. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  550. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  551. package/fp16/test/peachpy/stubs.py +0 -38
  552. package/fp16/test/tables.cc +0 -530
  553. package/fp16/test/tables.h +0 -13
  554. package/fp16/third-party/THHalf.h +0 -92
  555. package/fp16/third-party/eigen-half.h +0 -170
  556. package/fp16/third-party/float16-compressor.h +0 -80
  557. package/fp16/third-party/half.hpp +0 -2916
  558. package/fp16/third-party/npy-halffloat.h +0 -328
  559. package/simsimd/.git-blame-ignore-revs +0 -2
  560. package/simsimd/CONTRIBUTING.md +0 -321
  561. package/simsimd/Cargo.toml +0 -49
  562. package/simsimd/MANIFEST.in +0 -4
  563. package/simsimd/Package.swift +0 -28
  564. package/simsimd/README.md +0 -1493
  565. package/simsimd/binding.gyp +0 -35
  566. package/simsimd/build.rs +0 -70
  567. package/simsimd/c/lib.c +0 -374
  568. package/simsimd/include/module.modulemap +0 -4
  569. package/simsimd/include/simsimd/binary.h +0 -486
  570. package/simsimd/include/simsimd/curved.h +0 -1541
  571. package/simsimd/include/simsimd/dot.h +0 -1853
  572. package/simsimd/include/simsimd/elementwise.h +0 -2465
  573. package/simsimd/include/simsimd/geospatial.h +0 -43
  574. package/simsimd/include/simsimd/mesh.h +0 -69
  575. package/simsimd/include/simsimd/probability.h +0 -606
  576. package/simsimd/include/simsimd/simsimd.h +0 -2567
  577. package/simsimd/include/simsimd/sparse.h +0 -1384
  578. package/simsimd/include/simsimd/spatial.h +0 -2335
  579. package/simsimd/include/simsimd/types.h +0 -666
  580. package/simsimd/javascript/fallback.ts +0 -233
  581. package/simsimd/javascript/lib.c +0 -125
  582. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  583. package/simsimd/javascript/simsimd.ts +0 -179
  584. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  585. package/simsimd/package-lock.json +0 -1761
  586. package/simsimd/package.json +0 -61
  587. package/simsimd/rust/lib.rs +0 -1821
  588. package/simsimd/scripts/bench.cxx +0 -1170
  589. package/simsimd/scripts/bench.ipynb +0 -187
  590. package/simsimd/scripts/bench.js +0 -127
  591. package/simsimd/scripts/bench.rs +0 -876
  592. package/simsimd/scripts/bench_vectors.py +0 -734
  593. package/simsimd/scripts/bench_vectors_live.py +0 -213
  594. package/simsimd/scripts/test.c +0 -187
  595. package/simsimd/scripts/test.mjs +0 -220
  596. package/simsimd/scripts/test.py +0 -1505
  597. package/simsimd/swift/SimSIMD.swift +0 -149
  598. package/simsimd/swift/Test.swift +0 -128
  599. /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
  600. /package/{simsimd → numkong}/LICENSE +0 -0
  601. /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
  602. /package/{simsimd → numkong}/javascript/dist-package-esm.json +0 -0
@@ -0,0 +1,485 @@
1
+ /**
2
+ * @brief SIMD-accelerated Set Similarity Measures for Ice Lake.
3
+ * @file include/numkong/set/icelake.h
4
+ * @author Ash Vardanian
5
+ * @date December 27, 2025
6
+ *
7
+ * @sa include/numkong/set.h
8
+ *
9
+ * @section set_icelake_instructions Key AVX-512 Set Instructions
10
+ *
11
+ * Intrinsic Instruction Ice Lake
12
+ * _mm512_popcnt_epi64 VPOPCNTQ (ZMM, ZMM) 3cy @ p5
13
+ * _mm512_and_si512 VPANDQ (ZMM, ZMM, ZMM) 1cy @ p05
14
+ * _mm512_or_si512 VPORQ (ZMM, ZMM, ZMM) 1cy @ p05
15
+ * _mm512_xor_si512 VPXORQ (ZMM, ZMM, ZMM) 1cy @ p05
16
+ * _mm512_maskz_loadu_epi8 VMOVDQU8 (ZMM, mem, k1) 7cy @ p23
17
+ *
18
+ * Ice Lake has native VPOPCNTQ instruction via AVX-512 VPOPCNTDQ extension, enabling
19
+ * efficient 64-bit element-wise popcount. We process 512 bits per iteration.
20
+ *
21
+ * @section set_icelake_stateful Stateful Streaming Logic
22
+ *
23
+ * To build memory-optimal tiled algorithms, this file defines:
24
+ *
25
+ * - nk_hamming_u1x512_state_icelake_t for streaming Hamming distance
26
+ * - nk_jaccard_u1x512_state_icelake_t for streaming Jaccard similarity
27
+ *
28
+ * @code{c}
29
+ * nk_jaccard_u1x512_state_icelake_t state_first, state_second, state_third, state_fourth;
30
+ * nk_jaccard_u1x512_init_icelake(&state_first);
31
+ * // ... stream through packed binary vectors ...
32
+ * nk_jaccard_u1x512_finalize_icelake(&state_first, &state_second, &state_third, &state_fourth,
33
+ * query_popcount, target_popcount_a, target_popcount_b, target_popcount_c, target_popcount_d,
34
+ * total_dimensions, &results);
35
+ * @endcode
36
+ */
37
+ #ifndef NK_SET_ICELAKE_H
38
+ #define NK_SET_ICELAKE_H
39
+
40
+ #if NK_TARGET_X8664_
41
+ #if NK_TARGET_ICELAKE
42
+
43
+ #include "numkong/types.h"
44
+
45
+ #if defined(__cplusplus)
46
+ extern "C" {
47
+ #endif
48
+
49
+ #if defined(__clang__)
50
+ #pragma clang attribute push( \
51
+ __attribute__((target("avx2,avx512f,avx512vl,avx512bw,avx512vpopcntdq,f16c,fma,bmi,bmi2"))), apply_to = function)
52
+ #elif defined(__GNUC__)
53
+ #pragma GCC push_options
54
+ #pragma GCC target("avx2", "avx512f", "avx512vl", "avx512bw", "avx512vpopcntdq", "f16c", "fma", "bmi", "bmi2")
55
+ #endif
56
+
57
+ #pragma region Binary Sets
58
+
59
+ NK_PUBLIC void nk_hamming_u1_icelake(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n, nk_u32_t *result) {
60
+ nk_size_t n_bytes = nk_size_divide_round_up_(n, NK_BITS_PER_BYTE);
61
+
62
+ nk_u32_t xor_count;
63
+ // It's harder to squeeze out performance from tiny representations, so we unroll the loops for binary metrics.
64
+ if (n_bytes <= 64) { // Up to 512 bits.
65
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
66
+ __m512i a_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
67
+ __m512i b_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
68
+ __m512i xor_popcount_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_u8x64, b_u8x64));
69
+ xor_count = _mm512_reduce_add_epi64(xor_popcount_u64x8);
70
+ }
71
+ else if (n_bytes <= 128) { // Up to 1024 bits.
72
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 64);
73
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
74
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
75
+ __m512i a_two_u8x64 = _mm512_maskz_loadu_epi8(mask, a + 64);
76
+ __m512i b_two_u8x64 = _mm512_maskz_loadu_epi8(mask, b + 64);
77
+ __m512i xor_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_one_u8x64, b_one_u8x64));
78
+ __m512i xor_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_two_u8x64, b_two_u8x64));
79
+ xor_count = _mm512_reduce_add_epi64(_mm512_add_epi64(xor_popcount_two_u64x8, xor_popcount_one_u64x8));
80
+ }
81
+ else if (n_bytes <= 192) { // Up to 1536 bits.
82
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 128);
83
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
84
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
85
+ __m512i a_two_u8x64 = _mm512_loadu_epi8(a + 64);
86
+ __m512i b_two_u8x64 = _mm512_loadu_epi8(b + 64);
87
+ __m512i a_three_u8x64 = _mm512_maskz_loadu_epi8(mask, a + 128);
88
+ __m512i b_three_u8x64 = _mm512_maskz_loadu_epi8(mask, b + 128);
89
+ __m512i xor_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_one_u8x64, b_one_u8x64));
90
+ __m512i xor_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_two_u8x64, b_two_u8x64));
91
+ __m512i xor_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_three_u8x64, b_three_u8x64));
92
+ xor_count = _mm512_reduce_add_epi64(_mm512_add_epi64(
93
+ xor_popcount_three_u64x8, _mm512_add_epi64(xor_popcount_two_u64x8, xor_popcount_one_u64x8)));
94
+ }
95
+ else if (n_bytes <= 256) { // Up to 2048 bits.
96
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 192);
97
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
98
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
99
+ __m512i a_two_u8x64 = _mm512_loadu_epi8(a + 64);
100
+ __m512i b_two_u8x64 = _mm512_loadu_epi8(b + 64);
101
+ __m512i a_three_u8x64 = _mm512_loadu_epi8(a + 128);
102
+ __m512i b_three_u8x64 = _mm512_loadu_epi8(b + 128);
103
+ __m512i a_four_u8x64 = _mm512_maskz_loadu_epi8(mask, a + 192);
104
+ __m512i b_four_u8x64 = _mm512_maskz_loadu_epi8(mask, b + 192);
105
+ __m512i xor_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_one_u8x64, b_one_u8x64));
106
+ __m512i xor_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_two_u8x64, b_two_u8x64));
107
+ __m512i xor_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_three_u8x64, b_three_u8x64));
108
+ __m512i xor_popcount_four_u64x8 = _mm512_popcnt_epi64(_mm512_xor_si512(a_four_u8x64, b_four_u8x64));
109
+ xor_count = _mm512_reduce_add_epi64(
110
+ _mm512_add_epi64(_mm512_add_epi64(xor_popcount_four_u64x8, xor_popcount_three_u64x8),
111
+ _mm512_add_epi64(xor_popcount_two_u64x8, xor_popcount_one_u64x8)));
112
+ }
113
+ else {
114
+ __m512i xor_popcount_u64x8 = _mm512_setzero_si512();
115
+ __m512i a_u8x64, b_u8x64;
116
+
117
+ nk_hamming_u1_icelake_cycle:
118
+ if (n_bytes < 64) {
119
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
120
+ a_u8x64 = _mm512_maskz_loadu_epi8(mask, a);
121
+ b_u8x64 = _mm512_maskz_loadu_epi8(mask, b);
122
+ n_bytes = 0;
123
+ }
124
+ else {
125
+ a_u8x64 = _mm512_loadu_epi8(a);
126
+ b_u8x64 = _mm512_loadu_epi8(b);
127
+ a += 64, b += 64, n_bytes -= 64;
128
+ }
129
+ __m512i xor_u8x64 = _mm512_xor_si512(a_u8x64, b_u8x64);
130
+ xor_popcount_u64x8 = _mm512_add_epi64(xor_popcount_u64x8, _mm512_popcnt_epi64(xor_u8x64));
131
+ if (n_bytes) goto nk_hamming_u1_icelake_cycle;
132
+
133
+ xor_count = _mm512_reduce_add_epi64(xor_popcount_u64x8);
134
+ }
135
+ *result = xor_count;
136
+ }
137
+
138
+ NK_PUBLIC void nk_jaccard_u1_icelake(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n, nk_f32_t *result) {
139
+ nk_size_t n_bytes = nk_size_divide_round_up_(n, NK_BITS_PER_BYTE);
140
+
141
+ nk_u32_t intersection_count = 0, union_count = 0;
142
+ // It's harder to squeeze out performance from tiny representations, so we unroll the loops for binary metrics.
143
+ if (n_bytes <= 64) { // Up to 512 bits.
144
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
145
+ __m512i a_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a);
146
+ __m512i b_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b);
147
+ __m512i intersection_popcount_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_u8x64, b_u8x64));
148
+ __m512i union_popcount_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_u8x64, b_u8x64));
149
+ intersection_count = _mm512_reduce_add_epi64(intersection_popcount_u64x8);
150
+ union_count = _mm512_reduce_add_epi64(union_popcount_u64x8);
151
+ }
152
+ else if (n_bytes <= 128) { // Up to 1024 bits.
153
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 64);
154
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
155
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
156
+ __m512i a_two_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a + 64);
157
+ __m512i b_two_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b + 64);
158
+ __m512i intersection_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_one_u8x64, b_one_u8x64));
159
+ __m512i union_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_one_u8x64, b_one_u8x64));
160
+ __m512i intersection_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_two_u8x64, b_two_u8x64));
161
+ __m512i union_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_two_u8x64, b_two_u8x64));
162
+ intersection_count = _mm512_reduce_add_epi64(
163
+ _mm512_add_epi64(intersection_popcount_two_u64x8, intersection_popcount_one_u64x8));
164
+ union_count = _mm512_reduce_add_epi64(_mm512_add_epi64(union_popcount_two_u64x8, union_popcount_one_u64x8));
165
+ }
166
+ else if (n_bytes <= 192) { // Up to 1536 bits.
167
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 128);
168
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
169
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
170
+ __m512i a_two_u8x64 = _mm512_loadu_epi8(a + 64);
171
+ __m512i b_two_u8x64 = _mm512_loadu_epi8(b + 64);
172
+ __m512i a_three_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a + 128);
173
+ __m512i b_three_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b + 128);
174
+ __m512i intersection_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_one_u8x64, b_one_u8x64));
175
+ __m512i union_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_one_u8x64, b_one_u8x64));
176
+ __m512i intersection_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_two_u8x64, b_two_u8x64));
177
+ __m512i union_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_two_u8x64, b_two_u8x64));
178
+ __m512i intersection_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_three_u8x64, b_three_u8x64));
179
+ __m512i union_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_three_u8x64, b_three_u8x64));
180
+ intersection_count = _mm512_reduce_add_epi64( //
181
+ _mm512_add_epi64(intersection_popcount_three_u64x8,
182
+ _mm512_add_epi64(intersection_popcount_two_u64x8, intersection_popcount_one_u64x8)));
183
+ union_count = _mm512_reduce_add_epi64( //
184
+ _mm512_add_epi64(union_popcount_three_u64x8,
185
+ _mm512_add_epi64(union_popcount_two_u64x8, union_popcount_one_u64x8)));
186
+ }
187
+ else if (n_bytes <= 256) { // Up to 2048 bits.
188
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes - 192);
189
+ __m512i a_one_u8x64 = _mm512_loadu_epi8(a);
190
+ __m512i b_one_u8x64 = _mm512_loadu_epi8(b);
191
+ __m512i a_two_u8x64 = _mm512_loadu_epi8(a + 64);
192
+ __m512i b_two_u8x64 = _mm512_loadu_epi8(b + 64);
193
+ __m512i a_three_u8x64 = _mm512_loadu_epi8(a + 128);
194
+ __m512i b_three_u8x64 = _mm512_loadu_epi8(b + 128);
195
+ __m512i a_four_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a + 192);
196
+ __m512i b_four_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b + 192);
197
+ __m512i intersection_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_one_u8x64, b_one_u8x64));
198
+ __m512i union_popcount_one_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_one_u8x64, b_one_u8x64));
199
+ __m512i intersection_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_two_u8x64, b_two_u8x64));
200
+ __m512i union_popcount_two_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_two_u8x64, b_two_u8x64));
201
+ __m512i intersection_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_three_u8x64, b_three_u8x64));
202
+ __m512i union_popcount_three_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_three_u8x64, b_three_u8x64));
203
+ __m512i intersection_popcount_four_u64x8 = _mm512_popcnt_epi64(_mm512_and_si512(a_four_u8x64, b_four_u8x64));
204
+ __m512i union_popcount_four_u64x8 = _mm512_popcnt_epi64(_mm512_or_si512(a_four_u8x64, b_four_u8x64));
205
+ intersection_count = _mm512_reduce_add_epi64(
206
+ _mm512_add_epi64(_mm512_add_epi64(intersection_popcount_four_u64x8, intersection_popcount_three_u64x8),
207
+ _mm512_add_epi64(intersection_popcount_two_u64x8, intersection_popcount_one_u64x8)));
208
+ union_count = _mm512_reduce_add_epi64(
209
+ _mm512_add_epi64(_mm512_add_epi64(union_popcount_four_u64x8, union_popcount_three_u64x8),
210
+ _mm512_add_epi64(union_popcount_two_u64x8, union_popcount_one_u64x8)));
211
+ }
212
+ else {
213
+ __m512i intersection_popcount_u64x8 = _mm512_setzero_si512();
214
+ __m512i union_popcount_u64x8 = _mm512_setzero_si512();
215
+ __m512i a_u8x64, b_u8x64;
216
+
217
+ nk_jaccard_u1_icelake_cycle:
218
+ if (n_bytes < 64) {
219
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_bytes);
220
+ a_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a);
221
+ b_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b);
222
+ n_bytes = 0;
223
+ }
224
+ else {
225
+ a_u8x64 = _mm512_loadu_epi8(a);
226
+ b_u8x64 = _mm512_loadu_epi8(b);
227
+ a += 64, b += 64, n_bytes -= 64;
228
+ }
229
+ __m512i intersection_u8x64 = _mm512_and_si512(a_u8x64, b_u8x64);
230
+ __m512i union_u8x64 = _mm512_or_si512(a_u8x64, b_u8x64);
231
+ intersection_popcount_u64x8 = _mm512_add_epi64(intersection_popcount_u64x8,
232
+ _mm512_popcnt_epi64(intersection_u8x64));
233
+ union_popcount_u64x8 = _mm512_add_epi64(union_popcount_u64x8, _mm512_popcnt_epi64(union_u8x64));
234
+ if (n_bytes) goto nk_jaccard_u1_icelake_cycle;
235
+
236
+ intersection_count = _mm512_reduce_add_epi64(intersection_popcount_u64x8);
237
+ union_count = _mm512_reduce_add_epi64(union_popcount_u64x8);
238
+ }
239
+ *result = (union_count != 0) ? 1.0f - (nk_f32_t)intersection_count / (nk_f32_t)union_count : 0.0f;
240
+ }
241
+
242
+ #pragma endregion Binary Sets
243
+
244
+ #pragma region Integer Sets
245
+
246
+ NK_PUBLIC void nk_jaccard_u32_icelake(nk_u32_t const *a, nk_u32_t const *b, nk_size_t n, nk_f32_t *result) {
247
+ nk_u32_t intersection_count = 0;
248
+ nk_size_t n_remaining = n;
249
+ for (; n_remaining >= 16; n_remaining -= 16, a += 16, b += 16) {
250
+ __m512i a_u32x16 = _mm512_loadu_epi32(a);
251
+ __m512i b_u32x16 = _mm512_loadu_epi32(b);
252
+ __mmask16 equality_mask = _mm512_cmpeq_epi32_mask(a_u32x16, b_u32x16);
253
+ intersection_count += _mm_popcnt_u32((unsigned int)equality_mask);
254
+ }
255
+ if (n_remaining) {
256
+ __mmask16 load_mask = (__mmask16)_bzhi_u32(0xFFFF, n_remaining);
257
+ __m512i a_u32x16 = _mm512_maskz_loadu_epi32(load_mask, a);
258
+ __m512i b_u32x16 = _mm512_maskz_loadu_epi32(load_mask, b);
259
+ __mmask16 equality_mask = _mm512_mask_cmpeq_epi32_mask(load_mask, a_u32x16, b_u32x16);
260
+ intersection_count += _mm_popcnt_u32((unsigned int)equality_mask);
261
+ }
262
+ *result = (n != 0) ? 1.0f - (nk_f32_t)intersection_count / (nk_f32_t)n : 0.0f;
263
+ }
264
+
265
+ NK_PUBLIC void nk_hamming_u8_icelake(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_u32_t *result) {
266
+ nk_u32_t differences = 0;
267
+ nk_size_t n_remaining = n;
268
+ for (; n_remaining >= 64; n_remaining -= 64, a += 64, b += 64) {
269
+ __m512i a_u8x64 = _mm512_loadu_si512((__m512i const *)a);
270
+ __m512i b_u8x64 = _mm512_loadu_si512((__m512i const *)b);
271
+ __mmask64 neq_mask = _mm512_cmpneq_epi8_mask(a_u8x64, b_u8x64);
272
+ differences += _mm_popcnt_u64(neq_mask);
273
+ }
274
+ if (n_remaining) {
275
+ __mmask64 load_mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n_remaining);
276
+ __m512i a_u8x64 = _mm512_maskz_loadu_epi8(load_mask, a);
277
+ __m512i b_u8x64 = _mm512_maskz_loadu_epi8(load_mask, b);
278
+ __mmask64 neq_mask = _mm512_mask_cmpneq_epi8_mask(load_mask, a_u8x64, b_u8x64);
279
+ differences += _mm_popcnt_u64(neq_mask);
280
+ }
281
+ *result = differences;
282
+ }
283
+
284
+ NK_PUBLIC void nk_jaccard_u16_icelake(nk_u16_t const *a, nk_u16_t const *b, nk_size_t n, nk_f32_t *result) {
285
+ nk_u32_t matches = 0;
286
+ nk_size_t n_remaining = n;
287
+ for (; n_remaining >= 32; n_remaining -= 32, a += 32, b += 32) {
288
+ __m512i a_u16x32 = _mm512_loadu_si512((__m512i const *)a);
289
+ __m512i b_u16x32 = _mm512_loadu_si512((__m512i const *)b);
290
+ __mmask32 equality_mask = _mm512_cmpeq_epi16_mask(a_u16x32, b_u16x32);
291
+ matches += _mm_popcnt_u32(equality_mask);
292
+ }
293
+ if (n_remaining) {
294
+ __mmask32 load_mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n_remaining);
295
+ __m512i a_u16x32 = _mm512_maskz_loadu_epi16(load_mask, a);
296
+ __m512i b_u16x32 = _mm512_maskz_loadu_epi16(load_mask, b);
297
+ __mmask32 equality_mask = _mm512_mask_cmpeq_epi16_mask(load_mask, a_u16x32, b_u16x32);
298
+ matches += _mm_popcnt_u32(equality_mask);
299
+ }
300
+ *result = (n != 0) ? 1.0f - (nk_f32_t)matches / (nk_f32_t)n : 0.0f;
301
+ }
302
+
303
+ #pragma endregion Integer Sets
304
+
305
+ #pragma region Stateful Streaming
306
+
307
+ typedef struct nk_hamming_u1x512_state_icelake_t {
308
+ __m512i intersection_count_i64x8;
309
+ } nk_hamming_u1x512_state_icelake_t;
310
+
311
+ NK_INTERNAL void nk_hamming_u1x512_init_icelake(nk_hamming_u1x512_state_icelake_t *state) {
312
+ state->intersection_count_i64x8 = _mm512_setzero_si512();
313
+ }
314
+
315
+ NK_INTERNAL void nk_hamming_u1x512_update_icelake(nk_hamming_u1x512_state_icelake_t *state, nk_b512_vec_t a,
316
+ nk_b512_vec_t b, nk_size_t depth_offset,
317
+ nk_size_t active_dimensions) {
318
+ nk_unused_(depth_offset);
319
+ nk_unused_(active_dimensions);
320
+ state->intersection_count_i64x8 = _mm512_add_epi64(state->intersection_count_i64x8,
321
+ _mm512_popcnt_epi64(_mm512_xor_si512(a.zmm, b.zmm)));
322
+ }
323
+
324
+ NK_INTERNAL void nk_hamming_u1x512_finalize_icelake( //
325
+ nk_hamming_u1x512_state_icelake_t const *state_a, nk_hamming_u1x512_state_icelake_t const *state_b,
326
+ nk_hamming_u1x512_state_icelake_t const *state_c, nk_hamming_u1x512_state_icelake_t const *state_d,
327
+ nk_size_t total_dimensions, nk_b128_vec_t *result) {
328
+ nk_unused_(total_dimensions);
329
+
330
+ // Port-optimized 4-way horizontal reduction, matching the Jaccard finalizer pattern.
331
+ // Truncate i64 → i32 early so we can use `VPHADDD` (p01) instead of shuffle-heavy i64 reductions (p5).
332
+
333
+ // Step 1: Truncate 8×i64 → 8×i32 per state via VPMOVQD (p01, 4cy, 0.5/cy)
334
+ __m256i a_i32x8 = _mm512_cvtepi64_epi32(state_a->intersection_count_i64x8);
335
+ __m256i b_i32x8 = _mm512_cvtepi64_epi32(state_b->intersection_count_i64x8);
336
+ __m256i c_i32x8 = _mm512_cvtepi64_epi32(state_c->intersection_count_i64x8);
337
+ __m256i d_i32x8 = _mm512_cvtepi64_epi32(state_d->intersection_count_i64x8);
338
+
339
+ // Step 2: Fold 8×i32 → 4×i32 (add high 128-bit lane to low)
340
+ __m128i a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(a_i32x8), _mm256_extracti128_si256(a_i32x8, 1));
341
+ __m128i b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(b_i32x8), _mm256_extracti128_si256(b_i32x8, 1));
342
+ __m128i c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(c_i32x8), _mm256_extracti128_si256(c_i32x8, 1));
343
+ __m128i d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(d_i32x8), _mm256_extracti128_si256(d_i32x8, 1));
344
+
345
+ // Step 3: Interleaved horizontal adds — 4×i32 → 2×i32 via VPHADDD (p01, 3cy, 0.5/cy)
346
+ __m128i ab_i32x4 = _mm_hadd_epi32(a_i32x4, b_i32x4); // [a01, a23, b01, b23]
347
+ __m128i cd_i32x4 = _mm_hadd_epi32(c_i32x4, d_i32x4); // [c01, c23, d01, d23]
348
+
349
+ // Step 4: Final horizontal add — 2×i32 → 1×i32 per state
350
+ result->xmm = _mm_hadd_epi32(ab_i32x4, cd_i32x4); // [sum_a, sum_b, sum_c, sum_d]
351
+ }
352
+
353
+ typedef struct nk_jaccard_u1x512_state_icelake_t {
354
+ __m512i intersection_count_i64x8;
355
+ } nk_jaccard_u1x512_state_icelake_t;
356
+
357
+ NK_INTERNAL void nk_jaccard_u1x512_init_icelake(nk_jaccard_u1x512_state_icelake_t *state) {
358
+ state->intersection_count_i64x8 = _mm512_setzero_si512();
359
+ }
360
+
361
+ NK_INTERNAL void nk_jaccard_u1x512_update_icelake(nk_jaccard_u1x512_state_icelake_t *state, nk_b512_vec_t a,
362
+ nk_b512_vec_t b, nk_size_t depth_offset,
363
+ nk_size_t active_dimensions) {
364
+ nk_unused_(depth_offset);
365
+ nk_unused_(active_dimensions);
366
+ state->intersection_count_i64x8 = _mm512_add_epi64(state->intersection_count_i64x8,
367
+ _mm512_popcnt_epi64(_mm512_and_si512(a.zmm, b.zmm)));
368
+ }
369
+
370
+ NK_INTERNAL void nk_jaccard_u1x512_finalize_icelake( //
371
+ nk_jaccard_u1x512_state_icelake_t const *state_a, nk_jaccard_u1x512_state_icelake_t const *state_b,
372
+ nk_jaccard_u1x512_state_icelake_t const *state_c, nk_jaccard_u1x512_state_icelake_t const *state_d,
373
+ nk_f32_t query_popcount, nk_f32_t target_popcount_a, nk_f32_t target_popcount_b, nk_f32_t target_popcount_c,
374
+ nk_f32_t target_popcount_d, nk_size_t total_dimensions, nk_b128_vec_t *result) {
375
+ nk_unused_(total_dimensions);
376
+
377
+ // Port-optimized 4-way horizontal reduction using early i64 → i32 truncation.
378
+ //
379
+ // Key insight: `_mm_hadd_epi32` uses ports p01, not p5, avoiding the shuffle bottleneck.
380
+ // By truncating to i32 early, we can use hadd for reduction instead of expensive shuffles.
381
+ //
382
+ // Ice Lake execution ports:
383
+ // - p0: Division, reciprocal (`VRCP14PS`: 4cy latency, 1/cy throughput)
384
+ // - p01: FP mul/add/fma, hadd (`VMULPS`/`VPHADDD`: 3cy latency, 0.5/cy throughput)
385
+ // - p015: Integer add (`VPADDD`: 1cy latency, 0.33/cy throughput)
386
+ // - p5: Shuffles/extracts (`VEXTRACTI128`: 3cy latency, 1/cy throughput)
387
+
388
+ // Step 1: Truncate 8x i64 → 8x i32 per state (fits in YMM)
389
+ // `VPMOVQD` (ZMM → YMM): 4cy latency, 0.5/cy throughput, port p01
390
+ __m256i a_i32x8 = _mm512_cvtepi64_epi32(state_a->intersection_count_i64x8);
391
+ __m256i b_i32x8 = _mm512_cvtepi64_epi32(state_b->intersection_count_i64x8);
392
+ __m256i c_i32x8 = _mm512_cvtepi64_epi32(state_c->intersection_count_i64x8);
393
+ __m256i d_i32x8 = _mm512_cvtepi64_epi32(state_d->intersection_count_i64x8);
394
+
395
+ // Step 2: Reduce 8x i32 → 4x i32 (add high 128-bit lane to low)
396
+ // - `VEXTRACTI128`: 3cy latency, 1/cy throughput, port p5
397
+ // - `VPADDD` (XMM): 1cy latency, 0.33/cy throughput, ports p015
398
+ __m128i a_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(a_i32x8), _mm256_extracti128_si256(a_i32x8, 1));
399
+ __m128i b_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(b_i32x8), _mm256_extracti128_si256(b_i32x8, 1));
400
+ __m128i c_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(c_i32x8), _mm256_extracti128_si256(c_i32x8, 1));
401
+ __m128i d_i32x4 = _mm_add_epi32(_mm256_castsi256_si128(d_i32x8), _mm256_extracti128_si256(d_i32x8, 1));
402
+
403
+ // Step 3: Reduce 4x i32 → 2x i32 using horizontal add (uses p01, not p5!)
404
+ // - `VPHADDD` (XMM): 3cy latency, 0.5/cy throughput, ports p01
405
+ __m128i ab_i32x4 = _mm_hadd_epi32(a_i32x4, b_i32x4); // [a01, a23, b01, b23]
406
+ __m128i cd_i32x4 = _mm_hadd_epi32(c_i32x4, d_i32x4); // [c01, c23, d01, d23]
407
+
408
+ // Step 4: Reduce 2x i32 → 1x i32 per state (final horizontal add)
409
+ __m128i intersection_i32x4 = _mm_hadd_epi32(ab_i32x4, cd_i32x4); // [a, b, c, d]
410
+
411
+ // Step 5: Direct i32 → f32 conversion (simpler than i64 → f64 → f32 path)
412
+ // - `VCVTDQ2PS` (XMM): 4cy latency, 0.5/cy throughput, port p01
413
+ __m128 intersection_f32x4 = _mm_cvtepi32_ps(intersection_i32x4);
414
+
415
+ // Compute Jaccard distance: 1 - intersection ÷ union
416
+ // where union = query_popcount + target_popcount - intersection
417
+ __m128 query_f32x4 = _mm_set1_ps(query_popcount);
418
+ __m128 targets_f32x4 = _mm_setr_ps(target_popcount_a, target_popcount_b, target_popcount_c, target_popcount_d);
419
+ __m128 union_f32x4 = _mm_sub_ps(_mm_add_ps(query_f32x4, targets_f32x4), intersection_f32x4);
420
+
421
+ // Handle zero-union edge case: if union == 0, result = 0.0
422
+ __m128 zero_union_mask = _mm_cmpeq_ps(union_f32x4, _mm_setzero_ps());
423
+ __m128 one_f32x4 = _mm_set1_ps(1.0f);
424
+ __m128 safe_union_f32x4 = _mm_blendv_ps(union_f32x4, one_f32x4, zero_union_mask);
425
+
426
+ // Fast reciprocal with Newton-Raphson refinement:
427
+ // - `VRCP14PS`: 4cy latency, 1/cy throughput, port p0 (~14-bit precision)
428
+ // Newton-Raphson: rcp' = rcp × (2 - x × rcp) doubles precision to ~28 bits
429
+ // - `VFNMADD`: 4cy latency, 0.5/cy throughput, ports p01
430
+ // - `VMULPS`: 4cy latency, 0.5/cy throughput, ports p01
431
+ // Total: ~12cy vs `VDIVPS` 11cy latency but 3cy throughput - NR wins on throughput
432
+ __m128 union_reciprocal_f32x4 = _mm_rcp14_ps(safe_union_f32x4);
433
+ union_reciprocal_f32x4 = _mm_mul_ps(union_reciprocal_f32x4,
434
+ _mm_fnmadd_ps(safe_union_f32x4, union_reciprocal_f32x4, _mm_set1_ps(2.0f)));
435
+
436
+ __m128 ratio_f32x4 = _mm_mul_ps(intersection_f32x4, union_reciprocal_f32x4);
437
+ __m128 jaccard_f32x4 = _mm_sub_ps(one_f32x4, ratio_f32x4);
438
+ result->xmm_ps = _mm_blendv_ps(jaccard_f32x4, _mm_setzero_ps(), zero_union_mask);
439
+ }
440
+
441
+ /** @brief Hamming from_dot: computes pop_a + pop_b - 2*dot for 4 pairs (Icelake). */
442
+ NK_INTERNAL void nk_hamming_u32x4_from_dot_icelake_(nk_b128_vec_t dots, nk_u32_t query_pop, nk_b128_vec_t target_pops,
443
+ nk_b128_vec_t *results) {
444
+ __m128i dots_i32x4 = dots.xmm;
445
+ __m128i query_i32x4 = _mm_set1_epi32((int)query_pop);
446
+ __m128i target_i32x4 = target_pops.xmm;
447
+ results->xmm = _mm_sub_epi32(_mm_add_epi32(query_i32x4, target_i32x4), _mm_slli_epi32(dots_i32x4, 1));
448
+ }
449
+
450
+ /** @brief Jaccard from_dot: computes 1 - dot / (pop_a + pop_b - dot) for 4 pairs (Icelake). */
451
+ NK_INTERNAL void nk_jaccard_f32x4_from_dot_icelake_(nk_b128_vec_t dots, nk_u32_t query_pop, nk_b128_vec_t target_pops,
452
+ nk_b128_vec_t *results) {
453
+ __m128 dot_f32x4 = _mm_cvtepi32_ps(dots.xmm);
454
+ __m128 query_f32x4 = _mm_set1_ps((nk_f32_t)query_pop);
455
+ __m128 target_f32x4 = _mm_cvtepi32_ps(target_pops.xmm);
456
+ __m128 union_f32x4 = _mm_sub_ps(_mm_add_ps(query_f32x4, target_f32x4), dot_f32x4);
457
+
458
+ __m128 zero_union_mask = _mm_cmpeq_ps(union_f32x4, _mm_setzero_ps());
459
+ __m128 one_f32x4 = _mm_set1_ps(1.0f);
460
+ __m128 safe_union_f32x4 = _mm_blendv_ps(union_f32x4, one_f32x4, zero_union_mask);
461
+
462
+ __m128 union_reciprocal_f32x4 = _mm_rcp14_ps(safe_union_f32x4);
463
+ union_reciprocal_f32x4 = _mm_mul_ps(union_reciprocal_f32x4,
464
+ _mm_fnmadd_ps(safe_union_f32x4, union_reciprocal_f32x4, _mm_set1_ps(2.0f)));
465
+
466
+ __m128 ratio_f32x4 = _mm_mul_ps(dot_f32x4, union_reciprocal_f32x4);
467
+ __m128 jaccard_f32x4 = _mm_sub_ps(one_f32x4, ratio_f32x4);
468
+ results->xmm_ps = _mm_blendv_ps(jaccard_f32x4, _mm_setzero_ps(), zero_union_mask);
469
+ }
470
+
471
+ #pragma endregion Stateful Streaming
472
+
473
+ #if defined(__clang__)
474
+ #pragma clang attribute pop
475
+ #elif defined(__GNUC__)
476
+ #pragma GCC pop_options
477
+ #endif
478
+
479
+ #if defined(__cplusplus)
480
+ } // extern "C"
481
+ #endif
482
+
483
+ #endif // NK_TARGET_ICELAKE
484
+ #endif // NK_TARGET_X8664_
485
+ #endif // NK_SET_ICELAKE_H
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @brief SIMD-accelerated Set Similarity Measures for LoongArch LASX (256-bit).
3
+ * @file include/numkong/set/loongsonasx.h
4
+ * @author Ash Vardanian
5
+ * @date March 23, 2026
6
+ *
7
+ * @sa include/numkong/set.h
8
+ *
9
+ * @section set_loongsonasx_instructions Key LASX Set Instructions
10
+ *
11
+ * Intrinsic Instruction
12
+ * __lasx_xvld XVLD (256-bit unaligned load)
13
+ * __lasx_xvxor_v XVXOR.V (bitwise XOR)
14
+ * __lasx_xvor_v XVOR.V (bitwise OR)
15
+ * __lasx_xvand_v XVAND.V (bitwise AND)
16
+ * __lasx_xvpcnt_d XVPCNT.D (popcount per u64 element)
17
+ * __lasx_xvseq_b XVSEQ.B (byte-wise equality, 0xFF/0x00)
18
+ * __lasx_xvmin_bu XVMIN.BU (unsigned byte minimum)
19
+ * __lasx_xvhaddw_hu_bu XVHADDW.HU.BU (horizontal pairwise add u8->u16)
20
+ * __lasx_xvhaddw_wu_hu XVHADDW.WU.HU (horizontal pairwise add u16->u32)
21
+ * __lasx_xvhaddw_du_wu XVHADDW.DU.WU (horizontal pairwise add u32->u64)
22
+ * __lasx_xvadd_d XVADD.D (i64 addition)
23
+ * __lasx_xvpermi_q XVPERMI.Q (extract/permute 128-bit lanes)
24
+ *
25
+ * LASX provides per-element popcount at multiple widths (`xvpcnt_b/h/w/d`).
26
+ * For binary set operations we use `xvpcnt_d` which gives 4 x u64 popcount values
27
+ * directly, eliminating the need for horizontal byte-sum reduction chains.
28
+ *
29
+ * For sorted integer set operations (jaccard_u16, jaccard_u32), SIMD provides limited
30
+ * benefit due to the inherently serial merge-based algorithm, so we delegate to the
31
+ * serial implementations.
32
+ */
33
+ #ifndef NK_SET_LOONGSONASX_H
34
+ #define NK_SET_LOONGSONASX_H
35
+
36
+ #if NK_TARGET_LOONGARCH64_
37
+ #if NK_TARGET_LOONGSONASX
38
+
39
+ #include "numkong/types.h"
40
+ #include "numkong/set/serial.h" // `nk_u1x8_popcount_`, serial fallbacks
41
+ #include "numkong/dot/loongsonasx.h" // `nk_reduce_add_i32x8_loongsonasx_`
42
+
43
+ #if defined(__cplusplus)
44
+ extern "C" {
45
+ #endif
46
+
47
+ #pragma region Reduction Helpers
48
+
49
+ /** @brief Horizontal sum of 4 u64 lanes in a 256-bit LASX register. */
50
+ NK_INTERNAL nk_u64_t nk_reduce_add_u64x4_loongsonasx_(__m256i sum_u64x4) {
51
+ __m256i high_u64x4 = __lasx_xvpermi_q(sum_u64x4, sum_u64x4, 0x11);
52
+ __m256i sum_u64x2 = __lasx_xvadd_d(sum_u64x4, high_u64x4);
53
+ __m256i swapped_u64x2 = __lasx_xvshuf4i_d(sum_u64x2, sum_u64x2, 0b0001);
54
+ __m256i reduced_u64x2 = __lasx_xvadd_d(sum_u64x2, swapped_u64x2);
55
+ return (nk_u64_t)__lasx_xvpickve2gr_du(reduced_u64x2, 0);
56
+ }
57
+
58
+ /** @brief Horizontally sum all bytes in a 256-bit register as unsigned values.
59
+ *
60
+ * Chains pairwise widening additions: u8→u16→u32→u64, then reduces 4 u64 lanes.
61
+ */
62
+ NK_INTERNAL nk_u64_t nk_reduce_add_u8x32_loongsonasx_(__m256i v_u8x32) {
63
+ __m256i sum_u16x16 = __lasx_xvhaddw_hu_bu(v_u8x32, v_u8x32);
64
+ __m256i sum_u32x8 = __lasx_xvhaddw_wu_hu(sum_u16x16, sum_u16x16);
65
+ __m256i sum_u64x4 = __lasx_xvhaddw_du_wu(sum_u32x8, sum_u32x8);
66
+ return nk_reduce_add_u64x4_loongsonasx_(sum_u64x4);
67
+ }
68
+
69
+ #pragma endregion Reduction Helpers
70
+
71
+ #pragma region Binary Sets
72
+
73
+ NK_PUBLIC void nk_hamming_u1_loongsonasx(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n, nk_u32_t *result) {
74
+ nk_size_t n_bytes = nk_size_divide_round_up_(n, NK_BITS_PER_BYTE);
75
+ __m256i count_u64x4 = __lasx_xvreplgr2vr_d(0);
76
+ nk_size_t i = 0;
77
+
78
+ for (; i + 32 <= n_bytes; i += 32) {
79
+ __m256i a_u8x32 = __lasx_xvld(a + i, 0);
80
+ __m256i b_u8x32 = __lasx_xvld(b + i, 0);
81
+ __m256i xor_u8x32 = __lasx_xvxor_v(a_u8x32, b_u8x32);
82
+ count_u64x4 = __lasx_xvadd_d(count_u64x4, __lasx_xvpcnt_d(xor_u8x32));
83
+ }
84
+
85
+ nk_u64_t count = nk_reduce_add_u64x4_loongsonasx_(count_u64x4);
86
+
87
+ for (; i < n_bytes; ++i) count += nk_u1x8_popcount_(a[i] ^ b[i]);
88
+ *result = (nk_u32_t)count;
89
+ }
90
+
91
+ NK_PUBLIC void nk_jaccard_u1_loongsonasx(nk_u1x8_t const *a, nk_u1x8_t const *b, nk_size_t n, nk_f32_t *result) {
92
+ nk_size_t n_bytes = nk_size_divide_round_up_(n, NK_BITS_PER_BYTE);
93
+ __m256i xor_count_u64x4 = __lasx_xvreplgr2vr_d(0);
94
+ __m256i or_count_u64x4 = __lasx_xvreplgr2vr_d(0);
95
+ nk_size_t i = 0;
96
+
97
+ for (; i + 32 <= n_bytes; i += 32) {
98
+ __m256i a_u8x32 = __lasx_xvld(a + i, 0);
99
+ __m256i b_u8x32 = __lasx_xvld(b + i, 0);
100
+ __m256i xor_u8x32 = __lasx_xvxor_v(a_u8x32, b_u8x32);
101
+ __m256i or_u8x32 = __lasx_xvor_v(a_u8x32, b_u8x32);
102
+ xor_count_u64x4 = __lasx_xvadd_d(xor_count_u64x4, __lasx_xvpcnt_d(xor_u8x32));
103
+ or_count_u64x4 = __lasx_xvadd_d(or_count_u64x4, __lasx_xvpcnt_d(or_u8x32));
104
+ }
105
+
106
+ nk_u64_t xor_count = nk_reduce_add_u64x4_loongsonasx_(xor_count_u64x4);
107
+ nk_u64_t or_count = nk_reduce_add_u64x4_loongsonasx_(or_count_u64x4);
108
+
109
+ for (; i < n_bytes; ++i) {
110
+ xor_count += nk_u1x8_popcount_(a[i] ^ b[i]);
111
+ or_count += nk_u1x8_popcount_(a[i] | b[i]);
112
+ }
113
+ *result = (or_count != 0) ? (nk_f32_t)xor_count / (nk_f32_t)or_count : 0.0f;
114
+ }
115
+
116
+ #pragma endregion Binary Sets
117
+
118
+ #pragma region Integer Sets
119
+
120
+ NK_PUBLIC void nk_hamming_u8_loongsonasx(nk_u8_t const *a, nk_u8_t const *b, nk_size_t n, nk_u32_t *result) {
121
+ __m256i count_u64x4 = __lasx_xvreplgr2vr_d(0);
122
+ __m256i ones_u8x32 = __lasx_xvreplgr2vr_b(1);
123
+ nk_size_t i = 0;
124
+
125
+ for (; i + 32 <= n; i += 32) {
126
+ __m256i a_u8x32 = __lasx_xvld(a + i, 0);
127
+ __m256i b_u8x32 = __lasx_xvld(b + i, 0);
128
+ __m256i xor_u8x32 = __lasx_xvxor_v(a_u8x32, b_u8x32);
129
+ __m256i min_u8x32 = __lasx_xvmin_bu(xor_u8x32, ones_u8x32);
130
+ __m256i sum_u16x16 = __lasx_xvhaddw_hu_bu(min_u8x32, min_u8x32);
131
+ __m256i sum_u32x8 = __lasx_xvhaddw_wu_hu(sum_u16x16, sum_u16x16);
132
+ __m256i sum_u64x4 = __lasx_xvhaddw_du_wu(sum_u32x8, sum_u32x8);
133
+ count_u64x4 = __lasx_xvadd_d(count_u64x4, sum_u64x4);
134
+ }
135
+
136
+ nk_u64_t count = nk_reduce_add_u64x4_loongsonasx_(count_u64x4);
137
+
138
+ for (; i < n; ++i) count += (a[i] != b[i]);
139
+ *result = (nk_u32_t)count;
140
+ }
141
+
142
+ #pragma endregion Integer Sets
143
+
144
+ #pragma region Batched Finalizers
145
+
146
+ /** @brief Hamming from_dot: computes pop_a + pop_b − 2 × dot for 4 pairs (LSX). */
147
+ NK_INTERNAL void nk_hamming_u32x4_from_dot_loongsonasx_(nk_b128_vec_t dots, nk_u32_t query_pop,
148
+ nk_b128_vec_t target_pops, nk_b128_vec_t *results) {
149
+ __m128i dots_u32x4 = dots.xmm;
150
+ __m128i query_u32x4 = __lsx_vreplgr2vr_w((int)query_pop);
151
+ __m128i target_u32x4 = target_pops.xmm;
152
+ results->xmm = __lsx_vsub_w(__lsx_vadd_w(query_u32x4, target_u32x4), __lsx_vslli_w(dots_u32x4, 1));
153
+ }
154
+
155
+ /** @brief Jaccard from_dot: computes 1 − dot / (pop_a + pop_b − dot) for 4 pairs (LSX). */
156
+ NK_INTERNAL void nk_jaccard_f32x4_from_dot_loongsonasx_(nk_b128_vec_t dots, nk_u32_t query_pop,
157
+ nk_b128_vec_t target_pops, nk_b128_vec_t *results) {
158
+ __m128 dot_f32x4 = __lsx_vffint_s_wu(dots.xmm);
159
+ __m128 query_f32x4 = nk_xvreplgr2vr_s_128_((nk_f32_t)query_pop);
160
+ __m128 target_f32x4 = __lsx_vffint_s_wu(target_pops.xmm);
161
+ __m128 union_f32x4 = __lsx_vfsub_s(__lsx_vfadd_s(query_f32x4, target_f32x4), dot_f32x4);
162
+
163
+ __m128 zero_f32x4 = (__m128)__lsx_vreplgr2vr_w(0);
164
+ __m128 one_f32x4 = nk_xvreplgr2vr_s_128_(1.0f);
165
+ __m128i zero_union_mask_u32x4 = __lsx_vfcmp_ceq_s(union_f32x4, zero_f32x4);
166
+ __m128 safe_union_f32x4 = (__m128)__lsx_vbitsel_v((__m128i)union_f32x4, (__m128i)one_f32x4, zero_union_mask_u32x4);
167
+
168
+ __m128 ratio_f32x4 = __lsx_vfdiv_s(dot_f32x4, safe_union_f32x4);
169
+ __m128 jaccard_f32x4 = __lsx_vfsub_s(one_f32x4, ratio_f32x4);
170
+ results->xmm_ps = (__m128)__lsx_vbitsel_v((__m128i)jaccard_f32x4, (__m128i)zero_f32x4, zero_union_mask_u32x4);
171
+ }
172
+
173
+ #pragma endregion Batched Finalizers
174
+
175
+ #if defined(__cplusplus)
176
+ } // extern "C"
177
+ #endif
178
+
179
+ #endif // NK_TARGET_LOONGSONASX
180
+ #endif // NK_TARGET_LOONGARCH64_
181
+ #endif // NK_SET_LOONGSONASX_H