usearch 2.21.4 → 2.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -69
- package/binding.gyp +15 -37
- package/c/CMakeLists.txt +4 -5
- package/c/README.md +8 -8
- package/c/lib.cpp +32 -14
- package/c/test.c +124 -1
- package/c/usearch.h +24 -3
- package/cpp/CMakeLists.txt +4 -0
- package/cpp/README.md +5 -5
- package/cpp/bench.cpp +97 -116
- package/cpp/test.cpp +117 -20
- package/include/usearch/index.hpp +224 -48
- package/include/usearch/index_dense.hpp +105 -40
- package/include/usearch/index_plugins.hpp +1387 -204
- package/javascript/README.md +1 -1
- package/javascript/dist/cjs/usearch.d.ts +8 -3
- package/javascript/dist/cjs/usearch.js +12 -4
- package/javascript/dist/esm/usearch.d.ts +8 -3
- package/javascript/dist/esm/usearch.js +12 -4
- package/javascript/lib.cpp +17 -0
- package/javascript/usearch.test.js +117 -94
- package/javascript/usearch.ts +14 -6
- package/numkong/.clang-tidy +30 -0
- package/{simsimd → numkong}/.cmake-format.py +18 -0
- package/numkong/.editorconfig +12 -0
- package/numkong/.git-blame-ignore-revs +123 -0
- package/numkong/.mailmap +15 -0
- package/numkong/.spi.yml +11 -0
- package/numkong/.swift-format +14 -0
- package/numkong/CITATION.cff +81 -0
- package/numkong/CONTRIBUTING.md +500 -0
- package/numkong/Cargo.toml +54 -0
- package/numkong/MANIFEST.in +8 -0
- package/numkong/Package.swift +84 -0
- package/numkong/README.md +569 -0
- package/numkong/bench/README.md +217 -0
- package/numkong/bench/bench-browser.html +323 -0
- package/numkong/bench/bench.cpp +344 -0
- package/numkong/bench/bench.hpp +816 -0
- package/numkong/bench/bench.js +148 -0
- package/numkong/bench/bench.mjs +616 -0
- package/numkong/bench/bench_attention.cpp +113 -0
- package/numkong/bench/bench_cast.cpp +264 -0
- package/numkong/bench/bench_cross_amx.cpp +62 -0
- package/numkong/bench/bench_cross_arm.cpp +245 -0
- package/numkong/bench/bench_cross_blas.cpp +309 -0
- package/numkong/bench/bench_cross_loongarch.cpp +106 -0
- package/numkong/bench/bench_cross_power.cpp +84 -0
- package/numkong/bench/bench_cross_rvv.cpp +121 -0
- package/numkong/bench/bench_cross_serial.cpp +151 -0
- package/numkong/bench/bench_cross_sme.cpp +163 -0
- package/numkong/bench/bench_cross_wasm.cpp +142 -0
- package/numkong/bench/bench_cross_x86.cpp +455 -0
- package/numkong/bench/bench_curved.cpp +210 -0
- package/numkong/bench/bench_dot.cpp +315 -0
- package/numkong/bench/bench_each.cpp +503 -0
- package/numkong/bench/bench_geospatial.cpp +112 -0
- package/numkong/bench/bench_maxsim.cpp +154 -0
- package/numkong/bench/bench_mesh.cpp +148 -0
- package/numkong/bench/bench_probability.cpp +60 -0
- package/numkong/bench/bench_reduce.cpp +371 -0
- package/numkong/bench/bench_set.cpp +73 -0
- package/numkong/bench/bench_sparse.cpp +198 -0
- package/numkong/bench/bench_spatial.cpp +347 -0
- package/numkong/bench/bench_trigonometry.cpp +137 -0
- package/numkong/bench/swift/Bench.swift +506 -0
- package/numkong/binding.gyp +169 -0
- package/numkong/build.rs +484 -0
- package/numkong/c/dispatch.h +512 -0
- package/numkong/c/dispatch_bf16.c +447 -0
- package/numkong/c/dispatch_bf16c.c +52 -0
- package/numkong/c/dispatch_e2m3.c +296 -0
- package/numkong/c/dispatch_e3m2.c +284 -0
- package/numkong/c/dispatch_e4m3.c +318 -0
- package/numkong/c/dispatch_e5m2.c +316 -0
- package/numkong/c/dispatch_f16.c +452 -0
- package/numkong/c/dispatch_f16c.c +58 -0
- package/numkong/c/dispatch_f32.c +434 -0
- package/numkong/c/dispatch_f32c.c +99 -0
- package/numkong/c/dispatch_f64.c +348 -0
- package/numkong/c/dispatch_f64c.c +98 -0
- package/numkong/c/dispatch_i16.c +96 -0
- package/numkong/c/dispatch_i32.c +89 -0
- package/numkong/c/dispatch_i4.c +153 -0
- package/numkong/c/dispatch_i64.c +86 -0
- package/numkong/c/dispatch_i8.c +348 -0
- package/numkong/c/dispatch_other.c +348 -0
- package/numkong/c/dispatch_u1.c +193 -0
- package/numkong/c/dispatch_u16.c +124 -0
- package/numkong/c/dispatch_u32.c +118 -0
- package/numkong/c/dispatch_u4.c +153 -0
- package/numkong/c/dispatch_u64.c +102 -0
- package/numkong/c/dispatch_u8.c +364 -0
- package/numkong/c/numkong.c +940 -0
- package/numkong/cmake/nk_arm_isa_probes.cmake +61 -0
- package/numkong/cmake/nk_isa_probe.cmake +90 -0
- package/numkong/cmake/nk_loongarch_isa_probes.cmake +15 -0
- package/numkong/cmake/nk_math_lib.cmake +46 -0
- package/numkong/cmake/nk_power_isa_probes.cmake +15 -0
- package/numkong/cmake/nk_riscv_isa_probes.cmake +19 -0
- package/numkong/cmake/nk_x86_isa_probes.cmake +35 -0
- package/numkong/cmake/toolchain-aarch64-gnu.cmake +86 -0
- package/numkong/cmake/toolchain-android-arm64.cmake +25 -0
- package/numkong/cmake/toolchain-android-armv7.cmake +25 -0
- package/numkong/cmake/toolchain-loongarch64-gnu.cmake +68 -0
- package/numkong/cmake/toolchain-ppc64le-gnu.cmake +74 -0
- package/numkong/cmake/toolchain-riscv64-gnu.cmake +107 -0
- package/numkong/cmake/toolchain-riscv64-llvm.cmake +114 -0
- package/numkong/cmake/toolchain-wasi.cmake +77 -0
- package/numkong/cmake/toolchain-wasm.cmake +70 -0
- package/numkong/cmake/toolchain-wasm64.cmake +62 -0
- package/numkong/cmake/toolchain-x86_64-llvm.cmake +47 -0
- package/numkong/include/README.md +643 -0
- package/numkong/include/module.modulemap +129 -0
- package/numkong/include/numkong/attention/sapphireamx.h +1363 -0
- package/numkong/include/numkong/attention/sme.h +2075 -0
- package/numkong/include/numkong/attention.h +49 -0
- package/numkong/include/numkong/capabilities.h +930 -0
- package/numkong/include/numkong/cast/README.md +262 -0
- package/numkong/include/numkong/cast/diamond.h +64 -0
- package/numkong/include/numkong/cast/haswell.h +823 -0
- package/numkong/include/numkong/cast/icelake.h +475 -0
- package/numkong/include/numkong/cast/loongsonasx.h +252 -0
- package/numkong/include/numkong/cast/neon.h +1174 -0
- package/numkong/include/numkong/cast/powervsx.h +449 -0
- package/numkong/include/numkong/cast/rvv.h +970 -0
- package/numkong/include/numkong/cast/sapphire.h +262 -0
- package/numkong/include/numkong/cast/serial.h +2336 -0
- package/numkong/include/numkong/cast/skylake.h +915 -0
- package/numkong/include/numkong/cast/v128relaxed.h +537 -0
- package/numkong/include/numkong/cast.h +254 -0
- package/numkong/include/numkong/cast.hpp +44 -0
- package/numkong/include/numkong/curved/README.md +223 -0
- package/numkong/include/numkong/curved/genoa.h +182 -0
- package/numkong/include/numkong/curved/haswell.h +276 -0
- package/numkong/include/numkong/curved/neon.h +329 -0
- package/numkong/include/numkong/curved/neonbfdot.h +211 -0
- package/numkong/include/numkong/curved/rvv.h +305 -0
- package/numkong/include/numkong/curved/serial.h +207 -0
- package/numkong/include/numkong/curved/skylake.h +457 -0
- package/numkong/include/numkong/curved/smef64.h +530 -0
- package/numkong/include/numkong/curved.h +513 -0
- package/numkong/include/numkong/curved.hpp +144 -0
- package/numkong/include/numkong/dot/README.md +442 -0
- package/numkong/include/numkong/dot/alder.h +563 -0
- package/numkong/include/numkong/dot/diamond.h +158 -0
- package/numkong/include/numkong/dot/genoa.h +289 -0
- package/numkong/include/numkong/dot/haswell.h +1723 -0
- package/numkong/include/numkong/dot/icelake.h +997 -0
- package/numkong/include/numkong/dot/loongsonasx.h +671 -0
- package/numkong/include/numkong/dot/neon.h +869 -0
- package/numkong/include/numkong/dot/neonbfdot.h +243 -0
- package/numkong/include/numkong/dot/neonfhm.h +358 -0
- package/numkong/include/numkong/dot/neonfp8.h +323 -0
- package/numkong/include/numkong/dot/neonsdot.h +622 -0
- package/numkong/include/numkong/dot/powervsx.h +752 -0
- package/numkong/include/numkong/dot/rvv.h +722 -0
- package/numkong/include/numkong/dot/rvvbb.h +72 -0
- package/numkong/include/numkong/dot/rvvbf16.h +123 -0
- package/numkong/include/numkong/dot/rvvhalf.h +129 -0
- package/numkong/include/numkong/dot/sapphire.h +141 -0
- package/numkong/include/numkong/dot/serial.h +874 -0
- package/numkong/include/numkong/dot/sierra.h +405 -0
- package/numkong/include/numkong/dot/skylake.h +1116 -0
- package/numkong/include/numkong/dot/sve.h +420 -0
- package/numkong/include/numkong/dot/svebfdot.h +75 -0
- package/numkong/include/numkong/dot/svehalf.h +168 -0
- package/numkong/include/numkong/dot/svesdot.h +90 -0
- package/numkong/include/numkong/dot/v128relaxed.h +1293 -0
- package/numkong/include/numkong/dot.h +1136 -0
- package/numkong/include/numkong/dot.hpp +94 -0
- package/numkong/include/numkong/dots/README.md +496 -0
- package/numkong/include/numkong/dots/alder.h +117 -0
- package/numkong/include/numkong/dots/diamond.h +86 -0
- package/numkong/include/numkong/dots/genoa.h +100 -0
- package/numkong/include/numkong/dots/graniteamx.h +733 -0
- package/numkong/include/numkong/dots/haswell.h +310 -0
- package/numkong/include/numkong/dots/icelake.h +180 -0
- package/numkong/include/numkong/dots/loongsonasx.h +176 -0
- package/numkong/include/numkong/dots/neon.h +123 -0
- package/numkong/include/numkong/dots/neonbfdot.h +59 -0
- package/numkong/include/numkong/dots/neonfhm.h +96 -0
- package/numkong/include/numkong/dots/neonfp8.h +99 -0
- package/numkong/include/numkong/dots/neonsdot.h +144 -0
- package/numkong/include/numkong/dots/powervsx.h +194 -0
- package/numkong/include/numkong/dots/rvv.h +2593 -0
- package/numkong/include/numkong/dots/sapphireamx.h +4017 -0
- package/numkong/include/numkong/dots/serial.h +2867 -0
- package/numkong/include/numkong/dots/sierra.h +100 -0
- package/numkong/include/numkong/dots/skylake.h +207 -0
- package/numkong/include/numkong/dots/sme.h +5041 -0
- package/numkong/include/numkong/dots/smebi32.h +477 -0
- package/numkong/include/numkong/dots/smef64.h +1328 -0
- package/numkong/include/numkong/dots/smehalf.h +47 -0
- package/numkong/include/numkong/dots/v128relaxed.h +317 -0
- package/numkong/include/numkong/dots.h +3110 -0
- package/numkong/include/numkong/dots.hpp +641 -0
- package/numkong/include/numkong/each/README.md +465 -0
- package/numkong/include/numkong/each/haswell.h +1658 -0
- package/numkong/include/numkong/each/icelake.h +272 -0
- package/numkong/include/numkong/each/neon.h +1138 -0
- package/numkong/include/numkong/each/neonbfdot.h +211 -0
- package/numkong/include/numkong/each/neonhalf.h +318 -0
- package/numkong/include/numkong/each/rvv.h +1121 -0
- package/numkong/include/numkong/each/sapphire.h +343 -0
- package/numkong/include/numkong/each/serial.h +282 -0
- package/numkong/include/numkong/each/skylake.h +1562 -0
- package/numkong/include/numkong/each/v128relaxed.h +562 -0
- package/numkong/include/numkong/each.h +2232 -0
- package/numkong/include/numkong/each.hpp +434 -0
- package/numkong/include/numkong/geospatial/README.md +147 -0
- package/numkong/include/numkong/geospatial/haswell.h +633 -0
- package/numkong/include/numkong/geospatial/neon.h +615 -0
- package/numkong/include/numkong/geospatial/rvv.h +701 -0
- package/numkong/include/numkong/geospatial/serial.h +309 -0
- package/numkong/include/numkong/geospatial/skylake.h +613 -0
- package/numkong/include/numkong/geospatial/v128relaxed.h +652 -0
- package/numkong/include/numkong/geospatial.h +453 -0
- package/numkong/include/numkong/geospatial.hpp +235 -0
- package/numkong/include/numkong/matrix.hpp +336 -0
- package/numkong/include/numkong/maxsim/README.md +187 -0
- package/numkong/include/numkong/maxsim/alder.h +511 -0
- package/numkong/include/numkong/maxsim/genoa.h +115 -0
- package/numkong/include/numkong/maxsim/haswell.h +553 -0
- package/numkong/include/numkong/maxsim/icelake.h +480 -0
- package/numkong/include/numkong/maxsim/neonsdot.h +394 -0
- package/numkong/include/numkong/maxsim/sapphireamx.h +877 -0
- package/numkong/include/numkong/maxsim/serial.h +490 -0
- package/numkong/include/numkong/maxsim/sme.h +993 -0
- package/numkong/include/numkong/maxsim/v128relaxed.h +280 -0
- package/numkong/include/numkong/maxsim.h +571 -0
- package/numkong/include/numkong/maxsim.hpp +133 -0
- package/numkong/include/numkong/mesh/README.md +219 -0
- package/numkong/include/numkong/mesh/haswell.h +2013 -0
- package/numkong/include/numkong/mesh/neon.h +1821 -0
- package/numkong/include/numkong/mesh/neonbfdot.h +771 -0
- package/numkong/include/numkong/mesh/rvv.h +936 -0
- package/numkong/include/numkong/mesh/serial.h +721 -0
- package/numkong/include/numkong/mesh/skylake.h +2344 -0
- package/numkong/include/numkong/mesh/v128relaxed.h +1081 -0
- package/numkong/include/numkong/mesh.h +666 -0
- package/numkong/include/numkong/mesh.hpp +701 -0
- package/numkong/include/numkong/numkong.h +78 -0
- package/numkong/include/numkong/numkong.hpp +58 -0
- package/numkong/include/numkong/probability/README.md +177 -0
- package/numkong/include/numkong/probability/haswell.h +267 -0
- package/numkong/include/numkong/probability/neon.h +255 -0
- package/numkong/include/numkong/probability/rvv.h +414 -0
- package/numkong/include/numkong/probability/serial.h +181 -0
- package/numkong/include/numkong/probability/skylake.h +324 -0
- package/numkong/include/numkong/probability.h +380 -0
- package/numkong/include/numkong/probability.hpp +120 -0
- package/numkong/include/numkong/random.h +50 -0
- package/numkong/include/numkong/random.hpp +285 -0
- package/numkong/include/numkong/reduce/README.md +552 -0
- package/numkong/include/numkong/reduce/alder.h +636 -0
- package/numkong/include/numkong/reduce/genoa.h +201 -0
- package/numkong/include/numkong/reduce/haswell.h +3785 -0
- package/numkong/include/numkong/reduce/icelake.h +549 -0
- package/numkong/include/numkong/reduce/neon.h +3980 -0
- package/numkong/include/numkong/reduce/neonbfdot.h +142 -0
- package/numkong/include/numkong/reduce/neonfhm.h +293 -0
- package/numkong/include/numkong/reduce/neonsdot.h +357 -0
- package/numkong/include/numkong/reduce/rvv.h +3533 -0
- package/numkong/include/numkong/reduce/serial.h +753 -0
- package/numkong/include/numkong/reduce/sierra.h +338 -0
- package/numkong/include/numkong/reduce/skylake.h +3794 -0
- package/numkong/include/numkong/reduce/sve.h +52 -0
- package/numkong/include/numkong/reduce/v128relaxed.h +2302 -0
- package/numkong/include/numkong/reduce.h +1582 -0
- package/numkong/include/numkong/reduce.hpp +778 -0
- package/numkong/include/numkong/scalar/README.md +89 -0
- package/numkong/include/numkong/scalar/haswell.h +122 -0
- package/numkong/include/numkong/scalar/loongsonasx.h +74 -0
- package/numkong/include/numkong/scalar/neon.h +122 -0
- package/numkong/include/numkong/scalar/neonhalf.h +70 -0
- package/numkong/include/numkong/scalar/powervsx.h +96 -0
- package/numkong/include/numkong/scalar/rvv.h +211 -0
- package/numkong/include/numkong/scalar/sapphire.h +74 -0
- package/numkong/include/numkong/scalar/serial.h +332 -0
- package/numkong/include/numkong/scalar/v128relaxed.h +56 -0
- package/numkong/include/numkong/scalar.h +696 -0
- package/numkong/include/numkong/set/README.md +179 -0
- package/numkong/include/numkong/set/haswell.h +334 -0
- package/numkong/include/numkong/set/icelake.h +485 -0
- package/numkong/include/numkong/set/loongsonasx.h +181 -0
- package/numkong/include/numkong/set/neon.h +363 -0
- package/numkong/include/numkong/set/powervsx.h +326 -0
- package/numkong/include/numkong/set/rvv.h +226 -0
- package/numkong/include/numkong/set/rvvbb.h +117 -0
- package/numkong/include/numkong/set/serial.h +174 -0
- package/numkong/include/numkong/set/sve.h +187 -0
- package/numkong/include/numkong/set/v128relaxed.h +240 -0
- package/numkong/include/numkong/set.h +471 -0
- package/numkong/include/numkong/set.hpp +114 -0
- package/numkong/include/numkong/sets/README.md +149 -0
- package/numkong/include/numkong/sets/haswell.h +63 -0
- package/numkong/include/numkong/sets/icelake.h +66 -0
- package/numkong/include/numkong/sets/loongsonasx.h +52 -0
- package/numkong/include/numkong/sets/neon.h +61 -0
- package/numkong/include/numkong/sets/powervsx.h +65 -0
- package/numkong/include/numkong/sets/serial.h +43 -0
- package/numkong/include/numkong/sets/smebi32.h +1135 -0
- package/numkong/include/numkong/sets/v128relaxed.h +58 -0
- package/numkong/include/numkong/sets.h +382 -0
- package/numkong/include/numkong/sparse/README.md +156 -0
- package/numkong/include/numkong/sparse/icelake.h +463 -0
- package/numkong/include/numkong/sparse/neon.h +288 -0
- package/numkong/include/numkong/sparse/serial.h +117 -0
- package/numkong/include/numkong/sparse/sve2.h +504 -0
- package/numkong/include/numkong/sparse/turin.h +322 -0
- package/numkong/include/numkong/sparse.h +363 -0
- package/numkong/include/numkong/sparse.hpp +113 -0
- package/numkong/include/numkong/spatial/README.md +439 -0
- package/numkong/include/numkong/spatial/alder.h +604 -0
- package/numkong/include/numkong/spatial/diamond.h +240 -0
- package/numkong/include/numkong/spatial/genoa.h +222 -0
- package/numkong/include/numkong/spatial/haswell.h +979 -0
- package/numkong/include/numkong/spatial/icelake.h +1067 -0
- package/numkong/include/numkong/spatial/loongsonasx.h +483 -0
- package/numkong/include/numkong/spatial/neon.h +846 -0
- package/numkong/include/numkong/spatial/neonbfdot.h +164 -0
- package/numkong/include/numkong/spatial/neonfp8.h +258 -0
- package/numkong/include/numkong/spatial/neonsdot.h +429 -0
- package/numkong/include/numkong/spatial/powervsx.h +738 -0
- package/numkong/include/numkong/spatial/rvv.h +991 -0
- package/numkong/include/numkong/spatial/rvvbf16.h +128 -0
- package/numkong/include/numkong/spatial/rvvhalf.h +120 -0
- package/numkong/include/numkong/spatial/serial.h +347 -0
- package/numkong/include/numkong/spatial/sierra.h +516 -0
- package/numkong/include/numkong/spatial/skylake.h +605 -0
- package/numkong/include/numkong/spatial/sve.h +246 -0
- package/numkong/include/numkong/spatial/svebfdot.h +125 -0
- package/numkong/include/numkong/spatial/svehalf.h +136 -0
- package/numkong/include/numkong/spatial/svesdot.h +143 -0
- package/numkong/include/numkong/spatial/v128relaxed.h +969 -0
- package/numkong/include/numkong/spatial.h +1681 -0
- package/numkong/include/numkong/spatial.hpp +183 -0
- package/numkong/include/numkong/spatials/README.md +580 -0
- package/numkong/include/numkong/spatials/alder.h +94 -0
- package/numkong/include/numkong/spatials/diamond.h +82 -0
- package/numkong/include/numkong/spatials/genoa.h +94 -0
- package/numkong/include/numkong/spatials/graniteamx.h +173 -0
- package/numkong/include/numkong/spatials/haswell.h +219 -0
- package/numkong/include/numkong/spatials/icelake.h +113 -0
- package/numkong/include/numkong/spatials/loongsonasx.h +153 -0
- package/numkong/include/numkong/spatials/neon.h +109 -0
- package/numkong/include/numkong/spatials/neonbfdot.h +60 -0
- package/numkong/include/numkong/spatials/neonfhm.h +92 -0
- package/numkong/include/numkong/spatials/neonfp8.h +111 -0
- package/numkong/include/numkong/spatials/neonsdot.h +143 -0
- package/numkong/include/numkong/spatials/powervsx.h +153 -0
- package/numkong/include/numkong/spatials/rvv.h +1976 -0
- package/numkong/include/numkong/spatials/sapphireamx.h +1149 -0
- package/numkong/include/numkong/spatials/serial.h +248 -0
- package/numkong/include/numkong/spatials/sierra.h +96 -0
- package/numkong/include/numkong/spatials/skylake.h +184 -0
- package/numkong/include/numkong/spatials/sme.h +1927 -0
- package/numkong/include/numkong/spatials/smef64.h +483 -0
- package/numkong/include/numkong/spatials/v128relaxed.h +240 -0
- package/numkong/include/numkong/spatials.h +3374 -0
- package/numkong/include/numkong/spatials.hpp +509 -0
- package/numkong/include/numkong/tensor.hpp +1981 -0
- package/numkong/include/numkong/trigonometry/README.md +184 -0
- package/numkong/include/numkong/trigonometry/haswell.h +653 -0
- package/numkong/include/numkong/trigonometry/neon.h +638 -0
- package/numkong/include/numkong/trigonometry/rvv.h +700 -0
- package/numkong/include/numkong/trigonometry/serial.h +703 -0
- package/numkong/include/numkong/trigonometry/skylake.h +725 -0
- package/numkong/include/numkong/trigonometry/v128relaxed.h +667 -0
- package/numkong/include/numkong/trigonometry.h +467 -0
- package/numkong/include/numkong/trigonometry.hpp +166 -0
- package/numkong/include/numkong/types.h +1695 -0
- package/numkong/include/numkong/types.hpp +6027 -0
- package/numkong/include/numkong/vector.hpp +766 -0
- package/numkong/javascript/@numkong-darwin-arm64/package.json +26 -0
- package/numkong/javascript/@numkong-darwin-x64/package.json +26 -0
- package/numkong/javascript/@numkong-linux-arm64/package.json +25 -0
- package/numkong/javascript/@numkong-linux-x64/package.json +25 -0
- package/numkong/javascript/@numkong-win32-arm64/package.json +25 -0
- package/numkong/javascript/@numkong-win32-x64/package.json +25 -0
- package/numkong/javascript/README.md +254 -0
- package/numkong/javascript/node-gyp-build.d.ts +4 -0
- package/numkong/javascript/numkong-browser.ts +40 -0
- package/numkong/javascript/numkong-wasm.ts +750 -0
- package/numkong/javascript/numkong.c +712 -0
- package/numkong/javascript/numkong.ts +613 -0
- package/{simsimd → numkong}/javascript/tsconfig-base.json +6 -3
- package/numkong/javascript/tsconfig-cjs.json +13 -0
- package/{simsimd → numkong}/javascript/tsconfig-esm.json +1 -1
- package/numkong/javascript/types.ts +784 -0
- package/numkong/numkong.gypi +104 -0
- package/numkong/package.json +108 -0
- package/numkong/probes/arm_neon.c +8 -0
- package/numkong/probes/arm_neon_bfdot.c +9 -0
- package/numkong/probes/arm_neon_fhm.c +9 -0
- package/numkong/probes/arm_neon_half.c +8 -0
- package/numkong/probes/arm_neon_sdot.c +9 -0
- package/numkong/probes/arm_neonfp8.c +9 -0
- package/numkong/probes/arm_sme.c +16 -0
- package/numkong/probes/arm_sme2.c +16 -0
- package/numkong/probes/arm_sme2p1.c +16 -0
- package/numkong/probes/arm_sme_bf16.c +16 -0
- package/numkong/probes/arm_sme_bi32.c +16 -0
- package/numkong/probes/arm_sme_f64.c +16 -0
- package/numkong/probes/arm_sme_fa64.c +14 -0
- package/numkong/probes/arm_sme_half.c +16 -0
- package/numkong/probes/arm_sme_lut2.c +15 -0
- package/numkong/probes/arm_sve.c +18 -0
- package/numkong/probes/arm_sve2.c +20 -0
- package/numkong/probes/arm_sve2p1.c +18 -0
- package/numkong/probes/arm_sve_bfdot.c +20 -0
- package/numkong/probes/arm_sve_half.c +18 -0
- package/numkong/probes/arm_sve_sdot.c +21 -0
- package/numkong/probes/loongarch_lasx.c +12 -0
- package/numkong/probes/power_vsx.c +12 -0
- package/numkong/probes/probe.js +127 -0
- package/numkong/probes/riscv_rvv.c +14 -0
- package/numkong/probes/riscv_rvv_bb.c +15 -0
- package/numkong/probes/riscv_rvv_bf16.c +17 -0
- package/numkong/probes/riscv_rvv_half.c +14 -0
- package/numkong/probes/wasm_v128relaxed.c +11 -0
- package/numkong/probes/x86_alder.c +17 -0
- package/numkong/probes/x86_diamond.c +17 -0
- package/numkong/probes/x86_genoa.c +17 -0
- package/numkong/probes/x86_graniteamx.c +19 -0
- package/numkong/probes/x86_haswell.c +11 -0
- package/numkong/probes/x86_icelake.c +17 -0
- package/numkong/probes/x86_sapphire.c +16 -0
- package/numkong/probes/x86_sapphireamx.c +18 -0
- package/numkong/probes/x86_sierra.c +17 -0
- package/numkong/probes/x86_skylake.c +15 -0
- package/numkong/probes/x86_turin.c +17 -0
- package/numkong/rust/README.md +698 -0
- package/numkong/rust/capabilities.rs +96 -0
- package/numkong/rust/cast.rs +314 -0
- package/numkong/rust/curved.rs +391 -0
- package/numkong/rust/each.rs +2721 -0
- package/numkong/rust/geospatial.rs +343 -0
- package/numkong/rust/matrix.rs +5424 -0
- package/numkong/rust/maxsim.rs +540 -0
- package/numkong/rust/mesh.rs +837 -0
- package/numkong/rust/numkong.rs +409 -0
- package/numkong/rust/probability.rs +272 -0
- package/numkong/rust/reduce.rs +1249 -0
- package/numkong/rust/set.rs +194 -0
- package/numkong/rust/sparse.rs +513 -0
- package/numkong/rust/spatial.rs +1815 -0
- package/numkong/rust/tensor.rs +7565 -0
- package/numkong/rust/types.rs +3666 -0
- package/numkong/rust/vector.rs +1899 -0
- package/numkong/rustfmt.toml +2 -0
- package/numkong/swift/Matrix.swift +1388 -0
- package/numkong/swift/MaxSim.swift +151 -0
- package/numkong/swift/NumKong.swift +283 -0
- package/numkong/swift/Numerics.swift +329 -0
- package/numkong/swift/README.md +433 -0
- package/numkong/swift/Scalar.swift +741 -0
- package/numkong/swift/Tensor.swift +209 -0
- package/numkong/test/README.md +315 -0
- package/numkong/test/playwright.config.ts +16 -0
- package/numkong/test/swift/Test.swift +534 -0
- package/numkong/test/test-browser-bundle-runner.mjs +36 -0
- package/numkong/test/test-browser-bundle.html +188 -0
- package/numkong/test/test-browser-runner.mjs +43 -0
- package/numkong/test/test-browser.html +305 -0
- package/numkong/test/test-dtypes.mjs +453 -0
- package/numkong/test/test-wasm.mjs +267 -0
- package/numkong/test/test.cpp +442 -0
- package/numkong/test/test.hpp +580 -0
- package/numkong/test/test.mjs +363 -0
- package/numkong/test/test_base.py +1041 -0
- package/numkong/test/test_cast.cpp +242 -0
- package/numkong/test/test_cross.hpp +551 -0
- package/numkong/test/test_cross.py +467 -0
- package/numkong/test/test_cross_amx.cpp +50 -0
- package/numkong/test/test_cross_arm.cpp +225 -0
- package/numkong/test/test_cross_blas.cpp +354 -0
- package/numkong/test/test_cross_loongarch.cpp +90 -0
- package/numkong/test/test_cross_power.cpp +73 -0
- package/numkong/test/test_cross_rvv.cpp +109 -0
- package/numkong/test/test_cross_serial.cpp +181 -0
- package/numkong/test/test_cross_sme.cpp +148 -0
- package/numkong/test/test_cross_wasm.cpp +141 -0
- package/numkong/test/test_cross_x86.cpp +442 -0
- package/numkong/test/test_curved.cpp +182 -0
- package/numkong/test/test_curved.py +207 -0
- package/numkong/test/test_dot.cpp +353 -0
- package/numkong/test/test_dot.py +296 -0
- package/numkong/test/test_each.cpp +437 -0
- package/numkong/test/test_each.py +756 -0
- package/numkong/test/test_geospatial.cpp +155 -0
- package/numkong/test/test_geospatial.py +241 -0
- package/numkong/test/test_maxsim.cpp +138 -0
- package/numkong/test/test_maxsim.py +209 -0
- package/numkong/test/test_mesh.cpp +203 -0
- package/numkong/test/test_mesh.py +394 -0
- package/numkong/test/test_probability.cpp +136 -0
- package/numkong/test/test_probability.py +166 -0
- package/numkong/test/test_reduce.cpp +563 -0
- package/numkong/test/test_reduce.py +781 -0
- package/numkong/test/test_set.cpp +151 -0
- package/numkong/test/test_set.py +120 -0
- package/numkong/test/test_similarities.py +579 -0
- package/numkong/test/test_sparse.cpp +121 -0
- package/numkong/test/test_sparse.py +118 -0
- package/numkong/test/test_spatial.cpp +518 -0
- package/numkong/test/test_spatial.py +269 -0
- package/numkong/test/test_tensor.cpp +884 -0
- package/numkong/test/test_tensor.py +1417 -0
- package/numkong/test/test_trigonometry.cpp +156 -0
- package/numkong/test/test_trigonometry.py +180 -0
- package/package.json +7 -8
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/fp16/CMakeLists.txt +0 -176
- package/fp16/LICENSE +0 -11
- package/fp16/README.md +0 -20
- package/fp16/bench/alt-element.cc +0 -89
- package/fp16/bench/from-alt-array.cc +0 -175
- package/fp16/bench/from-ieee-array.cc +0 -399
- package/fp16/bench/ieee-element.cc +0 -272
- package/fp16/bench/to-alt-array.cc +0 -91
- package/fp16/bench/to-ieee-array.cc +0 -303
- package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
- package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
- package/fp16/cmake/DownloadPSimd.cmake +0 -15
- package/fp16/configure.py +0 -65
- package/fp16/confu.yaml +0 -8
- package/fp16/include/fp16/__init__.py +0 -0
- package/fp16/include/fp16/avx.py +0 -50
- package/fp16/include/fp16/avx2.py +0 -53
- package/fp16/include/fp16/bitcasts.h +0 -92
- package/fp16/include/fp16/fp16.h +0 -451
- package/fp16/include/fp16/psimd.h +0 -131
- package/fp16/include/fp16.h +0 -11
- package/fp16/jni/Android.mk +0 -6
- package/fp16/jni/Application.mk +0 -4
- package/fp16/test/alt-from-fp32-value.cc +0 -498
- package/fp16/test/alt-to-fp32-bits.cc +0 -263
- package/fp16/test/alt-to-fp32-psimd.cc +0 -145
- package/fp16/test/alt-to-fp32-value.cc +0 -348
- package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
- package/fp16/test/bitcasts.cc +0 -56
- package/fp16/test/ieee-from-fp32-value.cc +0 -523
- package/fp16/test/ieee-to-fp32-bits.cc +0 -333
- package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
- package/fp16/test/ieee-to-fp32-value.cc +0 -420
- package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
- package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
- package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
- package/fp16/test/peachpy/stubs.py +0 -38
- package/fp16/test/tables.cc +0 -530
- package/fp16/test/tables.h +0 -13
- package/fp16/third-party/THHalf.h +0 -92
- package/fp16/third-party/eigen-half.h +0 -170
- package/fp16/third-party/float16-compressor.h +0 -80
- package/fp16/third-party/half.hpp +0 -2916
- package/fp16/third-party/npy-halffloat.h +0 -328
- package/simsimd/.git-blame-ignore-revs +0 -2
- package/simsimd/CONTRIBUTING.md +0 -321
- package/simsimd/Cargo.toml +0 -49
- package/simsimd/MANIFEST.in +0 -4
- package/simsimd/Package.swift +0 -28
- package/simsimd/README.md +0 -1493
- package/simsimd/binding.gyp +0 -35
- package/simsimd/build.rs +0 -70
- package/simsimd/c/lib.c +0 -374
- package/simsimd/include/module.modulemap +0 -4
- package/simsimd/include/simsimd/binary.h +0 -486
- package/simsimd/include/simsimd/curved.h +0 -1541
- package/simsimd/include/simsimd/dot.h +0 -1853
- package/simsimd/include/simsimd/elementwise.h +0 -2465
- package/simsimd/include/simsimd/geospatial.h +0 -43
- package/simsimd/include/simsimd/mesh.h +0 -69
- package/simsimd/include/simsimd/probability.h +0 -606
- package/simsimd/include/simsimd/simsimd.h +0 -2567
- package/simsimd/include/simsimd/sparse.h +0 -1384
- package/simsimd/include/simsimd/spatial.h +0 -2335
- package/simsimd/include/simsimd/types.h +0 -666
- package/simsimd/javascript/fallback.ts +0 -233
- package/simsimd/javascript/lib.c +0 -125
- package/simsimd/javascript/node-gyp-build.d.ts +0 -1
- package/simsimd/javascript/simsimd.ts +0 -179
- package/simsimd/javascript/tsconfig-cjs.json +0 -8
- package/simsimd/package-lock.json +0 -1761
- package/simsimd/package.json +0 -61
- package/simsimd/rust/lib.rs +0 -1821
- package/simsimd/scripts/bench.cxx +0 -1170
- package/simsimd/scripts/bench.ipynb +0 -187
- package/simsimd/scripts/bench.js +0 -127
- package/simsimd/scripts/bench.rs +0 -876
- package/simsimd/scripts/bench_vectors.py +0 -734
- package/simsimd/scripts/bench_vectors_live.py +0 -213
- package/simsimd/scripts/test.c +0 -187
- package/simsimd/scripts/test.mjs +0 -220
- package/simsimd/scripts/test.py +0 -1505
- package/simsimd/swift/SimSIMD.swift +0 -149
- package/simsimd/swift/Test.swift +0 -128
- /package/{simsimd → numkong}/CODE_OF_CONDUCT.md +0 -0
- /package/{simsimd → numkong}/LICENSE +0 -0
- /package/{simsimd → numkong}/javascript/dist-package-cjs.json +0 -0
- /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)
|