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,579 @@
1
+ #!/usr/bin/env python3
2
+ """Test pairwise cross-distances: nk.cdist for all metric families.
3
+
4
+ Dtypes: float64, float32, float16, bfloat16, int8, uint8, sub-byte floats.
5
+ Baselines: SciPy cdist.
6
+ Matches C++ suite: test_cross_*.cpp.
7
+ """
8
+
9
+ import atexit
10
+ from typing import TYPE_CHECKING
11
+
12
+ import pytest
13
+
14
+ if TYPE_CHECKING:
15
+ import numpy as np # static-analysis-only; the runtime try/except below is authoritative
16
+
17
+ try:
18
+ import numpy as np
19
+
20
+ numpy_available = True
21
+ except Exception:
22
+ numpy_available = False
23
+
24
+ try:
25
+ from scipy.stats import entropy as scipy_entropy
26
+ except ImportError:
27
+ scipy_entropy = None # type: ignore[assignment]
28
+
29
+ import numkong as nk
30
+ from test_base import (
31
+ NK_ATOL,
32
+ NK_RTOL,
33
+ PACKING_GRANULARITY,
34
+ assert_allclose,
35
+ create_stats,
36
+ dense_dimensions,
37
+ keep_one_capability,
38
+ make_random,
39
+ ml_dtypes_available,
40
+ nk_seed, # noqa: F401 — pytest fixture
41
+ numpy_available,
42
+ possible_capabilities,
43
+ print_stats_report,
44
+ randomized_repetitions_count,
45
+ round_up_to,
46
+ scipy_available,
47
+ scipy_metric_name,
48
+ seed_rng, # noqa: F401 — pytest fixture (autouse)
49
+ )
50
+
51
+ try:
52
+ import scipy.spatial.distance as spd
53
+ except ImportError:
54
+ spd = None # type: ignore[assignment]
55
+
56
+ try:
57
+ import ml_dtypes
58
+ except ImportError:
59
+ ml_dtypes = None # type: ignore[assignment]
60
+
61
+ stats = create_stats()
62
+ atexit.register(print_stats_report, stats)
63
+
64
+
65
+ def round_and_clip_even(values, out_dtype):
66
+ nk_dtype_conversion_info = np.iinfo(out_dtype)
67
+ finite_values = np.nan_to_num(
68
+ values, nan=0.0, posinf=float(nk_dtype_conversion_info.max), neginf=float(nk_dtype_conversion_info.min)
69
+ )
70
+ clipped_values = np.clip(finite_values, nk_dtype_conversion_info.min, nk_dtype_conversion_info.max)
71
+ return np.rint(clipped_values).astype(out_dtype)
72
+
73
+
74
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
75
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
76
+ @pytest.mark.parametrize("ndim", dense_dimensions)
77
+ @pytest.mark.parametrize("input_dtype", ["float64", "float32"])
78
+ @pytest.mark.parametrize("metric", ["dot", "angular", "euclidean"])
79
+ @pytest.mark.parametrize("capability", possible_capabilities)
80
+ def test_cdist_batch_metrics(ndim, input_dtype, metric, capability):
81
+ """Verify the SIMD batch dispatch for dot, angular, and euclidean.
82
+
83
+ Sets ``out_dtype`` equal to ``input_dtype`` (float64 or float32) so that the
84
+ kernel's native output type matches and the fast packed/symmetric batch path
85
+ is selected instead of the scalar pairwise fallback. Uses asymmetric matrix
86
+ sizes (7 x 11) to exercise the general rectangular case.
87
+
88
+ Dimensions are inherited from ``NK_DENSE_DIMENSIONS``; capabilities from
89
+ platform auto-detection via ``possible_capabilities``. Baseline for ``dot``
90
+ is ``np.dot`` (SciPy has no ``cdist`` metric for inner product); other
91
+ metrics use ``scipy.spatial.distance.cdist``.
92
+ """
93
+ keep_one_capability(capability)
94
+
95
+ num_rows_a, num_rows_b = 7, 11
96
+ a_matrix = np.random.randn(num_rows_a, ndim).astype(input_dtype)
97
+ b_matrix = np.random.randn(num_rows_b, ndim).astype(input_dtype)
98
+
99
+ # Use native out_dtype to force batch path (float64->float64, float32->float32)
100
+ out_dtype = input_dtype
101
+ scipy_metric = scipy_metric_name(metric)
102
+
103
+ if metric == "dot":
104
+ expected = np.array(
105
+ [[np.dot(a_matrix[i], b_matrix[j]) for j in range(num_rows_b)] for i in range(num_rows_a)]
106
+ ).astype(out_dtype)
107
+ else:
108
+ expected = spd.cdist(a_matrix, b_matrix, scipy_metric).astype(out_dtype)
109
+
110
+ result = nk.cdist(a_matrix, b_matrix, metric=metric, out_dtype=out_dtype)
111
+ assert_allclose(result, expected, atol=NK_ATOL, rtol=NK_RTOL)
112
+
113
+
114
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
115
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
116
+ @pytest.mark.parametrize("ndim", dense_dimensions)
117
+ @pytest.mark.parametrize("input_dtype", ["float64", "float32"])
118
+ @pytest.mark.parametrize("metric", ["dot", "angular", "euclidean", "sqeuclidean"])
119
+ def test_cdist_self_distance(ndim, input_dtype, metric):
120
+ """Verify ``cdist(A, A)`` produces a complete, correct symmetric matrix.
121
+
122
+ When both operands are the same object the C code takes a symmetric batch
123
+ shortcut that only computes the upper triangle. A post-kernel mirror loop
124
+ must fill the lower triangle. This test checks:
125
+
126
+ 1. Default ``out_dtype=float64`` (pairwise fallback on f32 input) — full
127
+ matrix against SciPy.
128
+ 2. Native ``out_dtype=input_dtype`` (forces batch symmetric path) — full
129
+ matrix, with an explicit lower-triangle mask assertion to catch missing
130
+ mirror writes.
131
+
132
+ No ``capability`` parameter: runs on whatever the default backend is (all
133
+ ISA-specific paths are already covered by ``test_cdist_batch_metrics``).
134
+ Dimensions from ``NK_DENSE_DIMENSIONS``.
135
+ """
136
+ a_matrix = np.random.randn(10, ndim).astype(input_dtype)
137
+
138
+ scipy_metric = scipy_metric_name(metric)
139
+ if metric == "dot":
140
+ expected = np.array([[np.dot(a_matrix[i], a_matrix[j]) for j in range(10)] for i in range(10)]).astype(
141
+ np.float64
142
+ )
143
+ else:
144
+ expected = spd.cdist(a_matrix, a_matrix, scipy_metric)
145
+
146
+ # Default out_dtype (f64) — may use pairwise fallback
147
+ result_default = np.asarray(nk.cdist(a_matrix, a_matrix, metric=metric))
148
+ assert_allclose(result_default, expected, atol=NK_ATOL, rtol=NK_RTOL)
149
+ # Check lower triangle explicitly
150
+ mask_lower = np.tril(np.ones((10, 10), dtype=bool), k=-1)
151
+ assert_allclose(result_default[mask_lower], expected[mask_lower], atol=NK_ATOL, rtol=NK_RTOL)
152
+
153
+ # Native out_dtype — should force batch symmetric path for f32
154
+ native_out_dtype = input_dtype
155
+ if metric == "dot":
156
+ expected_native = np.array([[np.dot(a_matrix[i], a_matrix[j]) for j in range(10)] for i in range(10)]).astype(
157
+ native_out_dtype
158
+ )
159
+ else:
160
+ expected_native = spd.cdist(a_matrix, a_matrix, scipy_metric).astype(native_out_dtype)
161
+ result_native = np.asarray(nk.cdist(a_matrix, a_matrix, metric=metric, out_dtype=native_out_dtype))
162
+ assert_allclose(result_native, expected_native, atol=NK_ATOL, rtol=NK_RTOL)
163
+ assert_allclose(result_native[mask_lower], expected_native[mask_lower], atol=NK_ATOL, rtol=NK_RTOL)
164
+
165
+
166
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
167
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
168
+ @pytest.mark.parametrize("ndim", dense_dimensions)
169
+ @pytest.mark.parametrize("input_dtype", ["float64", "float32", "float16"])
170
+ @pytest.mark.parametrize("out_dtype", [None, "float32", "int32"])
171
+ @pytest.mark.parametrize("metric", ["angular", "sqeuclidean", "euclidean", "dot"])
172
+ @pytest.mark.parametrize("capability", possible_capabilities)
173
+ def test_cdist_float_accuracy(ndim, input_dtype, out_dtype, metric, capability):
174
+ """Broad coverage of cdist for standard IEEE float inputs.
175
+
176
+ Exercises four metrics (angular, sqeuclidean, euclidean, dot) across three
177
+ input dtypes (float64, float32, float16) and three output modes (default
178
+ float64, explicit float32, explicit int32). Inputs are intentionally
179
+ *strided* — sliced from wider arrays — so the kernel must respect non-
180
+ contiguous memory layouts.
181
+
182
+ Additionally tests the ``out=`` buffer path: a strided slice of a wider
183
+ allocation is passed and checked for correct in-place writes.
184
+
185
+ Skips:
186
+ * ``angular`` at ndim=1 — degenerate (norm is a single element, 0/0).
187
+
188
+ Dimensions from ``NK_DENSE_DIMENSIONS``; capabilities from platform
189
+ auto-detection. Integer output uses ``atol=1`` (discrete rounding);
190
+ floats use ``NK_ATOL / NK_RTOL``.
191
+ """
192
+ if metric == "angular" and ndim == 1:
193
+ pytest.skip("angular at ndim=1 is degenerate (0/0 from single-element norms)")
194
+ keep_one_capability(capability)
195
+
196
+ num_rows_a, num_rows_b = 10, 15
197
+ a_matrix_extended = np.random.randn(num_rows_a, ndim + 1).astype(input_dtype)
198
+ b_matrix_extended = np.random.randn(num_rows_b, ndim + 3).astype(input_dtype)
199
+ a_matrix = a_matrix_extended[:, :ndim]
200
+ b_matrix = b_matrix_extended[:, :ndim]
201
+
202
+ is_integer_output = out_dtype in ("int32", "int64", "int16", "int8", "uint32", "uint64", "uint16", "uint8")
203
+ scipy_metric = scipy_metric_name(metric)
204
+
205
+ if metric == "dot":
206
+ baseline = np.array(
207
+ [
208
+ [np.dot(a_matrix[i].astype(np.float64), b_matrix[j].astype(np.float64)) for j in range(num_rows_b)]
209
+ for i in range(num_rows_a)
210
+ ]
211
+ )
212
+ else:
213
+ baseline = spd.cdist(a_matrix, b_matrix, scipy_metric)
214
+
215
+ if out_dtype is None:
216
+ expected = baseline
217
+ result = nk.cdist(a_matrix, b_matrix, metric)
218
+ else:
219
+ expected = round_and_clip_even(baseline, out_dtype) if is_integer_output else baseline.astype(out_dtype)
220
+ result = nk.cdist(a_matrix, b_matrix, metric, out_dtype=out_dtype)
221
+
222
+ atol = 1 if is_integer_output else NK_ATOL
223
+ assert_allclose(result, expected, atol=atol, rtol=NK_RTOL)
224
+
225
+ # Test out= buffer with strides
226
+ out_np_dtype = out_dtype if out_dtype else "float64"
227
+ output_buffer_extended = np.zeros((num_rows_a, num_rows_b + 7), dtype=out_np_dtype)
228
+ output_buffer = output_buffer_extended[:, :num_rows_b]
229
+ assert nk.cdist(a_matrix, b_matrix, metric, out=output_buffer) is None
230
+ assert_allclose(output_buffer, expected, atol=atol, rtol=NK_RTOL)
231
+
232
+
233
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
234
+ @pytest.mark.parametrize("ndim", dense_dimensions)
235
+ @pytest.mark.parametrize("input_dtype", ["complex128", "complex64"])
236
+ @pytest.mark.parametrize("out_dtype", [None, "complex128", "complex64"])
237
+ @pytest.mark.parametrize("metric", ["dot", "vdot"])
238
+ @pytest.mark.parametrize("capability", possible_capabilities)
239
+ def test_cdist_complex(ndim, input_dtype, out_dtype, metric, capability):
240
+ """Verify cdist for complex-valued dot and vdot metrics.
241
+
242
+ Tests three output modes (default complex128, explicit complex128,
243
+ explicit complex64) for both ``dot`` (Hermitian-unaware) and ``vdot``
244
+ (conjugate dot) metrics. Exercises:
245
+
246
+ * 1D scalar result (single row vs single row).
247
+ * 2D matrix result (10 x 15).
248
+ * ``out=`` buffer path with strided column slice.
249
+
250
+ Inputs are strided (sliced from wider allocations). Dimensions from
251
+ ``NK_DENSE_DIMENSIONS``; capabilities from platform auto-detection.
252
+ """
253
+ keep_one_capability(capability)
254
+
255
+ num_rows_a, num_rows_b = 10, 15
256
+ a_matrix_extended = np.random.randn(num_rows_a, ndim + 1).astype(input_dtype)
257
+ b_matrix_extended = np.random.randn(num_rows_b, ndim + 3).astype(input_dtype)
258
+ a_matrix = a_matrix_extended[:, :ndim]
259
+ b_matrix = b_matrix_extended[:, :ndim]
260
+ c_matrix_extended = np.random.randn(num_rows_a, num_rows_b + 7).astype(out_dtype if out_dtype else np.complex128)
261
+ c_matrix = c_matrix_extended[:, :num_rows_b]
262
+
263
+ expected = np.zeros((num_rows_a, num_rows_b), dtype=out_dtype if out_dtype else np.complex128)
264
+ baseline_kernel = np.dot if metric == "dot" else np.vdot
265
+ for i in range(num_rows_a):
266
+ for j in range(num_rows_b):
267
+ expected[i, j] = baseline_kernel(a_matrix[i], b_matrix[j])
268
+
269
+ if out_dtype is None:
270
+ result1d = nk.cdist(a_matrix[0], b_matrix[0], metric=metric)
271
+ result2d = nk.cdist(a_matrix, b_matrix, metric=metric)
272
+ assert nk.cdist(a_matrix, b_matrix, metric=metric, out=c_matrix) is None
273
+ else:
274
+ expected = expected.astype(out_dtype)
275
+ result1d = nk.cdist(a_matrix[0], b_matrix[0], metric=metric, out_dtype=out_dtype)
276
+ result2d = nk.cdist(a_matrix, b_matrix, metric=metric, out_dtype=out_dtype)
277
+ assert nk.cdist(a_matrix, b_matrix, metric=metric, out_dtype=out_dtype, out=c_matrix) is None
278
+
279
+ assert_allclose(result1d, expected[0, 0], atol=NK_ATOL, rtol=NK_RTOL)
280
+ assert_allclose(result2d, expected, atol=NK_ATOL, rtol=NK_RTOL)
281
+ assert_allclose(c_matrix, expected, atol=NK_ATOL, rtol=NK_RTOL)
282
+
283
+
284
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
285
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
286
+ @pytest.mark.repeat(randomized_repetitions_count)
287
+ @pytest.mark.parametrize("ndim", dense_dimensions)
288
+ @pytest.mark.parametrize("out_dtype", [None, "float32", "float16", "int8"])
289
+ @pytest.mark.parametrize("capability", possible_capabilities)
290
+ def test_cdist_hamming(ndim, out_dtype, capability):
291
+ """Verify cdist Hamming distance on packed bit vectors.
292
+
293
+ Generates random binary matrices, packs them via ``np.packbits``, and passes
294
+ ``dtype="uint1"`` so the kernel interprets each byte as 8 bits. Baseline is
295
+ ``scipy.spatial.distance.cdist(bits, bits, "hamming") * ndim`` — SciPy
296
+ normalises by dimension, so we undo that.
297
+
298
+ Output dtype coverage: default (float64), float32, float16, int8. Integer
299
+ output uses standard ``NK_ATOL / NK_RTOL`` (Hamming counts are exact for
300
+ integer types but may round for float16).
301
+
302
+ Randomised via ``@pytest.mark.repeat(randomized_repetitions_count)`` (env
303
+ ``NK_RANDOMIZED_REPETITIONS``, default 1). Dimensions from
304
+ ``NK_DENSE_DIMENSIONS``; capabilities from platform auto-detection.
305
+ """
306
+ keep_one_capability(capability)
307
+
308
+ num_rows_a, num_rows_b = 10, 15
309
+ a_bits = np.random.randint(2, size=(num_rows_a, ndim)).astype(np.uint8)
310
+ b_bits = np.random.randint(2, size=(num_rows_b, ndim)).astype(np.uint8)
311
+ a_packed_bits, b_packed_bits = np.packbits(a_bits, axis=1), np.packbits(b_bits, axis=1)
312
+
313
+ if out_dtype is None:
314
+ expected = spd.cdist(a_bits, b_bits, "hamming") * ndim
315
+ result = nk.cdist(a_packed_bits, b_packed_bits, metric="hamming", dtype="uint1")
316
+ else:
317
+ raw = spd.cdist(a_bits, b_bits, "hamming") * ndim
318
+ if np.issubdtype(np.dtype(out_dtype), np.integer):
319
+ expected = round_and_clip_even(raw, out_dtype)
320
+ else:
321
+ expected = raw.astype(out_dtype)
322
+ result = nk.cdist(a_packed_bits, b_packed_bits, metric="hamming", dtype="uint1", out_dtype=out_dtype)
323
+
324
+ assert_allclose(result, expected, atol=NK_ATOL, rtol=NK_RTOL)
325
+
326
+
327
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
328
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
329
+ @pytest.mark.repeat(randomized_repetitions_count)
330
+ @pytest.mark.parametrize("ndim", dense_dimensions)
331
+ @pytest.mark.parametrize("out_dtype", [None, "float32"])
332
+ @pytest.mark.parametrize("capability", possible_capabilities)
333
+ def test_cdist_jaccard(ndim, out_dtype, capability):
334
+ """Verify cdist Jaccard distance on packed bit vectors.
335
+
336
+ Same ``np.packbits`` + ``dtype="uint1"`` pattern as the Hamming test, but
337
+ with ``metric="jaccard"``. Baseline is ``scipy.spatial.distance.cdist``
338
+ with ``"jaccard"`` (already normalised — ratio of disagreeing bits to bits
339
+ where at least one is set).
340
+
341
+ Output dtype coverage: default (float64) and explicit float32.
342
+
343
+ Randomised via ``@pytest.mark.repeat``; dimensions from
344
+ ``NK_DENSE_DIMENSIONS``; capabilities from platform auto-detection.
345
+ """
346
+ keep_one_capability(capability)
347
+
348
+ num_rows_a, num_rows_b = 10, 15
349
+ a_bits = np.random.randint(2, size=(num_rows_a, ndim)).astype(np.uint8)
350
+ b_bits = np.random.randint(2, size=(num_rows_b, ndim)).astype(np.uint8)
351
+ a_packed_bits, b_packed_bits = np.packbits(a_bits, axis=1), np.packbits(b_bits, axis=1)
352
+
353
+ if out_dtype is None:
354
+ expected = spd.cdist(a_bits, b_bits, "jaccard")
355
+ result = nk.cdist(a_packed_bits, b_packed_bits, metric="jaccard", dtype="uint1")
356
+ else:
357
+ expected = spd.cdist(a_bits, b_bits, "jaccard").astype(out_dtype)
358
+ result = nk.cdist(a_packed_bits, b_packed_bits, metric="jaccard", dtype="uint1", out_dtype=out_dtype)
359
+
360
+ assert_allclose(result, expected, atol=NK_ATOL, rtol=NK_RTOL)
361
+
362
+
363
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
364
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
365
+ @pytest.mark.parametrize("ndim", dense_dimensions)
366
+ @pytest.mark.parametrize("input_dtype", ["float64", "float32"])
367
+ @pytest.mark.parametrize("metric", ["kld", "jsd"])
368
+ @pytest.mark.parametrize("capability", possible_capabilities)
369
+ def test_cdist_probability(ndim, input_dtype, metric, capability):
370
+ """Verify cdist for Kullback-Leibler divergence and Jensen-Shannon distance.
371
+
372
+ Inputs are positive probability vectors (softmax of randn + epsilon) using
373
+ asymmetric 7 x 11 matrix sizes.
374
+
375
+ Baselines:
376
+ * **KLD** — ``scipy_entropy(p, q)`` which computes
377
+ ``sum(p * ln(p/q))`` in natural log (nats), matching ``nk.kld``.
378
+ Note: KLD is asymmetric, so ``cdist(A, B)`` != ``cdist(B, A)``.
379
+ * **JSD** — ``scipy.spatial.distance.jensenshannon`` with default
380
+ ``base=e`` (natural log), returning ``sqrt(JS divergence)`` which
381
+ matches ``nk.jsd`` directly.
382
+
383
+ Only float64 and float32 input dtypes are supported. No ``out_dtype``
384
+ variants — probability divergences always produce float64 output.
385
+ Dimensions from ``NK_DENSE_DIMENSIONS``; capabilities from platform
386
+ auto-detection.
387
+ """
388
+ keep_one_capability(capability)
389
+
390
+ num_rows_a, num_rows_b = 7, 11
391
+ # Positive normalized vectors (softmax of randn)
392
+ a_raw = np.abs(np.random.randn(num_rows_a, ndim)).astype(input_dtype) + 1e-6
393
+ b_raw = np.abs(np.random.randn(num_rows_b, ndim)).astype(input_dtype) + 1e-6
394
+ a_matrix = (a_raw / a_raw.sum(axis=1, keepdims=True)).astype(input_dtype)
395
+ b_matrix = (b_raw / b_raw.sum(axis=1, keepdims=True)).astype(input_dtype)
396
+
397
+ if metric == "kld":
398
+ # scipy_entropy(p, q) = sum(p * ln(p/q)), natural log, matches nk.kld
399
+ expected = spd.cdist(
400
+ a_matrix.astype(np.float64),
401
+ b_matrix.astype(np.float64),
402
+ lambda u, v: scipy_entropy(u, v),
403
+ )
404
+ else:
405
+ # spd.jensenshannon defaults to base=e (natural log), returns sqrt(JS divergence)
406
+ expected = spd.cdist(a_matrix.astype(np.float64), b_matrix.astype(np.float64), "jensenshannon")
407
+
408
+ result = nk.cdist(a_matrix, b_matrix, metric=metric)
409
+ assert_allclose(result, expected, atol=NK_ATOL, rtol=NK_RTOL)
410
+
411
+
412
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
413
+ @pytest.mark.parametrize("ndim", dense_dimensions)
414
+ @pytest.mark.parametrize(
415
+ "input_dtype",
416
+ ["bfloat16", "e4m3", "e5m2", "e2m3", "e3m2", "int8", "uint8", "int4", "uint4"],
417
+ )
418
+ @pytest.mark.parametrize("metric", ["dot", "euclidean"])
419
+ def test_cdist_exotic_dtypes(ndim, input_dtype, metric):
420
+ """Verify cdist for sub-byte and non-standard types via pairwise fallback.
421
+
422
+ Covers bfloat16, e4m3, e5m2, e2m3, e3m2, int8, uint8, int4, and uint4 for
423
+ the ``dot`` and ``euclidean`` metrics. These dtypes have no batch-path
424
+ kernel, so cdist always falls back to the scalar pairwise loop with float64
425
+ output.
426
+
427
+ Baselines are computed from the float64 arrays returned by ``make_random``
428
+ (second return value), avoiding sub-byte row-indexing issues on
429
+ ``nk.Tensor``. Raw numpy byte-arrays are passed to cdist with an explicit
430
+ ``dtype=`` parameter so the C code knows how to interpret the data.
431
+
432
+ Sub-byte types (int4, uint4, uint1) have their ``ndim`` rounded up to
433
+ the packing alignment (2 for nibble types, 8 for bit types) so the
434
+ kernel sees a whole number of packed bytes per row.
435
+
436
+ No ``capability`` parameter — only the default backend is tested.
437
+ Dimensions from ``NK_DENSE_DIMENSIONS``.
438
+ """
439
+ ndim = round_up_to(ndim, PACKING_GRANULARITY.get(input_dtype, 1))
440
+
441
+ num_rows_a, num_rows_b = 5, 7
442
+ a_raw, a_baseline = make_random((num_rows_a, ndim), input_dtype)
443
+ b_raw, b_baseline = make_random((num_rows_b, ndim), input_dtype)
444
+
445
+ # Baseline: compute from float64 baseline arrays (avoids sub-byte row indexing issues)
446
+ if metric == "dot":
447
+ expected = a_baseline @ b_baseline.T
448
+ else:
449
+ expected = np.zeros((num_rows_a, num_rows_b), dtype=np.float64)
450
+ for i in range(num_rows_a):
451
+ for j in range(num_rows_b):
452
+ expected[i, j] = np.sqrt(np.sum((a_baseline[i] - b_baseline[j]) ** 2))
453
+
454
+ # Use raw numpy arrays with explicit dtype= for sub-byte/exotic types
455
+ cdist_kwargs = {"metric": metric, "dtype": input_dtype}
456
+
457
+ # Test with default out_dtype (f64, pairwise fallback)
458
+ result_f64 = nk.cdist(a_raw, b_raw, **cdist_kwargs)
459
+ assert_allclose(result_f64, expected, atol=NK_ATOL, rtol=NK_RTOL)
460
+
461
+
462
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
463
+ @pytest.mark.skipif(not scipy_available, reason="SciPy is not installed")
464
+ @pytest.mark.parametrize(
465
+ "m,n,k",
466
+ [(1, 1, 8), (1, 5, 16), (5, 1, 16), (1, 1, 1), (2, 3, 4096), (13, 17, 97)],
467
+ )
468
+ def test_cdist_shapes(m, n, k):
469
+ """Verify cdist output shape and correctness for diverse matrix geometries.
470
+
471
+ Uses a hardcoded list of ``(m, n, k)`` triples that exercise edge cases
472
+ the dimension-parameterised tests miss:
473
+
474
+ * ``(1, 1, 8)`` — minimal single-row × single-row.
475
+ * ``(1, 5, 16)`` and ``(5, 1, 16)`` — 1-row broadcasting in each direction.
476
+ * ``(1, 1, 1)`` — absolute minimum dimensions.
477
+ * ``(2, 3, 4096)`` — large vector depth (exercises SIMD tail handling).
478
+ * ``(13, 17, 97)`` — odd prime dimensions with no alignment.
479
+
480
+ Both ``euclidean`` (may use batch path) and ``sqeuclidean`` (pairwise only)
481
+ are tested on float32 input with default float64 output. Asserts both
482
+ correctness (against SciPy) and output shape ``(m, n)``.
483
+
484
+ Not parameterised by capability — shape handling is ISA-independent.
485
+ """
486
+ a_matrix = np.random.randn(m, k).astype(np.float32)
487
+ b_matrix = np.random.randn(n, k).astype(np.float32)
488
+
489
+ # euclidean (may use batch path)
490
+ expected_euc = spd.cdist(a_matrix, b_matrix, "euclidean")
491
+ result_euc = nk.cdist(a_matrix, b_matrix, "euclidean")
492
+ assert result_euc.shape == (m, n), f"Expected shape ({m}, {n}), got {result_euc.shape}"
493
+ assert_allclose(result_euc, expected_euc, atol=NK_ATOL, rtol=NK_RTOL)
494
+
495
+ # sqeuclidean (pairwise)
496
+ expected_sqeuc = spd.cdist(a_matrix, b_matrix, "sqeuclidean")
497
+ result_sqeuc = nk.cdist(a_matrix, b_matrix, "sqeuclidean")
498
+ assert result_sqeuc.shape == (m, n), f"Expected shape ({m}, {n}), got {result_sqeuc.shape}"
499
+ assert_allclose(result_sqeuc, expected_sqeuc, atol=NK_ATOL, rtol=NK_RTOL)
500
+
501
+
502
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
503
+ @pytest.mark.parametrize("m", [31, 64, 129])
504
+ def test_cdist_threads(m):
505
+ """Verify OpenMP-parallel cdist matches the serial path across tile boundaries.
506
+
507
+ Exercises both the symmetric batch path (A vs A) and the packed batch path
508
+ (A vs B). Packed tile is 64, symmetric tile is 32, so ``m`` values are
509
+ chosen to straddle one and two tiles.
510
+ """
511
+ k = 32
512
+ a_matrix = np.random.randn(m, k).astype(np.float32)
513
+ b_matrix = np.random.randn(m, k).astype(np.float32)
514
+
515
+ # Symmetric batch path
516
+ serial_symmetric = nk.cdist(a_matrix, a_matrix, "sqeuclidean", threads=1)
517
+ parallel_symmetric = nk.cdist(a_matrix, a_matrix, "sqeuclidean", threads=4)
518
+ assert_allclose(parallel_symmetric, serial_symmetric, atol=NK_ATOL, rtol=NK_RTOL)
519
+
520
+ # Packed batch path
521
+ serial_packed = nk.cdist(a_matrix, b_matrix, "euclidean", threads=1)
522
+ parallel_packed = nk.cdist(a_matrix, b_matrix, "euclidean", threads=4)
523
+ assert_allclose(parallel_packed, serial_packed, atol=NK_ATOL, rtol=NK_RTOL)
524
+
525
+
526
+ def test_cdist_edge_cases(nk_seed):
527
+ """Verify cdist edge cases: scalar return, error handling, and kwarg validation.
528
+
529
+ Covers four categories:
530
+
531
+ 1. **Scalar return** — passing two 1D vectors ``(D,)`` must return a scalar
532
+ float, not a ``(1, 1)`` matrix.
533
+ 2. **Accepted kwargs** — ``threads=`` controls OpenMP parallelism and must
534
+ produce results identical to the single-threaded path.
535
+ 3. **Rejected kwargs** — unknown keywords must raise ``TypeError`` with
536
+ "unexpected keyword" so callers get a clear error.
537
+ 4. **Input validation** — mismatched dimensions and empty matrices must
538
+ raise ``ValueError``.
539
+
540
+ Not parameterised — uses fixed 16-element vectors on float32.
541
+ """
542
+ ndim = 16
543
+ a_vec = nk.hash((ndim,), seed=nk_seed, dtype="float32")
544
+ b_vec = nk.hash((ndim,), seed=nk_seed + 1, dtype="float32")
545
+
546
+ # 1D vectors → scalar float return, not matrix
547
+ result = nk.cdist(a_vec, b_vec, "euclidean")
548
+ assert isinstance(result, (int, float)), f"Expected scalar for 1D inputs, got {type(result)}"
549
+
550
+ # threads= is accepted and must match the single-threaded result
551
+ a_matrix = nk.ones((4, ndim), dtype="float32")
552
+ b_matrix = nk.ones((4, ndim), dtype="float32")
553
+ result_serial = nk.cdist(a_matrix, b_matrix, "euclidean", threads=1)
554
+ result_parallel = nk.cdist(a_matrix, b_matrix, "euclidean", threads=2)
555
+ assert_allclose(result_serial, result_parallel, atol=NK_ATOL, rtol=NK_RTOL)
556
+
557
+ # Unknown kwargs are rejected
558
+ with pytest.raises(TypeError, match="unexpected keyword"):
559
+ nk.cdist(a_matrix, b_matrix, not_a_real_kwarg=2)
560
+
561
+ # Mismatched dimensions → ValueError
562
+ with pytest.raises(ValueError):
563
+ nk.cdist(nk.ones((2, 3), dtype="float32"), nk.ones((2, 5), dtype="float32"), "euclidean")
564
+
565
+ # Empty matrix → ValueError
566
+ with pytest.raises(ValueError):
567
+ nk.cdist(nk.ones((0, 3), dtype="float32"), nk.ones((2, 3), dtype="float32"), "euclidean")
568
+
569
+
570
+ @pytest.mark.skipif(not numpy_available, reason="NumPy is not installed")
571
+ @pytest.mark.skipif(not ml_dtypes_available, reason="ml_dtypes not installed")
572
+ @pytest.mark.parametrize("ml_dtype", ["bfloat16", "float8_e4m3fn", "float8_e5m2", "float6_e2m3fn", "float6_e3m2fn"])
573
+ def test_cdist_with_ml_dtypes(ml_dtype):
574
+ """Verify cdist accepts ml_dtypes arrays via __array_interface__ fallback."""
575
+ dt = getattr(ml_dtypes, ml_dtype)
576
+ a = np.random.randn(4, 8).astype(np.float32).clip(-1, 1).astype(dt)
577
+ b = np.random.randn(4, 8).astype(np.float32).clip(-1, 1).astype(dt)
578
+ result = nk.cdist(a, b, "dot")
579
+ assert result.shape == (4, 4)