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,1041 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Shared test infrastructure for the NumKong test suite.
|
|
3
|
+
|
|
4
|
+
Provides random data generators, capability detection, high-precision baselines
|
|
5
|
+
via ``precise_decimal()``, and assertion helpers. Mirrors ``test.hpp`` from the
|
|
6
|
+
C++ test suite.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import array
|
|
12
|
+
import collections
|
|
13
|
+
import contextlib
|
|
14
|
+
import decimal
|
|
15
|
+
import faulthandler
|
|
16
|
+
import math
|
|
17
|
+
import os
|
|
18
|
+
import platform
|
|
19
|
+
import random
|
|
20
|
+
import sys
|
|
21
|
+
import time
|
|
22
|
+
from collections.abc import Callable, Generator
|
|
23
|
+
from typing import TYPE_CHECKING, Any
|
|
24
|
+
|
|
25
|
+
import numkong as nk
|
|
26
|
+
import pytest
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
import numpy as np # static-analysis-only; the runtime try/except below is authoritative
|
|
30
|
+
|
|
31
|
+
faulthandler.enable()
|
|
32
|
+
|
|
33
|
+
_nk_seed_base: int | None = int(s) if (s := os.environ.get("NK_SEED")) is not None else None
|
|
34
|
+
_nk_in_qemu: bool = "NK_IN_QEMU" in os.environ
|
|
35
|
+
|
|
36
|
+
_nk_possible_dimensions = (
|
|
37
|
+
[1, 4, 16, 33, 64]
|
|
38
|
+
if _nk_in_qemu
|
|
39
|
+
else [
|
|
40
|
+
# start with simplest cases
|
|
41
|
+
4,
|
|
42
|
+
8,
|
|
43
|
+
16,
|
|
44
|
+
64,
|
|
45
|
+
128,
|
|
46
|
+
# cover tiny cases
|
|
47
|
+
1,
|
|
48
|
+
2,
|
|
49
|
+
3,
|
|
50
|
+
5,
|
|
51
|
+
7,
|
|
52
|
+
9,
|
|
53
|
+
# corner cases around common sizes
|
|
54
|
+
15,
|
|
55
|
+
16,
|
|
56
|
+
17,
|
|
57
|
+
31,
|
|
58
|
+
32,
|
|
59
|
+
33,
|
|
60
|
+
63,
|
|
61
|
+
64,
|
|
62
|
+
65,
|
|
63
|
+
97,
|
|
64
|
+
]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
randomized_repetitions_count: int = (
|
|
69
|
+
int(s) if (s := os.environ.get("NK_REPETITIONS")) is not None else (3 if _nk_in_qemu else 10)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
dense_dimensions: list[int] = (
|
|
73
|
+
[int(d) for d in s.split(",")]
|
|
74
|
+
if (s := os.environ.get("NK_DENSE_DIMENSIONS")) is not None
|
|
75
|
+
else _nk_possible_dimensions
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Deterministic random subsamples for multi-dimensional parametrization (height × width × depth),
|
|
79
|
+
# matching the C API naming in dots.h. Override via NK_MATRIX_HEIGHT/WIDTH/DEPTH env vars.
|
|
80
|
+
_dim_sample_k = min(6, len(dense_dimensions))
|
|
81
|
+
test_height_dimensions: list[int] = (
|
|
82
|
+
[int(d) for d in s.split(",")]
|
|
83
|
+
if (s := os.environ.get("NK_MATRIX_HEIGHT")) is not None
|
|
84
|
+
else sorted(random.Random(42).sample(dense_dimensions, _dim_sample_k))
|
|
85
|
+
)
|
|
86
|
+
test_width_dimensions: list[int] = (
|
|
87
|
+
[int(d) for d in s.split(",")]
|
|
88
|
+
if (s := os.environ.get("NK_MATRIX_WIDTH")) is not None
|
|
89
|
+
else sorted(random.Random(43).sample(dense_dimensions, _dim_sample_k))
|
|
90
|
+
)
|
|
91
|
+
test_depth_dimensions: list[int] = (
|
|
92
|
+
[int(d) for d in s.split(",")]
|
|
93
|
+
if (s := os.environ.get("NK_MATRIX_DEPTH")) is not None
|
|
94
|
+
else sorted(random.Random(44).sample(dense_dimensions, _dim_sample_k))
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
reduced_repetitions_count: int = max(1, randomized_repetitions_count // 5)
|
|
98
|
+
|
|
99
|
+
test_curved_dimensions: list[int] = (
|
|
100
|
+
[int(d) for d in s.split(",")]
|
|
101
|
+
if (s := os.environ.get("NK_CURVED_DIMENSIONS")) is not None
|
|
102
|
+
else sorted(random.Random(45).sample(dense_dimensions, min(5, len(dense_dimensions))))
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
sparse_dimensions: list[int] = (
|
|
106
|
+
[int(d) for d in s.split(",")] if (s := os.environ.get("NK_SPARSE_DIMENSIONS")) is not None else [256]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
mesh_points: int = int(s) if (s := os.environ.get("NK_MESH_POINTS")) is not None else 100
|
|
110
|
+
|
|
111
|
+
max_coord_angle: float = float(s) if (s := os.environ.get("NK_MAX_COORD_ANGLE")) is not None else 180.0
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
import numpy as np
|
|
115
|
+
|
|
116
|
+
numpy_available = True
|
|
117
|
+
except Exception:
|
|
118
|
+
numpy_available = False
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
import scipy.spatial.distance # noqa: F401
|
|
122
|
+
|
|
123
|
+
scipy_available = True
|
|
124
|
+
except ImportError:
|
|
125
|
+
scipy_available = False
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
import ml_dtypes # noqa: F401
|
|
129
|
+
|
|
130
|
+
ml_dtypes_available = True
|
|
131
|
+
except ImportError:
|
|
132
|
+
ml_dtypes_available = False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
NK_RTOL = 0.1
|
|
136
|
+
NK_ATOL = 0.1
|
|
137
|
+
|
|
138
|
+
# Map dtype → the NumPy dtype used for "native precision" baseline computation.
|
|
139
|
+
# f64 types compute at f64; everything else at f32 (for floats) or i64 (for ints).
|
|
140
|
+
NATIVE_COMPUTE_DTYPE: dict[str, type] = (
|
|
141
|
+
{
|
|
142
|
+
"float64": np.float64,
|
|
143
|
+
"float32": np.float32,
|
|
144
|
+
"float16": np.float32,
|
|
145
|
+
"bfloat16": np.float32,
|
|
146
|
+
"bf16": np.float32,
|
|
147
|
+
"e4m3": np.float32,
|
|
148
|
+
"e5m2": np.float32,
|
|
149
|
+
"e2m3": np.float32,
|
|
150
|
+
"e3m2": np.float32,
|
|
151
|
+
"int8": np.int64,
|
|
152
|
+
"uint8": np.int64,
|
|
153
|
+
"int16": np.int64,
|
|
154
|
+
"uint16": np.int64,
|
|
155
|
+
"int32": np.int64,
|
|
156
|
+
"uint32": np.int64,
|
|
157
|
+
"int64": np.int64,
|
|
158
|
+
"uint64": np.int64,
|
|
159
|
+
"int4": np.int64,
|
|
160
|
+
"uint4": np.int64,
|
|
161
|
+
"complex64": np.complex128,
|
|
162
|
+
"complex128": np.complex128,
|
|
163
|
+
}
|
|
164
|
+
if numpy_available
|
|
165
|
+
else {}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
PACKING_GRANULARITY: dict[str, int] = {
|
|
170
|
+
"int4": 2,
|
|
171
|
+
"uint4": 2,
|
|
172
|
+
"uint1": 8,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def round_up_to(value: int, multiple: int) -> int:
|
|
177
|
+
"""Round *value* up to the nearest multiple of *multiple*."""
|
|
178
|
+
return (value + multiple - 1) // multiple * multiple
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
_DTYPES_NEEDING_DECIMAL = {"float32", "float64", "complex64", "complex128"}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@contextlib.contextmanager
|
|
185
|
+
def precise_decimal(dtype: str | None = None) -> Generator[tuple[Callable, Callable, Callable], None, None]:
|
|
186
|
+
"""Yield ``(upcast, sqrt, ln)`` helpers for high-precision baselines.
|
|
187
|
+
|
|
188
|
+
When *dtype* is a small type (float32, float16, int8, …) native ``float``
|
|
189
|
+
already exceeds its precision, so we skip the Decimal overhead. For
|
|
190
|
+
float64/complex128 we use 120-digit Decimal arithmetic.
|
|
191
|
+
|
|
192
|
+
Usage::
|
|
193
|
+
|
|
194
|
+
with precise_decimal("float32") as (upcast, sqrt, ln):
|
|
195
|
+
total = upcast(0)
|
|
196
|
+
for x, y in zip(a, b):
|
|
197
|
+
total += upcast(x) * upcast(y)
|
|
198
|
+
return float(sqrt(total))
|
|
199
|
+
"""
|
|
200
|
+
if dtype is not None and dtype not in _DTYPES_NEEDING_DECIMAL:
|
|
201
|
+
yield float, math.sqrt, math.log
|
|
202
|
+
else:
|
|
203
|
+
ctx = decimal.Context(prec=120)
|
|
204
|
+
ctx.traps[decimal.InvalidOperation] = False
|
|
205
|
+
with decimal.localcontext(ctx):
|
|
206
|
+
yield decimal.Decimal.from_float, decimal.Decimal.sqrt, decimal.Decimal.ln
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def is_running_under_qemu() -> bool:
|
|
210
|
+
return "NK_IN_QEMU" in os.environ
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def profile(callable: Callable | None, *args: Any, **kwargs: Any) -> tuple[int, Any]:
|
|
214
|
+
if callable is None:
|
|
215
|
+
return 0, None
|
|
216
|
+
before = time.perf_counter_ns()
|
|
217
|
+
result = callable(*args, **kwargs)
|
|
218
|
+
after = time.perf_counter_ns()
|
|
219
|
+
return after - before, result
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def scipy_metric_name(metric: str) -> str:
|
|
223
|
+
"""Convert NumKong metric names to SciPy equivalents."""
|
|
224
|
+
if metric == "angular":
|
|
225
|
+
return "cosine"
|
|
226
|
+
return metric
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def to_array(x: Any, dtype: str | None = None) -> np.ndarray:
|
|
230
|
+
if numpy_available:
|
|
231
|
+
y = np.array(x)
|
|
232
|
+
if dtype is not None:
|
|
233
|
+
y = y.astype(dtype)
|
|
234
|
+
return y
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
_DTYPE_TOLERANCES: dict[str, tuple[float, float]] = {
|
|
238
|
+
"float64": (1e-6, 1e-6),
|
|
239
|
+
"float32": (1e-4, 1e-4),
|
|
240
|
+
"float16": (NK_ATOL, NK_RTOL),
|
|
241
|
+
"bfloat16": (NK_ATOL, NK_RTOL),
|
|
242
|
+
"bf16": (NK_ATOL, NK_RTOL),
|
|
243
|
+
"e4m3": (NK_ATOL, NK_RTOL),
|
|
244
|
+
"e5m2": (NK_ATOL, NK_RTOL),
|
|
245
|
+
"e2m3": (NK_ATOL, NK_RTOL),
|
|
246
|
+
"e3m2": (NK_ATOL, NK_RTOL),
|
|
247
|
+
"complex128": (1e-6, 1e-6),
|
|
248
|
+
"complex64": (1e-4, 1e-4),
|
|
249
|
+
"int8": (1, 0),
|
|
250
|
+
"uint8": (1, 0),
|
|
251
|
+
"int16": (1, 0),
|
|
252
|
+
"uint16": (1, 0),
|
|
253
|
+
"int32": (1, 0),
|
|
254
|
+
"uint32": (1, 0),
|
|
255
|
+
"int64": (1, 0),
|
|
256
|
+
"uint64": (1, 0),
|
|
257
|
+
"int4": (1, 0),
|
|
258
|
+
"uint4": (1, 0),
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def tolerances_for_dtype(dtype: str) -> tuple[float, float]:
|
|
263
|
+
"""Returns ``(atol, rtol)`` appropriate for assertions on the given dtype."""
|
|
264
|
+
return _DTYPE_TOLERANCES.get(dtype, (NK_ATOL, NK_RTOL))
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def random_of_dtype(dtype: str, shape: tuple[int, ...]) -> tuple[Any, Any]:
|
|
268
|
+
"""Legacy helper — thin wrapper around :func:`make_random`."""
|
|
269
|
+
raw, _ = make_random(shape, dtype)
|
|
270
|
+
return raw
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class LazyFormat:
|
|
274
|
+
"""Deferred string formatting — only evaluated when str() is called (on assertion failure)."""
|
|
275
|
+
|
|
276
|
+
__slots__ = ("_fn",)
|
|
277
|
+
|
|
278
|
+
def __init__(self, fn):
|
|
279
|
+
self._fn = fn
|
|
280
|
+
|
|
281
|
+
def __str__(self):
|
|
282
|
+
return self._fn()
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def f32_downcast_to_bf16(array: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
286
|
+
"""Converts an array of 32-bit floats into 16-bit brain-floats.
|
|
287
|
+
|
|
288
|
+
Uses IEEE 754 round-to-nearest-even (banker's rounding) to match
|
|
289
|
+
ml_dtypes.bfloat16 behavior.
|
|
290
|
+
"""
|
|
291
|
+
array = np.asarray(array, dtype=np.float32)
|
|
292
|
+
u32 = array.view(np.uint32)
|
|
293
|
+
lower = u32 & np.uint32(0xFFFF)
|
|
294
|
+
# For exact ties (lower 16 bits == 0x8000), round to even:
|
|
295
|
+
# only round up when the bf16 mantissa LSB (bit 16) is odd.
|
|
296
|
+
is_tie = lower == np.uint32(0x8000)
|
|
297
|
+
lsb = (u32 >> np.uint32(16)) & np.uint32(1)
|
|
298
|
+
adjustment = np.where(is_tie, lsb << np.uint32(15), np.uint32(0x8000))
|
|
299
|
+
rounded_u32 = (u32 + adjustment) & np.uint32(0xFFFF0000)
|
|
300
|
+
array_f32_rounded = rounded_u32.view(np.float32)
|
|
301
|
+
array_bf16 = np.right_shift(rounded_u32, 16).astype(np.uint16)
|
|
302
|
+
return array_f32_rounded, array_bf16
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _pack_nibbles(array):
|
|
306
|
+
"""Pack pairs of nibbles along the last axis, preserving leading dimensions.
|
|
307
|
+
|
|
308
|
+
Each pair of consecutive elements is packed into one byte:
|
|
309
|
+
low nibble = even index, high nibble = odd index.
|
|
310
|
+
Odd-length rows are zero-padded. Returns a uint8 array.
|
|
311
|
+
"""
|
|
312
|
+
shape = array.shape
|
|
313
|
+
if array.ndim >= 2:
|
|
314
|
+
rows = array.reshape(-1, shape[-1])
|
|
315
|
+
cols = shape[-1]
|
|
316
|
+
packed_cols = (cols + 1) // 2
|
|
317
|
+
if cols % 2:
|
|
318
|
+
rows = np.concatenate([rows, np.zeros((rows.shape[0], 1), dtype=np.uint8)], axis=1)
|
|
319
|
+
low = rows[:, 0::2].astype(np.uint8) & 0x0F
|
|
320
|
+
high = (rows[:, 1::2].astype(np.uint8) & 0x0F) << 4
|
|
321
|
+
packed = (low | high).astype(np.uint8)
|
|
322
|
+
return packed.reshape(*shape[:-1], packed_cols)
|
|
323
|
+
else:
|
|
324
|
+
flat = array.ravel()
|
|
325
|
+
if len(flat) % 2:
|
|
326
|
+
flat = np.append(flat, np.uint8(0))
|
|
327
|
+
low = flat[0::2].astype(np.uint8) & 0x0F
|
|
328
|
+
high = (flat[1::2].astype(np.uint8) & 0x0F) << 4
|
|
329
|
+
return (low | high).astype(np.uint8)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def i8_downcast_to_i4(array):
|
|
333
|
+
"""Pack signed 8-bit integers into signed 4-bit pairs (2 per byte).
|
|
334
|
+
|
|
335
|
+
Layout matches C ``nk_i4x2_t``: low nibble = even index, high nibble = odd index.
|
|
336
|
+
Input values must be in [-8, 7]. Preserves leading dimensions for 2-D+ inputs.
|
|
337
|
+
"""
|
|
338
|
+
array = np.asarray(array, dtype=np.int8)
|
|
339
|
+
assert np.all(array >= -8) and np.all(array <= 7), "values must be in [-8, 7]"
|
|
340
|
+
return _pack_nibbles(array)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def u8_downcast_to_u4(array):
|
|
344
|
+
"""Pack unsigned 8-bit integers into unsigned 4-bit pairs (2 per byte).
|
|
345
|
+
|
|
346
|
+
Layout matches C ``nk_u4x2_t``: low nibble = even index, high nibble = odd index.
|
|
347
|
+
Input values must be in [0, 15]. Preserves leading dimensions for 2-D+ inputs.
|
|
348
|
+
"""
|
|
349
|
+
array = np.asarray(array, dtype=np.uint8)
|
|
350
|
+
assert np.all(array <= 15), "values must be in [0, 15]"
|
|
351
|
+
return _pack_nibbles(array)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def hex_array(arr: Any) -> str:
|
|
355
|
+
"""Converts numerical array into a string of comma-separated hexadecimal values for debugging."""
|
|
356
|
+
arr = np.asarray(arr)
|
|
357
|
+
if not np.issubdtype(arr.dtype, np.integer):
|
|
358
|
+
# View non-integer data as raw bytes for hex display
|
|
359
|
+
shape = arr.shape
|
|
360
|
+
arr = arr.view(np.uint8).reshape((*shape, -1))
|
|
361
|
+
printer = np.vectorize(hex)
|
|
362
|
+
strings = printer(arr)
|
|
363
|
+
if strings.ndim == 1:
|
|
364
|
+
return ", ".join(strings)
|
|
365
|
+
else:
|
|
366
|
+
return "\n".join(", ".join(row) for row in strings.reshape(-1, strings.shape[-1]))
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# Lookup tables for sub-byte float types.
|
|
370
|
+
# Built once from the encoding rules in ``include/numkong/cast/serial.h``.
|
|
371
|
+
# Each table maps a raw byte value to its float64 representation.
|
|
372
|
+
# NaN entries are stored as ``float('nan')``.
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def build_subbyte_float_lookup_table(
|
|
376
|
+
sign_bit, exp_bits, mant_bits, bias, total_bits, has_inf=False, nan_only_max_mant=False
|
|
377
|
+
):
|
|
378
|
+
"""Build a byte→float64 lookup table for a sub-byte float format.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
sign_bit: position of the sign bit (counting from bit 0)
|
|
382
|
+
exp_bits: number of exponent bits
|
|
383
|
+
mant_bits: number of mantissa bits
|
|
384
|
+
bias: exponent bias
|
|
385
|
+
total_bits: number of significant bits (6 for float6, 8 for float8)
|
|
386
|
+
has_inf: if True, max exponent with zero mantissa = ±∞
|
|
387
|
+
nan_only_max_mant: if True, only max_exp + max_mant is NaN (e4m3 rule)
|
|
388
|
+
"""
|
|
389
|
+
n = 1 << total_bits
|
|
390
|
+
exp_mask = (1 << exp_bits) - 1
|
|
391
|
+
mant_mask = (1 << mant_bits) - 1
|
|
392
|
+
max_exp = exp_mask
|
|
393
|
+
max_mant = mant_mask
|
|
394
|
+
lut = [0.0] * n
|
|
395
|
+
for i in range(n):
|
|
396
|
+
sign = (i >> sign_bit) & 1
|
|
397
|
+
exp = (i >> mant_bits) & exp_mask
|
|
398
|
+
mant = i & mant_mask
|
|
399
|
+
if exp == 0:
|
|
400
|
+
# Subnormal: value = (−1)^s × 2^(1−bias) × (mant / 2^mant_bits)
|
|
401
|
+
val = (mant / (1 << mant_bits)) * (2.0 ** (1 - bias))
|
|
402
|
+
elif exp == max_exp:
|
|
403
|
+
if has_inf and mant == 0:
|
|
404
|
+
lut[i] = -float("inf") if sign else float("inf")
|
|
405
|
+
continue
|
|
406
|
+
if has_inf and mant != 0:
|
|
407
|
+
lut[i] = float("nan")
|
|
408
|
+
continue
|
|
409
|
+
if nan_only_max_mant and mant == max_mant:
|
|
410
|
+
lut[i] = float("nan")
|
|
411
|
+
continue
|
|
412
|
+
# Finite max-exponent value
|
|
413
|
+
val = 2.0 ** (exp - bias) * (1.0 + mant / (1 << mant_bits))
|
|
414
|
+
else:
|
|
415
|
+
val = 2.0 ** (exp - bias) * (1.0 + mant / (1 << mant_bits))
|
|
416
|
+
lut[i] = -val if sign else val
|
|
417
|
+
return lut
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
LOOKUP_TABLE_E2M3 = build_subbyte_float_lookup_table(sign_bit=5, exp_bits=2, mant_bits=3, bias=1, total_bits=6)
|
|
421
|
+
LOOKUP_TABLE_E3M2 = build_subbyte_float_lookup_table(sign_bit=5, exp_bits=3, mant_bits=2, bias=3, total_bits=6)
|
|
422
|
+
LOOKUP_TABLE_E4M3 = build_subbyte_float_lookup_table(
|
|
423
|
+
sign_bit=7, exp_bits=4, mant_bits=3, bias=7, total_bits=8, nan_only_max_mant=True
|
|
424
|
+
)
|
|
425
|
+
LOOKUP_TABLE_E5M2 = build_subbyte_float_lookup_table(
|
|
426
|
+
sign_bit=7, exp_bits=5, mant_bits=2, bias=15, total_bits=8, has_inf=True
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
SUBBYTE_LOOKUP_TABLES = {
|
|
430
|
+
"e2m3": LOOKUP_TABLE_E2M3,
|
|
431
|
+
"e3m2": LOOKUP_TABLE_E3M2,
|
|
432
|
+
"e4m3": LOOKUP_TABLE_E4M3,
|
|
433
|
+
"e5m2": LOOKUP_TABLE_E5M2,
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def _make_random_numpy(shape, dtype):
|
|
438
|
+
"""NumPy-backed random-data factory (original implementation)."""
|
|
439
|
+
if dtype in ("float64", "float32", "float16"):
|
|
440
|
+
raw = np.random.randn(*shape).astype(dtype)
|
|
441
|
+
baseline = raw.astype(np.float64)
|
|
442
|
+
return raw, baseline
|
|
443
|
+
|
|
444
|
+
if dtype in ("bfloat16", "bf16"):
|
|
445
|
+
f32_arr = np.random.randn(*shape).astype(np.float32)
|
|
446
|
+
f32_rounded, bf16_raw = f32_downcast_to_bf16(f32_arr)
|
|
447
|
+
baseline = f32_rounded.astype(np.float64)
|
|
448
|
+
return bf16_raw, baseline
|
|
449
|
+
|
|
450
|
+
if dtype in ("int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64"):
|
|
451
|
+
info = np.iinfo(np.dtype(dtype))
|
|
452
|
+
raw = np.random.randint(info.min, info.max, size=shape, dtype=dtype)
|
|
453
|
+
baseline = raw.astype(np.float64)
|
|
454
|
+
return raw, baseline
|
|
455
|
+
|
|
456
|
+
if dtype in ("complex64", "complex128"):
|
|
457
|
+
raw = (np.random.randn(*shape) + 1j * np.random.randn(*shape)).astype(dtype)
|
|
458
|
+
baseline = raw.astype(np.complex128)
|
|
459
|
+
return raw, baseline
|
|
460
|
+
|
|
461
|
+
if dtype in ("e4m3", "e5m2", "e2m3", "e3m2"):
|
|
462
|
+
lut = np.array(SUBBYTE_LOOKUP_TABLES[dtype])
|
|
463
|
+
# Exclude NaN/±∞ entries from random generation
|
|
464
|
+
finite_mask = np.isfinite(lut)
|
|
465
|
+
valid_bytes = np.where(finite_mask)[0].astype(np.uint8)
|
|
466
|
+
# For types whose max representable value can cause FP32 accumulation
|
|
467
|
+
# errors exceeding NK_ATOL (catastrophic cancellation), restrict to
|
|
468
|
+
# values whose magnitude keeps products within FP32's reliable range.
|
|
469
|
+
# Threshold: T² * eps_f32 < NK_ATOL/4 => T = floor(sqrt(NK_ATOL / (4 * eps_f32))) ~ 458
|
|
470
|
+
# Only e5m2 (max 57344) is affected; e4m3/e3m2/e2m3 are within bounds.
|
|
471
|
+
eps32 = np.finfo(np.float32).eps
|
|
472
|
+
mag_threshold = np.floor(np.sqrt(NK_ATOL / (4 * eps32)))
|
|
473
|
+
decoded = lut[valid_bytes.astype(int)]
|
|
474
|
+
magnitude_ok = np.abs(decoded) <= mag_threshold
|
|
475
|
+
if not np.all(magnitude_ok):
|
|
476
|
+
valid_bytes = valid_bytes[magnitude_ok]
|
|
477
|
+
raw = valid_bytes[np.random.randint(0, len(valid_bytes), size=shape)]
|
|
478
|
+
baseline = lut[raw.astype(int)]
|
|
479
|
+
return raw, baseline
|
|
480
|
+
|
|
481
|
+
if dtype == "int4":
|
|
482
|
+
values = np.random.randint(-8, 8, size=shape, dtype=np.int8)
|
|
483
|
+
baseline = values.astype(np.float64)
|
|
484
|
+
raw = i8_downcast_to_i4(values)
|
|
485
|
+
return raw, baseline
|
|
486
|
+
|
|
487
|
+
if dtype == "uint4":
|
|
488
|
+
values = np.random.randint(0, 16, size=shape, dtype=np.uint8)
|
|
489
|
+
baseline = values.astype(np.float64)
|
|
490
|
+
raw = u8_downcast_to_u4(values)
|
|
491
|
+
return raw, baseline
|
|
492
|
+
|
|
493
|
+
raise ValueError(f"Unsupported dtype for make_random: {dtype}")
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _make_random_fallback(shape, dtype, seed):
|
|
497
|
+
"""NumPy-free random-data factory using ``nk.iota``.
|
|
498
|
+
|
|
499
|
+
Returns ``(nk_tensor, list_of_floats)`` — the list serves as the
|
|
500
|
+
baseline for Decimal-based precise functions.
|
|
501
|
+
|
|
502
|
+
Values are centered around zero and scaled to approximately [-3, 3]
|
|
503
|
+
(matching ``np.random.randn`` magnitude) to avoid overflow in
|
|
504
|
+
low-precision types.
|
|
505
|
+
"""
|
|
506
|
+
n = 1
|
|
507
|
+
for d in shape:
|
|
508
|
+
n *= d
|
|
509
|
+
# Build centered iota: values from -n//2 to +n//2
|
|
510
|
+
raw = nk.iota(shape, seed=seed - n // 2, dtype=dtype)
|
|
511
|
+
if n > 6:
|
|
512
|
+
# Scale down to [-3, 3] range to match np.random.randn magnitude.
|
|
513
|
+
# This prevents overflow in product ops for float16/bf16.
|
|
514
|
+
scale_factor = 6.0 / n
|
|
515
|
+
raw = nk.scale(raw, alpha=scale_factor, beta=0.0)
|
|
516
|
+
baseline = [float(x) for x in raw.flatten()]
|
|
517
|
+
return raw, baseline
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def make_random(shape: int | tuple[int, ...], dtype: str, seed: int = 0) -> tuple[Any, Any]:
|
|
521
|
+
"""Unified random-data factory.
|
|
522
|
+
|
|
523
|
+
Returns ``(raw, baseline)`` where:
|
|
524
|
+
|
|
525
|
+
- *raw*: data in the dtype's storage format, suitable for SIMD kernels.
|
|
526
|
+
- *baseline*: ``float64`` (or ``complex128``) array for reference comparison.
|
|
527
|
+
When NumPy is available this is a NumPy array; otherwise a plain ``list[float]``.
|
|
528
|
+
|
|
529
|
+
For exotic types the raw array uses a NumPy-native storage dtype
|
|
530
|
+
(``uint16`` for bf16, ``uint8`` for float8/float6).
|
|
531
|
+
"""
|
|
532
|
+
if isinstance(shape, int):
|
|
533
|
+
shape = (shape,)
|
|
534
|
+
|
|
535
|
+
if numpy_available:
|
|
536
|
+
return _make_random_numpy(shape, dtype)
|
|
537
|
+
|
|
538
|
+
return _make_random_fallback(shape, dtype, seed)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def make_nk(np_arr: np.ndarray, dtype: str | None = None) -> nk.Tensor:
|
|
542
|
+
"""Copy a NumPy array into a NumKong tensor."""
|
|
543
|
+
if dtype is None:
|
|
544
|
+
dtype = str(np_arr.dtype)
|
|
545
|
+
nk_arr = nk.zeros(np_arr.shape, dtype=dtype)
|
|
546
|
+
dst = np.asarray(nk_arr)
|
|
547
|
+
src = np.ascontiguousarray(np_arr)
|
|
548
|
+
if dst.dtype != src.dtype:
|
|
549
|
+
src = src.view(np.uint8).reshape(dst.shape)
|
|
550
|
+
np.copyto(dst, src)
|
|
551
|
+
return nk_arr
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def downcast_f32_to_dtype(f32_arr: np.ndarray, dtype: str) -> tuple[np.ndarray, np.ndarray]:
|
|
555
|
+
"""Downcast an f32 array to *dtype*, returning (raw, f64_baseline).
|
|
556
|
+
|
|
557
|
+
For native NumPy dtypes (float16/32/64, int*), casts directly.
|
|
558
|
+
For bfloat16, uses round-to-nearest bf16 truncation.
|
|
559
|
+
The baseline is always derived from the *actually stored* values
|
|
560
|
+
(post-quantization), not the original f32.
|
|
561
|
+
"""
|
|
562
|
+
if dtype in ("bfloat16", "bf16"):
|
|
563
|
+
f32_rounded, raw = f32_downcast_to_bf16(f32_arr)
|
|
564
|
+
return raw, f32_rounded.astype(np.float64)
|
|
565
|
+
raw = f32_arr.astype(dtype)
|
|
566
|
+
return raw, raw.astype(np.float64)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
available_capabilities: dict[str, str] = nk.get_capabilities()
|
|
570
|
+
|
|
571
|
+
# fmt: off
|
|
572
|
+
possible_x86_capabilities: list[str] = [
|
|
573
|
+
"haswell", "alder", "sierra",
|
|
574
|
+
"skylake", "icelake", "genoa", "turin", "sapphire",
|
|
575
|
+
"sapphireamx", "graniteamx", "diamond",
|
|
576
|
+
]
|
|
577
|
+
possible_arm_capabilities: list[str] = [
|
|
578
|
+
"neon", "neonhalf", "neonfhm", "neonbfdot", "neonsdot", "neonfp8",
|
|
579
|
+
"sve", "svehalf", "svebfdot", "svesdot", "sve2", "sve2p1",
|
|
580
|
+
"sme", "sme2", "sme2p1", "smef64", "smehalf", "smebf16", "smebi32", "smelut2", "smefa64",
|
|
581
|
+
]
|
|
582
|
+
possible_rvv_capabilities: list[str] = ["rvv", "rvvhalf", "rvvbf16", "rvvbb"]
|
|
583
|
+
possible_loongarch_capabilities: list[str] = ["loongsonasx"]
|
|
584
|
+
possible_power_capabilities: list[str] = ["powervsx"]
|
|
585
|
+
possible_wasm_capabilities: list[str] = ["v128relaxed"]
|
|
586
|
+
# fmt: on
|
|
587
|
+
|
|
588
|
+
possible_x86_capabilities = [c for c in possible_x86_capabilities if available_capabilities.get(c, False)]
|
|
589
|
+
possible_arm_capabilities = [c for c in possible_arm_capabilities if available_capabilities.get(c, False)]
|
|
590
|
+
possible_rvv_capabilities = [c for c in possible_rvv_capabilities if available_capabilities.get(c, False)]
|
|
591
|
+
possible_loongarch_capabilities = [c for c in possible_loongarch_capabilities if available_capabilities.get(c, False)]
|
|
592
|
+
possible_power_capabilities = [c for c in possible_power_capabilities if available_capabilities.get(c, False)]
|
|
593
|
+
possible_wasm_capabilities = [c for c in possible_wasm_capabilities if available_capabilities.get(c, False)]
|
|
594
|
+
|
|
595
|
+
hardware_capabilities: list[str] = []
|
|
596
|
+
machine_architecture = platform.machine()
|
|
597
|
+
|
|
598
|
+
if sys.platform == "linux":
|
|
599
|
+
if machine_architecture == "x86_64":
|
|
600
|
+
hardware_capabilities = possible_x86_capabilities
|
|
601
|
+
elif machine_architecture == "aarch64":
|
|
602
|
+
hardware_capabilities = possible_arm_capabilities
|
|
603
|
+
elif machine_architecture == "riscv64":
|
|
604
|
+
hardware_capabilities = possible_rvv_capabilities
|
|
605
|
+
elif machine_architecture == "loongarch64":
|
|
606
|
+
hardware_capabilities = possible_loongarch_capabilities
|
|
607
|
+
elif machine_architecture in ("ppc64le", "ppc64"):
|
|
608
|
+
hardware_capabilities = possible_power_capabilities
|
|
609
|
+
elif sys.platform == "darwin":
|
|
610
|
+
if machine_architecture == "x86_64":
|
|
611
|
+
hardware_capabilities = possible_x86_capabilities
|
|
612
|
+
elif machine_architecture == "arm64":
|
|
613
|
+
hardware_capabilities = possible_arm_capabilities
|
|
614
|
+
elif sys.platform == "win32":
|
|
615
|
+
if machine_architecture == "AMD64":
|
|
616
|
+
hardware_capabilities = possible_x86_capabilities
|
|
617
|
+
elif machine_architecture == "ARM64":
|
|
618
|
+
hardware_capabilities = possible_arm_capabilities
|
|
619
|
+
elif sys.platform.startswith("freebsd"):
|
|
620
|
+
if machine_architecture == "amd64":
|
|
621
|
+
hardware_capabilities = possible_x86_capabilities
|
|
622
|
+
elif machine_architecture == "arm64":
|
|
623
|
+
hardware_capabilities = possible_arm_capabilities
|
|
624
|
+
elif sys.platform in ("emscripten", "wasi"):
|
|
625
|
+
hardware_capabilities = possible_wasm_capabilities
|
|
626
|
+
|
|
627
|
+
possible_capabilities: list[str] = ["serial", *hardware_capabilities]
|
|
628
|
+
|
|
629
|
+
current_capability: str | None = None
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def keep_one_capability(cap: str):
|
|
633
|
+
global current_capability
|
|
634
|
+
assert cap in possible_capabilities, f"Capability {cap} is not available on this platform."
|
|
635
|
+
if cap == current_capability:
|
|
636
|
+
return
|
|
637
|
+
for c in possible_capabilities:
|
|
638
|
+
if c != cap and c != "serial":
|
|
639
|
+
nk.disable_capability(c)
|
|
640
|
+
if cap != "serial":
|
|
641
|
+
nk.enable_capability(cap)
|
|
642
|
+
current_capability = cap
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def create_stats() -> dict[str, list]:
|
|
646
|
+
"""Create a fresh stats dict for error collection."""
|
|
647
|
+
return {
|
|
648
|
+
"metric": [],
|
|
649
|
+
"ndim": [],
|
|
650
|
+
"dtype": [],
|
|
651
|
+
"capability": [],
|
|
652
|
+
"absolute_baseline_error": [],
|
|
653
|
+
"relative_baseline_error": [],
|
|
654
|
+
"absolute_nk_error": [],
|
|
655
|
+
"relative_nk_error": [],
|
|
656
|
+
"accurate_duration": [],
|
|
657
|
+
"baseline_duration": [],
|
|
658
|
+
"nk_duration": [],
|
|
659
|
+
"warnings": [],
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
_SENTINEL = object()
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def _infer_dtype_name(value) -> str:
|
|
667
|
+
"""Extract a dtype name string from a NumPy array, nk.Tensor, or scalar."""
|
|
668
|
+
if hasattr(value, "dtype"):
|
|
669
|
+
dt = value.dtype
|
|
670
|
+
if hasattr(dt, "name"):
|
|
671
|
+
return dt.name
|
|
672
|
+
return str(dt)
|
|
673
|
+
if isinstance(value, int):
|
|
674
|
+
return "int64"
|
|
675
|
+
if isinstance(value, float):
|
|
676
|
+
return "float64"
|
|
677
|
+
if numpy_available:
|
|
678
|
+
arr = np.asarray(value)
|
|
679
|
+
return arr.dtype.name
|
|
680
|
+
return ""
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def assert_allclose(
|
|
684
|
+
actual: Any, expected: Any, atol: object = _SENTINEL, rtol: object = _SENTINEL, err_msg: str = ""
|
|
685
|
+
) -> None:
|
|
686
|
+
"""Drop-in replacement for ``np.testing.assert_allclose`` with dtype-aware defaults.
|
|
687
|
+
|
|
688
|
+
When both *atol* and *rtol* are omitted the tolerances are inferred from
|
|
689
|
+
the dtype of *actual* via :func:`tolerances_for_dtype`.
|
|
690
|
+
"""
|
|
691
|
+
if atol is _SENTINEL and rtol is _SENTINEL:
|
|
692
|
+
dtype_name = _infer_dtype_name(actual)
|
|
693
|
+
atol, rtol = tolerances_for_dtype(dtype_name)
|
|
694
|
+
else:
|
|
695
|
+
if atol is _SENTINEL:
|
|
696
|
+
atol = 0
|
|
697
|
+
if rtol is _SENTINEL:
|
|
698
|
+
rtol = 1e-7
|
|
699
|
+
if numpy_available:
|
|
700
|
+
a_arr = np.asarray(actual)
|
|
701
|
+
e_arr = np.asarray(expected)
|
|
702
|
+
if not np.issubdtype(a_arr.dtype, np.complexfloating):
|
|
703
|
+
a_arr = a_arr.astype(float)
|
|
704
|
+
if not np.issubdtype(e_arr.dtype, np.complexfloating):
|
|
705
|
+
e_arr = e_arr.astype(float)
|
|
706
|
+
np.testing.assert_allclose(
|
|
707
|
+
a_arr,
|
|
708
|
+
e_arr,
|
|
709
|
+
atol=atol,
|
|
710
|
+
rtol=rtol,
|
|
711
|
+
err_msg=err_msg,
|
|
712
|
+
)
|
|
713
|
+
return
|
|
714
|
+
# Scalar path
|
|
715
|
+
if not hasattr(actual, "__len__") and not hasattr(expected, "__len__"):
|
|
716
|
+
a, e = float(actual), float(expected)
|
|
717
|
+
if abs(a - e) > atol + rtol * abs(e):
|
|
718
|
+
raise AssertionError(f"Not close: {a} vs {e}. {err_msg}")
|
|
719
|
+
return
|
|
720
|
+
# Iterable path
|
|
721
|
+
if hasattr(actual, "flatten"):
|
|
722
|
+
actual = actual.flatten()
|
|
723
|
+
if hasattr(expected, "flatten"):
|
|
724
|
+
expected = expected.flatten()
|
|
725
|
+
for i, (ai, ei) in enumerate(zip(actual, expected)):
|
|
726
|
+
ai_f, ei_f = float(ai), float(ei)
|
|
727
|
+
if abs(ai_f - ei_f) > atol + rtol * abs(ei_f):
|
|
728
|
+
raise AssertionError(f"Element {i}: {ai_f} vs {ei_f}. {err_msg}")
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def _compute_errors_numpy(accurate_result, baseline_result, nk_result):
|
|
732
|
+
"""Compute error metrics using NumPy."""
|
|
733
|
+
accurate_result = np.asarray(accurate_result)
|
|
734
|
+
eps = np.finfo(accurate_result.dtype).resolution if np.issubdtype(accurate_result.dtype, np.inexact) else 1.0
|
|
735
|
+
if baseline_result is None:
|
|
736
|
+
abs_bl, rel_bl = float("nan"), float("nan")
|
|
737
|
+
else:
|
|
738
|
+
abs_bl = float(np.max(np.abs(baseline_result - accurate_result)))
|
|
739
|
+
rel_bl = float(np.max(np.abs(baseline_result - accurate_result) / (np.abs(accurate_result) + eps)))
|
|
740
|
+
abs_nk = float(np.max(np.abs(nk_result - accurate_result)))
|
|
741
|
+
rel_nk = float(np.max(np.abs(nk_result - accurate_result) / (np.abs(accurate_result) + eps)))
|
|
742
|
+
return abs_bl, rel_bl, abs_nk, rel_nk
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def _compute_errors_fallback(accurate_result, baseline_result, nk_result):
|
|
746
|
+
"""Compute error metrics using pure Python."""
|
|
747
|
+
eps = 1e-15
|
|
748
|
+
accurate_f = float(accurate_result) if not hasattr(accurate_result, "__len__") else None
|
|
749
|
+
if accurate_f is not None:
|
|
750
|
+
nk_f = float(nk_result)
|
|
751
|
+
abs_nk = abs(nk_f - accurate_f)
|
|
752
|
+
rel_nk = abs(nk_f - accurate_f) / (abs(accurate_f) + eps)
|
|
753
|
+
else:
|
|
754
|
+
acc_flat = list(accurate_result.flatten()) if hasattr(accurate_result, "flatten") else list(accurate_result)
|
|
755
|
+
nk_flat = list(nk_result.flatten()) if hasattr(nk_result, "flatten") else list(nk_result)
|
|
756
|
+
abs_errs = [abs(float(a) - float(b)) for a, b in zip(nk_flat, acc_flat)]
|
|
757
|
+
rel_errs = [abs(float(a) - float(b)) / (abs(float(b)) + eps) for a, b in zip(nk_flat, acc_flat)]
|
|
758
|
+
abs_nk = max(abs_errs)
|
|
759
|
+
rel_nk = max(rel_errs)
|
|
760
|
+
if baseline_result is None:
|
|
761
|
+
abs_bl, rel_bl = float("nan"), float("nan")
|
|
762
|
+
else:
|
|
763
|
+
bl_f = float(baseline_result) if not hasattr(baseline_result, "__len__") else None
|
|
764
|
+
if bl_f is not None:
|
|
765
|
+
abs_bl = abs(bl_f - float(accurate_result))
|
|
766
|
+
rel_bl = abs(bl_f - float(accurate_result)) / (abs(float(accurate_result)) + eps)
|
|
767
|
+
else:
|
|
768
|
+
acc_flat = list(accurate_result.flatten()) if hasattr(accurate_result, "flatten") else list(accurate_result)
|
|
769
|
+
bl_flat = list(baseline_result.flatten()) if hasattr(baseline_result, "flatten") else list(baseline_result)
|
|
770
|
+
abs_errs = [abs(float(a) - float(b)) for a, b in zip(bl_flat, acc_flat)]
|
|
771
|
+
rel_errs = [abs(float(a) - float(b)) / (abs(float(b)) + eps) for a, b in zip(bl_flat, acc_flat)]
|
|
772
|
+
abs_bl = max(abs_errs)
|
|
773
|
+
rel_bl = max(rel_errs)
|
|
774
|
+
return abs_bl, rel_bl, abs_nk, rel_nk
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def collect_errors(
|
|
778
|
+
metric: str,
|
|
779
|
+
ndim: int,
|
|
780
|
+
dtype: str,
|
|
781
|
+
accurate_result: float,
|
|
782
|
+
accurate_duration: float,
|
|
783
|
+
baseline_result: float,
|
|
784
|
+
baseline_duration: float,
|
|
785
|
+
nk_result: float,
|
|
786
|
+
nk_duration: float,
|
|
787
|
+
stats: dict[str, list],
|
|
788
|
+
) -> None:
|
|
789
|
+
"""Calculates and aggregates errors for a given test."""
|
|
790
|
+
if numpy_available:
|
|
791
|
+
abs_bl, rel_bl, abs_nk, rel_nk = _compute_errors_numpy(accurate_result, baseline_result, nk_result)
|
|
792
|
+
else:
|
|
793
|
+
abs_bl, rel_bl, abs_nk, rel_nk = _compute_errors_fallback(accurate_result, baseline_result, nk_result)
|
|
794
|
+
|
|
795
|
+
stats["metric"].append(metric)
|
|
796
|
+
stats["ndim"].append(ndim)
|
|
797
|
+
stats["dtype"].append(dtype)
|
|
798
|
+
stats["capability"].append(current_capability or "unknown")
|
|
799
|
+
stats["absolute_baseline_error"].append(abs_bl)
|
|
800
|
+
stats["relative_baseline_error"].append(rel_bl)
|
|
801
|
+
stats["absolute_nk_error"].append(abs_nk)
|
|
802
|
+
stats["relative_nk_error"].append(rel_nk)
|
|
803
|
+
stats["accurate_duration"].append(accurate_duration)
|
|
804
|
+
stats["baseline_duration"].append(baseline_duration)
|
|
805
|
+
stats["nk_duration"].append(nk_duration)
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def collect_warnings(message: str, stats: dict[str, list]) -> None:
|
|
809
|
+
"""Collects warnings for the final report."""
|
|
810
|
+
full_name = os.environ.get("PYTEST_CURRENT_TEST", "unknown::unknown").split(" ")[0]
|
|
811
|
+
function_name = full_name.split("::")[-1].split("[")[0]
|
|
812
|
+
stats["warnings"].append((function_name, message))
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def format_scientific(value):
|
|
816
|
+
"""Format a float as compact scientific notation (e.g. 7.4e-5). Return '0' for exact zero."""
|
|
817
|
+
if value == 0:
|
|
818
|
+
return "0"
|
|
819
|
+
s = f"{value:.1e}"
|
|
820
|
+
if "e" not in s:
|
|
821
|
+
return s # inf, nan, etc.
|
|
822
|
+
mantissa, exp = s.split("e")
|
|
823
|
+
exp_sign = exp[0]
|
|
824
|
+
exp_digits = exp[1:].lstrip("0") or "0"
|
|
825
|
+
return f"{mantissa}e{exp_sign}{exp_digits}"
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def pad_with_ansi_color(visible, width, code):
|
|
829
|
+
"""Pad visible string to width first, then wrap in ANSI so escape codes don't break alignment."""
|
|
830
|
+
padded = f"{visible:<{width}}"
|
|
831
|
+
return f"\033[{code}m{padded}\033[0m"
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
CapabilityRecord = collections.namedtuple(
|
|
835
|
+
"CapabilityRecord",
|
|
836
|
+
["metric", "ndim", "dtype", "capability", "baseline_error_mean", "nk_error_mean", "speedup_mean"],
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def print_stats_report(stats: dict[str, list]) -> None:
|
|
841
|
+
"""Print a condensed error/speedup report: two rows per (metric, dtype) showing min/max ndim."""
|
|
842
|
+
if not stats["metric"]:
|
|
843
|
+
return
|
|
844
|
+
|
|
845
|
+
# Stage 1: Group raw stats by (metric, ndim, dtype, capability) and compute per-group means.
|
|
846
|
+
grouped = collections.defaultdict(
|
|
847
|
+
lambda: {"relative_baseline_error": [], "relative_nk_error": [], "baseline_duration": [], "nk_duration": []}
|
|
848
|
+
)
|
|
849
|
+
for metric, ndim, dtype, capability, rel_base, rel_nk, base_dur, nk_dur in zip(
|
|
850
|
+
stats["metric"],
|
|
851
|
+
stats["ndim"],
|
|
852
|
+
stats["dtype"],
|
|
853
|
+
stats["capability"],
|
|
854
|
+
stats["relative_baseline_error"],
|
|
855
|
+
stats["relative_nk_error"],
|
|
856
|
+
stats["baseline_duration"],
|
|
857
|
+
stats["nk_duration"],
|
|
858
|
+
):
|
|
859
|
+
key = (metric, ndim, dtype, capability)
|
|
860
|
+
grouped[key]["relative_baseline_error"].append(rel_base)
|
|
861
|
+
grouped[key]["relative_nk_error"].append(rel_nk)
|
|
862
|
+
grouped[key]["baseline_duration"].append(base_dur)
|
|
863
|
+
grouped[key]["nk_duration"].append(nk_dur)
|
|
864
|
+
|
|
865
|
+
cap_records = []
|
|
866
|
+
for (metric, ndim, dtype, capability), vals in grouped.items():
|
|
867
|
+
n = len(vals["nk_duration"])
|
|
868
|
+
base_err_mean = sum(vals["relative_baseline_error"]) / n
|
|
869
|
+
nk_err_mean = sum(vals["relative_nk_error"]) / n
|
|
870
|
+
speedups = [b / nk for b, nk in zip(vals["baseline_duration"], vals["nk_duration"]) if nk > 0]
|
|
871
|
+
speedup_mean = sum(speedups) / len(speedups) if speedups else 0.0
|
|
872
|
+
cap_records.append(CapabilityRecord(metric, ndim, dtype, capability, base_err_mean, nk_err_mean, speedup_mean))
|
|
873
|
+
|
|
874
|
+
# Stage 2: Re-aggregate by (metric, dtype). For each, find min/max ndim and cross-capability aggregates.
|
|
875
|
+
by_metric_dtype = collections.defaultdict(list)
|
|
876
|
+
for rec in cap_records:
|
|
877
|
+
by_metric_dtype[(rec.metric, rec.dtype)].append(rec)
|
|
878
|
+
|
|
879
|
+
# Each output row: (metric_str, dtype_str, ndim_str, base_err_str, worst_nk_err_str, best_speedup_str)
|
|
880
|
+
rows = []
|
|
881
|
+
for (metric, dtype), recs in sorted(by_metric_dtype.items()):
|
|
882
|
+
all_ndims = sorted(set(r.ndim for r in recs))
|
|
883
|
+
min_ndim, max_ndim = all_ndims[0], all_ndims[-1]
|
|
884
|
+
single_ndim = min_ndim == max_ndim
|
|
885
|
+
target_ndims = [min_ndim] if single_ndim else [min_ndim, max_ndim]
|
|
886
|
+
|
|
887
|
+
for i, target_ndim in enumerate(target_ndims):
|
|
888
|
+
subset = [r for r in recs if r.ndim == target_ndim]
|
|
889
|
+
if not subset:
|
|
890
|
+
continue
|
|
891
|
+
|
|
892
|
+
# Base error: average across capabilities
|
|
893
|
+
base_err = sum(r.baseline_error_mean for r in subset) / len(subset)
|
|
894
|
+
# Worst NK error: capability with highest mean NK error
|
|
895
|
+
worst_rec = max(subset, key=lambda r: r.nk_error_mean)
|
|
896
|
+
worst_nk_err = worst_rec.nk_error_mean
|
|
897
|
+
worst_cap = worst_rec.capability
|
|
898
|
+
# Best speedup: capability with highest mean speedup
|
|
899
|
+
best_rec = max(subset, key=lambda r: r.speedup_mean)
|
|
900
|
+
best_speedup = best_rec.speedup_mean
|
|
901
|
+
best_cap = best_rec.capability
|
|
902
|
+
best_cap_err = best_rec.nk_error_mean
|
|
903
|
+
|
|
904
|
+
# Format strings
|
|
905
|
+
if i == 0:
|
|
906
|
+
metric_str = metric
|
|
907
|
+
dtype_str = dtype
|
|
908
|
+
else:
|
|
909
|
+
metric_str = ""
|
|
910
|
+
dtype_str = ""
|
|
911
|
+
|
|
912
|
+
if single_ndim:
|
|
913
|
+
ndim_str = str(target_ndim)
|
|
914
|
+
elif i == 0:
|
|
915
|
+
ndim_str = f"\u230a{target_ndim:>4}\u230b"
|
|
916
|
+
else:
|
|
917
|
+
ndim_str = f"\u2308{target_ndim:>4}\u2309"
|
|
918
|
+
|
|
919
|
+
rows.append(
|
|
920
|
+
(
|
|
921
|
+
metric_str,
|
|
922
|
+
dtype_str,
|
|
923
|
+
ndim_str,
|
|
924
|
+
base_err,
|
|
925
|
+
worst_nk_err,
|
|
926
|
+
worst_cap,
|
|
927
|
+
best_speedup,
|
|
928
|
+
best_cap,
|
|
929
|
+
best_cap_err,
|
|
930
|
+
)
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
# Stage 3: Render
|
|
934
|
+
col_w = {"kernel": 17, "dtype": 12, "ndim": 8, "base_err": 14, "worst_nk": 30, "best_spd": 34}
|
|
935
|
+
header = (
|
|
936
|
+
f"{'Kernel':<{col_w['kernel']}}"
|
|
937
|
+
f"{'DType':<{col_w['dtype']}}"
|
|
938
|
+
f"{'NDim':<{col_w['ndim']}}"
|
|
939
|
+
f"{'Base Error':<{col_w['base_err']}}"
|
|
940
|
+
f"{'Worst NK Error':<{col_w['worst_nk']}}"
|
|
941
|
+
f"{'Best NK Speedup':<{col_w['best_spd']}}"
|
|
942
|
+
)
|
|
943
|
+
sep = "\u2500" * len(header)
|
|
944
|
+
print(f"\n\n{header}")
|
|
945
|
+
print(sep)
|
|
946
|
+
|
|
947
|
+
for (
|
|
948
|
+
metric_str,
|
|
949
|
+
dtype_str,
|
|
950
|
+
ndim_str,
|
|
951
|
+
base_err,
|
|
952
|
+
worst_nk_err,
|
|
953
|
+
worst_cap,
|
|
954
|
+
best_speedup,
|
|
955
|
+
best_cap,
|
|
956
|
+
best_cap_err,
|
|
957
|
+
) in rows:
|
|
958
|
+
base_err_s = format_scientific(base_err)
|
|
959
|
+
worst_nk_s = f"{format_scientific(worst_nk_err)} \u2039{worst_cap}\u203a"
|
|
960
|
+
best_spd_s = f"{best_speedup:.1f}x \u2039{best_cap}, err {format_scientific(best_cap_err)}\u203a"
|
|
961
|
+
|
|
962
|
+
# Color for worst NK error: red if NK error > base error
|
|
963
|
+
nk_err_code = "31" if worst_nk_err > base_err else "0"
|
|
964
|
+
# Color for speedup: green >=2x, yellow 1-2x, red <1x
|
|
965
|
+
if best_speedup >= 2.0:
|
|
966
|
+
spd_code = "32"
|
|
967
|
+
elif best_speedup >= 1.0:
|
|
968
|
+
spd_code = "33"
|
|
969
|
+
else:
|
|
970
|
+
spd_code = "31"
|
|
971
|
+
|
|
972
|
+
line = (
|
|
973
|
+
f"{metric_str:<{col_w['kernel']}}"
|
|
974
|
+
f"{dtype_str:<{col_w['dtype']}}"
|
|
975
|
+
f"{ndim_str:<{col_w['ndim']}}"
|
|
976
|
+
f"{base_err_s:<{col_w['base_err']}}"
|
|
977
|
+
f"{pad_with_ansi_color(worst_nk_s, col_w['worst_nk'], nk_err_code)}"
|
|
978
|
+
f"{pad_with_ansi_color(best_spd_s, col_w['best_spd'], spd_code)}"
|
|
979
|
+
)
|
|
980
|
+
print(line)
|
|
981
|
+
|
|
982
|
+
warnings_list = stats.get("warnings", [])
|
|
983
|
+
warnings_list = sorted(warnings_list)
|
|
984
|
+
warnings_list = [f"{name}: {message}" for name, message in warnings_list]
|
|
985
|
+
if len(warnings_list) != 0:
|
|
986
|
+
print("\nWarnings:")
|
|
987
|
+
warning_counts = collections.Counter(warnings_list)
|
|
988
|
+
for warning, count in sorted(warning_counts.items()):
|
|
989
|
+
print(f"- {count}x times: {warning}")
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
@pytest.fixture(autouse=True)
|
|
993
|
+
def seed_rng(__pytest_repeat_step_number: int) -> int:
|
|
994
|
+
"""Auto-seed NumPy RNG before every test and return the computed seed.
|
|
995
|
+
|
|
996
|
+
When NK_SEED is set, each @pytest.mark.repeat() step gets a unique
|
|
997
|
+
derived seed. Tests that use ``nk.hash`` can accept this fixture as a
|
|
998
|
+
parameter and pass the return value as the ``seed=`` argument so that
|
|
999
|
+
repeated runs exercise different data.
|
|
1000
|
+
"""
|
|
1001
|
+
step = __pytest_repeat_step_number or 0
|
|
1002
|
+
seed = (_nk_seed_base or 0) + step
|
|
1003
|
+
if numpy_available and _nk_seed_base is not None:
|
|
1004
|
+
np.random.seed(seed)
|
|
1005
|
+
return seed
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
@pytest.fixture
|
|
1009
|
+
def nk_seed(seed_rng: int) -> int:
|
|
1010
|
+
"""Per-test seed that incorporates the repeat step number.
|
|
1011
|
+
|
|
1012
|
+
Wraps *seed_rng* under the name ``nk_seed`` so tests that build data
|
|
1013
|
+
with ``nk.hash`` / ``nk.iota`` can request it by name.
|
|
1014
|
+
"""
|
|
1015
|
+
return seed_rng
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
# Map nk dtype → (array.array typecode, low, high)
|
|
1019
|
+
ARRAY_TYPECODES = {
|
|
1020
|
+
"float32": ("f", -10.0, 10.0),
|
|
1021
|
+
"float64": ("d", -10.0, 10.0),
|
|
1022
|
+
"int8": ("b", -128, 127),
|
|
1023
|
+
"uint8": ("B", 0, 255),
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
def make_random_buffer(n: int, dtype: str = "float32") -> array.array:
|
|
1028
|
+
"""Create a random array.array for the given dtype — no numpy needed."""
|
|
1029
|
+
tc, lo, hi = ARRAY_TYPECODES[dtype]
|
|
1030
|
+
if tc in ("f", "d"):
|
|
1031
|
+
return array.array(tc, [random.uniform(lo, hi) for _ in range(n)])
|
|
1032
|
+
else:
|
|
1033
|
+
return array.array(tc, [random.randint(int(lo), int(hi)) for _ in range(n)])
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
def make_positive_buffer(n: int, dtype: str = "float32") -> array.array:
|
|
1037
|
+
"""Create a random positive array.array — for probability distributions."""
|
|
1038
|
+
tc = "f" if dtype == "float32" else "d"
|
|
1039
|
+
vals = [random.uniform(0.01, 1.0) for _ in range(n)]
|
|
1040
|
+
total = sum(vals)
|
|
1041
|
+
return array.array(tc, [v / total for v in vals])
|