usearch 2.21.4 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/binding.gyp +15 -37
- package/c/CMakeLists.txt +4 -5
- package/c/README.md +7 -7
- package/c/lib.cpp +32 -14
- package/c/test.c +83 -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/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.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,1815 @@
|
|
|
1
|
+
//! Spatial similarity: dot products, angular (cosine), and Euclidean distances.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides:
|
|
4
|
+
//!
|
|
5
|
+
//! - [`Dot`]: Inner product of two vectors
|
|
6
|
+
//! - [`Angular`]: Cosine distance (1 - cosine similarity)
|
|
7
|
+
//! - [`Euclidean`]: Squared L2 distance
|
|
8
|
+
//! - [`VDot`]: Complex-valued dot product
|
|
9
|
+
//! - [`Roots`]: Scalar square root and reciprocal square root
|
|
10
|
+
//! - [`SpatialSimilarity`]: Blanket trait combining `Dot + Angular + Euclidean`
|
|
11
|
+
//!
|
|
12
|
+
//! # Accumulator Widening — The Core Value Proposition
|
|
13
|
+
//!
|
|
14
|
+
//! Every spatial kernel promotes its accumulator to a wider type than the inputs.
|
|
15
|
+
//! This is not cosmetic — on long vectors of low-precision data, naive same-width
|
|
16
|
+
//! accumulation overflows silently (integers) or loses huge amounts of precision
|
|
17
|
+
//! (half-floats). NumKong widens systematically so every kernel in this module
|
|
18
|
+
//! returns a result that matches a textbook-accurate reference in the wide type:
|
|
19
|
+
//!
|
|
20
|
+
//! - **`f32` → `f64`**: single-precision inputs accumulate in double precision. A
|
|
21
|
+
//! `dot` over 2048 `f32` values returns `f64` and uses Neumaier-compensated
|
|
22
|
+
//! summation internally.
|
|
23
|
+
//! - **`f16` → `f32`** and **`bf16` → `f32`**: half-precision dots, norms, and
|
|
24
|
+
//! distances accumulate in `f32` rather than clamping at `f16::MAX = 65 504`.
|
|
25
|
+
//! - **`i8` → `i32`** (unsigned `u8` → `u32`): byte-level quantised inputs widen
|
|
26
|
+
//! into 32-bit integer accumulators. Two `i8` vectors of all `100`s and length
|
|
27
|
+
//! 2048 have a true dot of `20 480 000`, which overflows `i8` (saturates at
|
|
28
|
+
//! `±127`) and `i16` (`±32 767`) but is exact in `i32`.
|
|
29
|
+
//! - **FP8 variants** (`e4m3` / `e5m2` / `e2m3` / `e3m2`) all accumulate in `f32`.
|
|
30
|
+
//! - **4-bit packed** `i4x2` / `u4x2` behave like `i8` / `u8` but compute over
|
|
31
|
+
//! double the element count because each byte holds two logical values.
|
|
32
|
+
//!
|
|
33
|
+
//! This widening is the reason quantised retrieval pipelines (e.g. BFloat16 or
|
|
34
|
+
//! INT8 embeddings in vector search) can rely on NumKong without post-hoc rescaling.
|
|
35
|
+
//!
|
|
36
|
+
//! # Example — INT8 Dot Without Overflow
|
|
37
|
+
//!
|
|
38
|
+
//! ```
|
|
39
|
+
//! use numkong::Dot;
|
|
40
|
+
//!
|
|
41
|
+
//! // 2048-dim i8 vectors full of 100s: true dot = 2048 * 100 * 100 = 20_480_000,
|
|
42
|
+
//! // which would overflow i16 but fits exactly in i32.
|
|
43
|
+
//! let left = vec![100_i8; 2048];
|
|
44
|
+
//! let right = vec![100_i8; 2048];
|
|
45
|
+
//! let exact: i32 = i8::dot(&left, &right).unwrap();
|
|
46
|
+
//! assert_eq!(exact, 20_480_000);
|
|
47
|
+
//! ```
|
|
48
|
+
|
|
49
|
+
use crate::types::{
|
|
50
|
+
bf16, bf16c, e2m3, e3m2, e4m3, e5m2, f16, f16c, f32c, f64c, i4x2, u4x2, StorageElement,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
#[link(name = "numkong")]
|
|
54
|
+
extern "C" {
|
|
55
|
+
// Scalar roots
|
|
56
|
+
fn nk_f32_sqrt(x: f32) -> f32;
|
|
57
|
+
fn nk_f32_rsqrt(x: f32) -> f32;
|
|
58
|
+
fn nk_f64_sqrt(x: f64) -> f64;
|
|
59
|
+
fn nk_f64_rsqrt(x: f64) -> f64;
|
|
60
|
+
fn nk_f16_sqrt(x: u16) -> u16;
|
|
61
|
+
fn nk_f16_rsqrt(x: u16) -> u16;
|
|
62
|
+
|
|
63
|
+
// Vector dot products
|
|
64
|
+
fn nk_dot_i8(a: *const i8, b: *const i8, c: usize, d: *mut i32);
|
|
65
|
+
fn nk_dot_u8(a: *const u8, b: *const u8, c: usize, d: *mut u32);
|
|
66
|
+
fn nk_dot_f16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
67
|
+
fn nk_dot_bf16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
68
|
+
fn nk_dot_e4m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
69
|
+
fn nk_dot_e5m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
70
|
+
fn nk_dot_e2m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
71
|
+
fn nk_dot_e3m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
72
|
+
fn nk_dot_f32(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
73
|
+
fn nk_dot_f64(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
74
|
+
|
|
75
|
+
fn nk_dot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
76
|
+
fn nk_dot_bf16c(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
77
|
+
fn nk_dot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
78
|
+
fn nk_dot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
79
|
+
|
|
80
|
+
fn nk_vdot_f16c(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
81
|
+
fn nk_vdot_bf16c(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
82
|
+
fn nk_vdot_f32c(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
83
|
+
fn nk_vdot_f64c(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
84
|
+
|
|
85
|
+
// Spatial similarity/distance functions
|
|
86
|
+
fn nk_angular_i8(a: *const i8, b: *const i8, c: usize, d: *mut f32);
|
|
87
|
+
fn nk_angular_u8(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
88
|
+
fn nk_angular_f16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
89
|
+
fn nk_angular_bf16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
90
|
+
fn nk_angular_e4m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
91
|
+
fn nk_angular_e5m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
92
|
+
fn nk_angular_e2m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
93
|
+
fn nk_angular_e3m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
94
|
+
fn nk_angular_f32(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
95
|
+
fn nk_angular_f64(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
96
|
+
|
|
97
|
+
fn nk_sqeuclidean_i8(a: *const i8, b: *const i8, c: usize, d: *mut u32);
|
|
98
|
+
fn nk_sqeuclidean_u8(a: *const u8, b: *const u8, c: usize, d: *mut u32);
|
|
99
|
+
fn nk_sqeuclidean_f16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
100
|
+
fn nk_sqeuclidean_bf16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
101
|
+
fn nk_sqeuclidean_e4m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
102
|
+
fn nk_sqeuclidean_e5m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
103
|
+
fn nk_sqeuclidean_e2m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
104
|
+
fn nk_sqeuclidean_e3m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
105
|
+
fn nk_sqeuclidean_f32(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
106
|
+
fn nk_sqeuclidean_f64(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
107
|
+
|
|
108
|
+
fn nk_euclidean_i8(a: *const i8, b: *const i8, c: usize, d: *mut f32);
|
|
109
|
+
fn nk_euclidean_u8(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
110
|
+
fn nk_euclidean_f16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
111
|
+
fn nk_euclidean_bf16(a: *const u16, b: *const u16, c: usize, d: *mut f32);
|
|
112
|
+
fn nk_euclidean_e4m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
113
|
+
fn nk_euclidean_e5m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
114
|
+
fn nk_euclidean_e2m3(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
115
|
+
fn nk_euclidean_e3m2(a: *const u8, b: *const u8, c: usize, d: *mut f32);
|
|
116
|
+
fn nk_euclidean_f32(a: *const f32, b: *const f32, c: usize, d: *mut f64);
|
|
117
|
+
fn nk_euclidean_f64(a: *const f64, b: *const f64, c: usize, d: *mut f64);
|
|
118
|
+
|
|
119
|
+
// 4-bit integer kernels
|
|
120
|
+
fn nk_dot_i4(a: *const u8, b: *const u8, n: usize, result: *mut i32);
|
|
121
|
+
fn nk_dot_u4(a: *const u8, b: *const u8, n: usize, result: *mut u32);
|
|
122
|
+
fn nk_sqeuclidean_i4(a: *const u8, b: *const u8, n: usize, result: *mut u32);
|
|
123
|
+
fn nk_sqeuclidean_u4(a: *const u8, b: *const u8, n: usize, result: *mut u32);
|
|
124
|
+
fn nk_euclidean_i4(a: *const u8, b: *const u8, n: usize, result: *mut f32);
|
|
125
|
+
fn nk_euclidean_u4(a: *const u8, b: *const u8, n: usize, result: *mut f32);
|
|
126
|
+
fn nk_angular_i4(a: *const u8, b: *const u8, n: usize, result: *mut f32);
|
|
127
|
+
fn nk_angular_u4(a: *const u8, b: *const u8, n: usize, result: *mut f32);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// region: Scalar Roots
|
|
131
|
+
|
|
132
|
+
/// Scalar square-root and reciprocal-square-root operations backed by NumKong's
|
|
133
|
+
/// exported kernels.
|
|
134
|
+
///
|
|
135
|
+
/// Unlike the standard library's `f32::sqrt` / `f64::sqrt`, this trait routes into
|
|
136
|
+
/// the hand-tuned NumKong C kernels. On platforms where the ISA offers dedicated
|
|
137
|
+
/// reciprocal-sqrt instructions (e.g. `vrsqrte` on Arm NEON, `vrsqrt14` on AVX-512)
|
|
138
|
+
/// `rsqrt` uses a single refined Newton step to hit ~1 ULP accuracy — roughly 2-4×
|
|
139
|
+
/// faster than computing `1.0 / sqrt(x)` explicitly.
|
|
140
|
+
///
|
|
141
|
+
/// Implementations are provided for the three scalar float types: `f32`, `f64`, and
|
|
142
|
+
/// `f16`. Half-precision input is upcast to `f32`, computed, and downcast.
|
|
143
|
+
pub trait Roots: Sized {
|
|
144
|
+
/// Non-negative square root of `self`. Equivalent to the intrinsic `sqrt`
|
|
145
|
+
/// but routed through NumKong's kernel table — picks up any runtime-dispatched
|
|
146
|
+
/// fast path available on the host CPU.
|
|
147
|
+
fn sqrt(self) -> Self;
|
|
148
|
+
|
|
149
|
+
/// Reciprocal square root `1 / sqrt(self)` computed as a single primitive op
|
|
150
|
+
/// when the hardware supports it, or a Newton-refined fallback otherwise.
|
|
151
|
+
/// Useful inside normalization-heavy kernels (e.g. cosine similarity) where
|
|
152
|
+
/// a division plus a sqrt would otherwise dominate the cost.
|
|
153
|
+
fn rsqrt(self) -> Self;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
impl Roots for f32 {
|
|
157
|
+
/// Single-precision square root. Dispatches to the SIMD-assisted kernel.
|
|
158
|
+
fn sqrt(self) -> Self {
|
|
159
|
+
unsafe { nk_f32_sqrt(self) }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/// Single-precision reciprocal square root with a Newton refinement step.
|
|
163
|
+
fn rsqrt(self) -> Self {
|
|
164
|
+
unsafe { nk_f32_rsqrt(self) }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
impl Roots for f64 {
|
|
169
|
+
/// Double-precision square root — full IEEE 754 accuracy.
|
|
170
|
+
fn sqrt(self) -> Self {
|
|
171
|
+
unsafe { nk_f64_sqrt(self) }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Double-precision reciprocal square root.
|
|
175
|
+
fn rsqrt(self) -> Self {
|
|
176
|
+
unsafe { nk_f64_rsqrt(self) }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
impl Roots for f16 {
|
|
181
|
+
/// Half-precision square root. Input is upcast to `f32` internally and the
|
|
182
|
+
/// result is rounded back to `f16`.
|
|
183
|
+
fn sqrt(self) -> Self {
|
|
184
|
+
f16(unsafe { nk_f16_sqrt(self.0) })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/// Half-precision reciprocal square root with `f32` intermediate precision.
|
|
188
|
+
fn rsqrt(self) -> Self {
|
|
189
|
+
f16(unsafe { nk_f16_rsqrt(self.0) })
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// endregion: Scalar Roots
|
|
194
|
+
|
|
195
|
+
// region: Dot
|
|
196
|
+
|
|
197
|
+
/// Computes the **dot product** (inner product) between two vectors.
|
|
198
|
+
///
|
|
199
|
+
/// d = ∑ᵢ aᵢ × bᵢ
|
|
200
|
+
///
|
|
201
|
+
/// Range: unbounded. Returns `None` if lengths differ.
|
|
202
|
+
///
|
|
203
|
+
/// Implemented for: `f64`, `f32`, `f16`, `bf16`, `i8`, `u8`,
|
|
204
|
+
/// `e4m3`, `e5m2`, `e2m3`, `e3m2`, `i4x2`, `u4x2`.
|
|
205
|
+
///
|
|
206
|
+
/// # Example
|
|
207
|
+
/// ```
|
|
208
|
+
/// use numkong::Dot;
|
|
209
|
+
/// let a = vec![1.0_f32, 2.0, 3.0];
|
|
210
|
+
/// let b = vec![4.0_f32, 5.0, 6.0];
|
|
211
|
+
/// let result = f32::dot(&a, &b).unwrap();
|
|
212
|
+
/// assert!((result - 32.0).abs() < 1e-5);
|
|
213
|
+
/// ```
|
|
214
|
+
pub trait Dot: StorageElement {
|
|
215
|
+
type Output;
|
|
216
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output>;
|
|
217
|
+
|
|
218
|
+
/// Alias for `dot`.
|
|
219
|
+
fn inner(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
220
|
+
Self::dot(a, b)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
impl Dot for f64 {
|
|
225
|
+
type Output = f64;
|
|
226
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
227
|
+
if a.len() != b.len() {
|
|
228
|
+
return None;
|
|
229
|
+
}
|
|
230
|
+
let mut result: Self::Output = 0.0;
|
|
231
|
+
unsafe { nk_dot_f64(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
232
|
+
Some(result)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
impl Dot for f32 {
|
|
237
|
+
type Output = f64;
|
|
238
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
239
|
+
if a.len() != b.len() {
|
|
240
|
+
return None;
|
|
241
|
+
}
|
|
242
|
+
let mut result: Self::Output = 0.0;
|
|
243
|
+
unsafe { nk_dot_f32(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
244
|
+
Some(result)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
impl Dot for f16 {
|
|
249
|
+
type Output = f32;
|
|
250
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
251
|
+
if a.len() != b.len() {
|
|
252
|
+
return None;
|
|
253
|
+
}
|
|
254
|
+
let mut result: Self::Output = 0.0;
|
|
255
|
+
unsafe {
|
|
256
|
+
nk_dot_f16(
|
|
257
|
+
a.as_ptr() as *const u16,
|
|
258
|
+
b.as_ptr() as *const u16,
|
|
259
|
+
a.len(),
|
|
260
|
+
&mut result,
|
|
261
|
+
)
|
|
262
|
+
};
|
|
263
|
+
Some(result)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
impl Dot for bf16 {
|
|
268
|
+
type Output = f32;
|
|
269
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
270
|
+
if a.len() != b.len() {
|
|
271
|
+
return None;
|
|
272
|
+
}
|
|
273
|
+
let mut result: Self::Output = 0.0;
|
|
274
|
+
unsafe {
|
|
275
|
+
nk_dot_bf16(
|
|
276
|
+
a.as_ptr() as *const u16,
|
|
277
|
+
b.as_ptr() as *const u16,
|
|
278
|
+
a.len(),
|
|
279
|
+
&mut result,
|
|
280
|
+
)
|
|
281
|
+
};
|
|
282
|
+
Some(result)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
impl Dot for i8 {
|
|
287
|
+
type Output = i32;
|
|
288
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
289
|
+
if a.len() != b.len() {
|
|
290
|
+
return None;
|
|
291
|
+
}
|
|
292
|
+
let mut result: Self::Output = 0;
|
|
293
|
+
unsafe { nk_dot_i8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
294
|
+
Some(result)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
impl Dot for u8 {
|
|
299
|
+
type Output = u32;
|
|
300
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
301
|
+
if a.len() != b.len() {
|
|
302
|
+
return None;
|
|
303
|
+
}
|
|
304
|
+
let mut result: Self::Output = 0;
|
|
305
|
+
unsafe { nk_dot_u8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
306
|
+
Some(result)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
impl Dot for e4m3 {
|
|
311
|
+
type Output = f32;
|
|
312
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
313
|
+
if a.len() != b.len() {
|
|
314
|
+
return None;
|
|
315
|
+
}
|
|
316
|
+
let mut result: Self::Output = 0.0;
|
|
317
|
+
unsafe {
|
|
318
|
+
nk_dot_e4m3(
|
|
319
|
+
a.as_ptr() as *const u8,
|
|
320
|
+
b.as_ptr() as *const u8,
|
|
321
|
+
a.len(),
|
|
322
|
+
&mut result,
|
|
323
|
+
)
|
|
324
|
+
};
|
|
325
|
+
Some(result)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
impl Dot for e5m2 {
|
|
330
|
+
type Output = f32;
|
|
331
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
332
|
+
if a.len() != b.len() {
|
|
333
|
+
return None;
|
|
334
|
+
}
|
|
335
|
+
let mut result: Self::Output = 0.0;
|
|
336
|
+
unsafe {
|
|
337
|
+
nk_dot_e5m2(
|
|
338
|
+
a.as_ptr() as *const u8,
|
|
339
|
+
b.as_ptr() as *const u8,
|
|
340
|
+
a.len(),
|
|
341
|
+
&mut result,
|
|
342
|
+
)
|
|
343
|
+
};
|
|
344
|
+
Some(result)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
impl Dot for e2m3 {
|
|
349
|
+
type Output = f32;
|
|
350
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
351
|
+
if a.len() != b.len() {
|
|
352
|
+
return None;
|
|
353
|
+
}
|
|
354
|
+
let mut result: Self::Output = 0.0;
|
|
355
|
+
unsafe {
|
|
356
|
+
nk_dot_e2m3(
|
|
357
|
+
a.as_ptr() as *const u8,
|
|
358
|
+
b.as_ptr() as *const u8,
|
|
359
|
+
a.len(),
|
|
360
|
+
&mut result,
|
|
361
|
+
)
|
|
362
|
+
};
|
|
363
|
+
Some(result)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
impl Dot for e3m2 {
|
|
368
|
+
type Output = f32;
|
|
369
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
370
|
+
if a.len() != b.len() {
|
|
371
|
+
return None;
|
|
372
|
+
}
|
|
373
|
+
let mut result: Self::Output = 0.0;
|
|
374
|
+
unsafe {
|
|
375
|
+
nk_dot_e3m2(
|
|
376
|
+
a.as_ptr() as *const u8,
|
|
377
|
+
b.as_ptr() as *const u8,
|
|
378
|
+
a.len(),
|
|
379
|
+
&mut result,
|
|
380
|
+
)
|
|
381
|
+
};
|
|
382
|
+
Some(result)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
impl Dot for i4x2 {
|
|
387
|
+
type Output = i32;
|
|
388
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
389
|
+
if a.len() != b.len() {
|
|
390
|
+
return None;
|
|
391
|
+
}
|
|
392
|
+
let mut result: Self::Output = 0;
|
|
393
|
+
let element_count = a.len() * 2; // Each i4x2 contains 2 elements
|
|
394
|
+
unsafe {
|
|
395
|
+
nk_dot_i4(
|
|
396
|
+
a.as_ptr() as *const u8,
|
|
397
|
+
b.as_ptr() as *const u8,
|
|
398
|
+
element_count,
|
|
399
|
+
&mut result,
|
|
400
|
+
)
|
|
401
|
+
};
|
|
402
|
+
Some(result)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
impl Dot for u4x2 {
|
|
407
|
+
type Output = u32;
|
|
408
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
409
|
+
if a.len() != b.len() {
|
|
410
|
+
return None;
|
|
411
|
+
}
|
|
412
|
+
let mut result: Self::Output = 0;
|
|
413
|
+
let element_count = a.len() * 2; // Each u4x2 contains 2 elements
|
|
414
|
+
unsafe {
|
|
415
|
+
nk_dot_u4(
|
|
416
|
+
a.as_ptr() as *const u8,
|
|
417
|
+
b.as_ptr() as *const u8,
|
|
418
|
+
element_count,
|
|
419
|
+
&mut result,
|
|
420
|
+
)
|
|
421
|
+
};
|
|
422
|
+
Some(result)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
impl Dot for f16c {
|
|
427
|
+
type Output = f32c;
|
|
428
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
429
|
+
if a.len() != b.len() {
|
|
430
|
+
return None;
|
|
431
|
+
}
|
|
432
|
+
let mut result = [0.0f32; 2];
|
|
433
|
+
unsafe {
|
|
434
|
+
nk_dot_f16c(
|
|
435
|
+
a.as_ptr() as *const u16,
|
|
436
|
+
b.as_ptr() as *const u16,
|
|
437
|
+
a.len(),
|
|
438
|
+
result.as_mut_ptr(),
|
|
439
|
+
)
|
|
440
|
+
};
|
|
441
|
+
Some(f32c {
|
|
442
|
+
re: result[0],
|
|
443
|
+
im: result[1],
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
impl Dot for bf16c {
|
|
449
|
+
type Output = f32c;
|
|
450
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
451
|
+
if a.len() != b.len() {
|
|
452
|
+
return None;
|
|
453
|
+
}
|
|
454
|
+
let mut result = [0.0f32; 2];
|
|
455
|
+
unsafe {
|
|
456
|
+
nk_dot_bf16c(
|
|
457
|
+
a.as_ptr() as *const u16,
|
|
458
|
+
b.as_ptr() as *const u16,
|
|
459
|
+
a.len(),
|
|
460
|
+
result.as_mut_ptr(),
|
|
461
|
+
)
|
|
462
|
+
};
|
|
463
|
+
Some(f32c {
|
|
464
|
+
re: result[0],
|
|
465
|
+
im: result[1],
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
impl Dot for f32c {
|
|
471
|
+
type Output = f64c;
|
|
472
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
473
|
+
if a.len() != b.len() {
|
|
474
|
+
return None;
|
|
475
|
+
}
|
|
476
|
+
let mut result = [0.0f64; 2];
|
|
477
|
+
unsafe {
|
|
478
|
+
nk_dot_f32c(
|
|
479
|
+
a.as_ptr() as *const f32,
|
|
480
|
+
b.as_ptr() as *const f32,
|
|
481
|
+
a.len(),
|
|
482
|
+
result.as_mut_ptr(),
|
|
483
|
+
)
|
|
484
|
+
};
|
|
485
|
+
Some(f64c {
|
|
486
|
+
re: result[0],
|
|
487
|
+
im: result[1],
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
impl Dot for f64c {
|
|
493
|
+
type Output = f64c;
|
|
494
|
+
fn dot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
495
|
+
if a.len() != b.len() {
|
|
496
|
+
return None;
|
|
497
|
+
}
|
|
498
|
+
let mut result = [0.0f64; 2];
|
|
499
|
+
unsafe {
|
|
500
|
+
nk_dot_f64c(
|
|
501
|
+
a.as_ptr() as *const f64,
|
|
502
|
+
b.as_ptr() as *const f64,
|
|
503
|
+
a.len(),
|
|
504
|
+
result.as_mut_ptr(),
|
|
505
|
+
)
|
|
506
|
+
};
|
|
507
|
+
Some(f64c {
|
|
508
|
+
re: result[0],
|
|
509
|
+
im: result[1],
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// endregion: Dot
|
|
515
|
+
|
|
516
|
+
// region: Angular
|
|
517
|
+
|
|
518
|
+
/// Computes the **angular distance** (cosine distance) between two vectors.
|
|
519
|
+
///
|
|
520
|
+
/// d = 1 − (a · b) / (‖a‖ × ‖b‖)
|
|
521
|
+
///
|
|
522
|
+
/// Range: \[0, 2\]. Returns `None` if lengths differ.
|
|
523
|
+
///
|
|
524
|
+
/// Implemented for: `f64`, `f32`, `f16`, `bf16`, `i8`, `u8`,
|
|
525
|
+
/// `e4m3`, `e5m2`, `e2m3`, `e3m2`, `i4x2`, `u4x2`.
|
|
526
|
+
pub trait Angular: StorageElement {
|
|
527
|
+
type Output;
|
|
528
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output>;
|
|
529
|
+
|
|
530
|
+
/// Alias for `angular`.
|
|
531
|
+
fn cosine(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
532
|
+
Self::angular(a, b)
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
impl Angular for f64 {
|
|
537
|
+
type Output = f64;
|
|
538
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
539
|
+
if a.len() != b.len() {
|
|
540
|
+
return None;
|
|
541
|
+
}
|
|
542
|
+
let mut result: Self::Output = 0.0;
|
|
543
|
+
unsafe { nk_angular_f64(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
544
|
+
Some(result)
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
impl Angular for f32 {
|
|
549
|
+
type Output = f64;
|
|
550
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
551
|
+
if a.len() != b.len() {
|
|
552
|
+
return None;
|
|
553
|
+
}
|
|
554
|
+
let mut result: Self::Output = 0.0;
|
|
555
|
+
unsafe { nk_angular_f32(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
556
|
+
Some(result)
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
impl Angular for f16 {
|
|
561
|
+
type Output = f32;
|
|
562
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
563
|
+
if a.len() != b.len() {
|
|
564
|
+
return None;
|
|
565
|
+
}
|
|
566
|
+
let mut result: Self::Output = 0.0;
|
|
567
|
+
unsafe {
|
|
568
|
+
nk_angular_f16(
|
|
569
|
+
a.as_ptr() as *const u16,
|
|
570
|
+
b.as_ptr() as *const u16,
|
|
571
|
+
a.len(),
|
|
572
|
+
&mut result,
|
|
573
|
+
)
|
|
574
|
+
};
|
|
575
|
+
Some(result)
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
impl Angular for bf16 {
|
|
580
|
+
type Output = f32;
|
|
581
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
582
|
+
if a.len() != b.len() {
|
|
583
|
+
return None;
|
|
584
|
+
}
|
|
585
|
+
let mut result: Self::Output = 0.0;
|
|
586
|
+
unsafe {
|
|
587
|
+
nk_angular_bf16(
|
|
588
|
+
a.as_ptr() as *const u16,
|
|
589
|
+
b.as_ptr() as *const u16,
|
|
590
|
+
a.len(),
|
|
591
|
+
&mut result,
|
|
592
|
+
)
|
|
593
|
+
};
|
|
594
|
+
Some(result)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
impl Angular for i8 {
|
|
599
|
+
type Output = f32;
|
|
600
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
601
|
+
if a.len() != b.len() {
|
|
602
|
+
return None;
|
|
603
|
+
}
|
|
604
|
+
let mut result: Self::Output = 0.0;
|
|
605
|
+
unsafe { nk_angular_i8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
606
|
+
Some(result)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
impl Angular for u8 {
|
|
611
|
+
type Output = f32;
|
|
612
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
613
|
+
if a.len() != b.len() {
|
|
614
|
+
return None;
|
|
615
|
+
}
|
|
616
|
+
let mut result: Self::Output = 0.0;
|
|
617
|
+
unsafe { nk_angular_u8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
618
|
+
Some(result)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
impl Angular for e4m3 {
|
|
623
|
+
type Output = f32;
|
|
624
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
625
|
+
if a.len() != b.len() {
|
|
626
|
+
return None;
|
|
627
|
+
}
|
|
628
|
+
let mut result: Self::Output = 0.0;
|
|
629
|
+
unsafe {
|
|
630
|
+
nk_angular_e4m3(
|
|
631
|
+
a.as_ptr() as *const u8,
|
|
632
|
+
b.as_ptr() as *const u8,
|
|
633
|
+
a.len(),
|
|
634
|
+
&mut result,
|
|
635
|
+
)
|
|
636
|
+
};
|
|
637
|
+
Some(result)
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
impl Angular for e5m2 {
|
|
642
|
+
type Output = f32;
|
|
643
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
644
|
+
if a.len() != b.len() {
|
|
645
|
+
return None;
|
|
646
|
+
}
|
|
647
|
+
let mut result: Self::Output = 0.0;
|
|
648
|
+
unsafe {
|
|
649
|
+
nk_angular_e5m2(
|
|
650
|
+
a.as_ptr() as *const u8,
|
|
651
|
+
b.as_ptr() as *const u8,
|
|
652
|
+
a.len(),
|
|
653
|
+
&mut result,
|
|
654
|
+
)
|
|
655
|
+
};
|
|
656
|
+
Some(result)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
impl Angular for e2m3 {
|
|
661
|
+
type Output = f32;
|
|
662
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
663
|
+
if a.len() != b.len() {
|
|
664
|
+
return None;
|
|
665
|
+
}
|
|
666
|
+
let mut result: Self::Output = 0.0;
|
|
667
|
+
unsafe {
|
|
668
|
+
nk_angular_e2m3(
|
|
669
|
+
a.as_ptr() as *const u8,
|
|
670
|
+
b.as_ptr() as *const u8,
|
|
671
|
+
a.len(),
|
|
672
|
+
&mut result,
|
|
673
|
+
)
|
|
674
|
+
};
|
|
675
|
+
Some(result)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
impl Angular for e3m2 {
|
|
680
|
+
type Output = f32;
|
|
681
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
682
|
+
if a.len() != b.len() {
|
|
683
|
+
return None;
|
|
684
|
+
}
|
|
685
|
+
let mut result: Self::Output = 0.0;
|
|
686
|
+
unsafe {
|
|
687
|
+
nk_angular_e3m2(
|
|
688
|
+
a.as_ptr() as *const u8,
|
|
689
|
+
b.as_ptr() as *const u8,
|
|
690
|
+
a.len(),
|
|
691
|
+
&mut result,
|
|
692
|
+
)
|
|
693
|
+
};
|
|
694
|
+
Some(result)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
impl Angular for i4x2 {
|
|
699
|
+
type Output = f32;
|
|
700
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
701
|
+
if a.len() != b.len() {
|
|
702
|
+
return None;
|
|
703
|
+
}
|
|
704
|
+
let mut result: Self::Output = 0.0;
|
|
705
|
+
let element_count = a.len() * 2; // Each i4x2 contains 2 elements
|
|
706
|
+
unsafe {
|
|
707
|
+
nk_angular_i4(
|
|
708
|
+
a.as_ptr() as *const u8,
|
|
709
|
+
b.as_ptr() as *const u8,
|
|
710
|
+
element_count,
|
|
711
|
+
&mut result,
|
|
712
|
+
)
|
|
713
|
+
};
|
|
714
|
+
Some(result)
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
impl Angular for u4x2 {
|
|
719
|
+
type Output = f32;
|
|
720
|
+
fn angular(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
721
|
+
if a.len() != b.len() {
|
|
722
|
+
return None;
|
|
723
|
+
}
|
|
724
|
+
let mut result: Self::Output = 0.0;
|
|
725
|
+
let element_count = a.len() * 2; // Each u4x2 contains 2 elements
|
|
726
|
+
unsafe {
|
|
727
|
+
nk_angular_u4(
|
|
728
|
+
a.as_ptr() as *const u8,
|
|
729
|
+
b.as_ptr() as *const u8,
|
|
730
|
+
element_count,
|
|
731
|
+
&mut result,
|
|
732
|
+
)
|
|
733
|
+
};
|
|
734
|
+
Some(result)
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// endregion: Angular
|
|
739
|
+
|
|
740
|
+
// region: Euclidean
|
|
741
|
+
|
|
742
|
+
/// Computes the **Euclidean distance** (L2) between two vectors.
|
|
743
|
+
///
|
|
744
|
+
/// d = √(∑ᵢ (aᵢ − bᵢ)²)
|
|
745
|
+
///
|
|
746
|
+
/// Range: \[0, ∞). Returns `None` if lengths differ.
|
|
747
|
+
///
|
|
748
|
+
/// Implemented for: `f64`, `f32`, `f16`, `bf16`, `i8`, `u8`,
|
|
749
|
+
/// `e4m3`, `e5m2`, `e2m3`, `e3m2`, `i4x2`, `u4x2`.
|
|
750
|
+
pub trait Euclidean: StorageElement {
|
|
751
|
+
type SqEuclideanOutput;
|
|
752
|
+
type EuclideanOutput;
|
|
753
|
+
|
|
754
|
+
/// Squared Euclidean distance (L2²). Faster than `euclidean` for comparisons.
|
|
755
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput>;
|
|
756
|
+
|
|
757
|
+
/// Euclidean distance (L2). True metric distance.
|
|
758
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput>;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
impl Euclidean for f64 {
|
|
762
|
+
type SqEuclideanOutput = f64;
|
|
763
|
+
type EuclideanOutput = f64;
|
|
764
|
+
|
|
765
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
766
|
+
if a.len() != b.len() {
|
|
767
|
+
return None;
|
|
768
|
+
}
|
|
769
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
770
|
+
unsafe { nk_sqeuclidean_f64(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
771
|
+
Some(result)
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
775
|
+
if a.len() != b.len() {
|
|
776
|
+
return None;
|
|
777
|
+
}
|
|
778
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
779
|
+
unsafe { nk_euclidean_f64(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
780
|
+
Some(result)
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
impl Euclidean for f32 {
|
|
785
|
+
type SqEuclideanOutput = f64;
|
|
786
|
+
type EuclideanOutput = f64;
|
|
787
|
+
|
|
788
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
789
|
+
if a.len() != b.len() {
|
|
790
|
+
return None;
|
|
791
|
+
}
|
|
792
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
793
|
+
unsafe { nk_sqeuclidean_f32(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
794
|
+
Some(result)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
798
|
+
if a.len() != b.len() {
|
|
799
|
+
return None;
|
|
800
|
+
}
|
|
801
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
802
|
+
unsafe { nk_euclidean_f32(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
803
|
+
Some(result)
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
impl Euclidean for f16 {
|
|
808
|
+
type SqEuclideanOutput = f32;
|
|
809
|
+
type EuclideanOutput = f32;
|
|
810
|
+
|
|
811
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
812
|
+
if a.len() != b.len() {
|
|
813
|
+
return None;
|
|
814
|
+
}
|
|
815
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
816
|
+
unsafe {
|
|
817
|
+
nk_sqeuclidean_f16(
|
|
818
|
+
a.as_ptr() as *const u16,
|
|
819
|
+
b.as_ptr() as *const u16,
|
|
820
|
+
a.len(),
|
|
821
|
+
&mut result,
|
|
822
|
+
)
|
|
823
|
+
};
|
|
824
|
+
Some(result)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
828
|
+
if a.len() != b.len() {
|
|
829
|
+
return None;
|
|
830
|
+
}
|
|
831
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
832
|
+
unsafe {
|
|
833
|
+
nk_euclidean_f16(
|
|
834
|
+
a.as_ptr() as *const u16,
|
|
835
|
+
b.as_ptr() as *const u16,
|
|
836
|
+
a.len(),
|
|
837
|
+
&mut result,
|
|
838
|
+
)
|
|
839
|
+
};
|
|
840
|
+
Some(result)
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
impl Euclidean for bf16 {
|
|
845
|
+
type SqEuclideanOutput = f32;
|
|
846
|
+
type EuclideanOutput = f32;
|
|
847
|
+
|
|
848
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
849
|
+
if a.len() != b.len() {
|
|
850
|
+
return None;
|
|
851
|
+
}
|
|
852
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
853
|
+
unsafe {
|
|
854
|
+
nk_sqeuclidean_bf16(
|
|
855
|
+
a.as_ptr() as *const u16,
|
|
856
|
+
b.as_ptr() as *const u16,
|
|
857
|
+
a.len(),
|
|
858
|
+
&mut result,
|
|
859
|
+
)
|
|
860
|
+
};
|
|
861
|
+
Some(result)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
865
|
+
if a.len() != b.len() {
|
|
866
|
+
return None;
|
|
867
|
+
}
|
|
868
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
869
|
+
unsafe {
|
|
870
|
+
nk_euclidean_bf16(
|
|
871
|
+
a.as_ptr() as *const u16,
|
|
872
|
+
b.as_ptr() as *const u16,
|
|
873
|
+
a.len(),
|
|
874
|
+
&mut result,
|
|
875
|
+
)
|
|
876
|
+
};
|
|
877
|
+
Some(result)
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
impl Euclidean for i8 {
|
|
882
|
+
type SqEuclideanOutput = u32;
|
|
883
|
+
type EuclideanOutput = f32;
|
|
884
|
+
|
|
885
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
886
|
+
if a.len() != b.len() {
|
|
887
|
+
return None;
|
|
888
|
+
}
|
|
889
|
+
let mut result: Self::SqEuclideanOutput = 0;
|
|
890
|
+
unsafe { nk_sqeuclidean_i8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
891
|
+
Some(result)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
895
|
+
if a.len() != b.len() {
|
|
896
|
+
return None;
|
|
897
|
+
}
|
|
898
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
899
|
+
unsafe { nk_euclidean_i8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
900
|
+
Some(result)
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
impl Euclidean for u8 {
|
|
905
|
+
type SqEuclideanOutput = u32;
|
|
906
|
+
type EuclideanOutput = f32;
|
|
907
|
+
|
|
908
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
909
|
+
if a.len() != b.len() {
|
|
910
|
+
return None;
|
|
911
|
+
}
|
|
912
|
+
let mut result: Self::SqEuclideanOutput = 0;
|
|
913
|
+
unsafe { nk_sqeuclidean_u8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
914
|
+
Some(result)
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
918
|
+
if a.len() != b.len() {
|
|
919
|
+
return None;
|
|
920
|
+
}
|
|
921
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
922
|
+
unsafe { nk_euclidean_u8(a.as_ptr(), b.as_ptr(), a.len(), &mut result) };
|
|
923
|
+
Some(result)
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
impl Euclidean for e4m3 {
|
|
928
|
+
type SqEuclideanOutput = f32;
|
|
929
|
+
type EuclideanOutput = f32;
|
|
930
|
+
|
|
931
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
932
|
+
if a.len() != b.len() {
|
|
933
|
+
return None;
|
|
934
|
+
}
|
|
935
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
936
|
+
unsafe {
|
|
937
|
+
nk_sqeuclidean_e4m3(
|
|
938
|
+
a.as_ptr() as *const u8,
|
|
939
|
+
b.as_ptr() as *const u8,
|
|
940
|
+
a.len(),
|
|
941
|
+
&mut result,
|
|
942
|
+
)
|
|
943
|
+
};
|
|
944
|
+
Some(result)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
948
|
+
if a.len() != b.len() {
|
|
949
|
+
return None;
|
|
950
|
+
}
|
|
951
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
952
|
+
unsafe {
|
|
953
|
+
nk_euclidean_e4m3(
|
|
954
|
+
a.as_ptr() as *const u8,
|
|
955
|
+
b.as_ptr() as *const u8,
|
|
956
|
+
a.len(),
|
|
957
|
+
&mut result,
|
|
958
|
+
)
|
|
959
|
+
};
|
|
960
|
+
Some(result)
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
impl Euclidean for e5m2 {
|
|
965
|
+
type SqEuclideanOutput = f32;
|
|
966
|
+
type EuclideanOutput = f32;
|
|
967
|
+
|
|
968
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
969
|
+
if a.len() != b.len() {
|
|
970
|
+
return None;
|
|
971
|
+
}
|
|
972
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
973
|
+
unsafe {
|
|
974
|
+
nk_sqeuclidean_e5m2(
|
|
975
|
+
a.as_ptr() as *const u8,
|
|
976
|
+
b.as_ptr() as *const u8,
|
|
977
|
+
a.len(),
|
|
978
|
+
&mut result,
|
|
979
|
+
)
|
|
980
|
+
};
|
|
981
|
+
Some(result)
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
985
|
+
if a.len() != b.len() {
|
|
986
|
+
return None;
|
|
987
|
+
}
|
|
988
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
989
|
+
unsafe {
|
|
990
|
+
nk_euclidean_e5m2(
|
|
991
|
+
a.as_ptr() as *const u8,
|
|
992
|
+
b.as_ptr() as *const u8,
|
|
993
|
+
a.len(),
|
|
994
|
+
&mut result,
|
|
995
|
+
)
|
|
996
|
+
};
|
|
997
|
+
Some(result)
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
impl Euclidean for e2m3 {
|
|
1002
|
+
type SqEuclideanOutput = f32;
|
|
1003
|
+
type EuclideanOutput = f32;
|
|
1004
|
+
|
|
1005
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
1006
|
+
if a.len() != b.len() {
|
|
1007
|
+
return None;
|
|
1008
|
+
}
|
|
1009
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
1010
|
+
unsafe {
|
|
1011
|
+
nk_sqeuclidean_e2m3(
|
|
1012
|
+
a.as_ptr() as *const u8,
|
|
1013
|
+
b.as_ptr() as *const u8,
|
|
1014
|
+
a.len(),
|
|
1015
|
+
&mut result,
|
|
1016
|
+
)
|
|
1017
|
+
};
|
|
1018
|
+
Some(result)
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
1022
|
+
if a.len() != b.len() {
|
|
1023
|
+
return None;
|
|
1024
|
+
}
|
|
1025
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
1026
|
+
unsafe {
|
|
1027
|
+
nk_euclidean_e2m3(
|
|
1028
|
+
a.as_ptr() as *const u8,
|
|
1029
|
+
b.as_ptr() as *const u8,
|
|
1030
|
+
a.len(),
|
|
1031
|
+
&mut result,
|
|
1032
|
+
)
|
|
1033
|
+
};
|
|
1034
|
+
Some(result)
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
impl Euclidean for e3m2 {
|
|
1039
|
+
type SqEuclideanOutput = f32;
|
|
1040
|
+
type EuclideanOutput = f32;
|
|
1041
|
+
|
|
1042
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
1043
|
+
if a.len() != b.len() {
|
|
1044
|
+
return None;
|
|
1045
|
+
}
|
|
1046
|
+
let mut result: Self::SqEuclideanOutput = 0.0;
|
|
1047
|
+
unsafe {
|
|
1048
|
+
nk_sqeuclidean_e3m2(
|
|
1049
|
+
a.as_ptr() as *const u8,
|
|
1050
|
+
b.as_ptr() as *const u8,
|
|
1051
|
+
a.len(),
|
|
1052
|
+
&mut result,
|
|
1053
|
+
)
|
|
1054
|
+
};
|
|
1055
|
+
Some(result)
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
1059
|
+
if a.len() != b.len() {
|
|
1060
|
+
return None;
|
|
1061
|
+
}
|
|
1062
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
1063
|
+
unsafe {
|
|
1064
|
+
nk_euclidean_e3m2(
|
|
1065
|
+
a.as_ptr() as *const u8,
|
|
1066
|
+
b.as_ptr() as *const u8,
|
|
1067
|
+
a.len(),
|
|
1068
|
+
&mut result,
|
|
1069
|
+
)
|
|
1070
|
+
};
|
|
1071
|
+
Some(result)
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
impl Euclidean for i4x2 {
|
|
1076
|
+
type SqEuclideanOutput = u32;
|
|
1077
|
+
type EuclideanOutput = f32;
|
|
1078
|
+
|
|
1079
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
1080
|
+
if a.len() != b.len() {
|
|
1081
|
+
return None;
|
|
1082
|
+
}
|
|
1083
|
+
let mut result: Self::SqEuclideanOutput = 0;
|
|
1084
|
+
let element_count = a.len() * 2; // Each i4x2 contains 2 elements
|
|
1085
|
+
unsafe {
|
|
1086
|
+
nk_sqeuclidean_i4(
|
|
1087
|
+
a.as_ptr() as *const u8,
|
|
1088
|
+
b.as_ptr() as *const u8,
|
|
1089
|
+
element_count,
|
|
1090
|
+
&mut result,
|
|
1091
|
+
)
|
|
1092
|
+
};
|
|
1093
|
+
Some(result)
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
1097
|
+
if a.len() != b.len() {
|
|
1098
|
+
return None;
|
|
1099
|
+
}
|
|
1100
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
1101
|
+
let element_count = a.len() * 2; // Each i4x2 contains 2 elements
|
|
1102
|
+
unsafe {
|
|
1103
|
+
nk_euclidean_i4(
|
|
1104
|
+
a.as_ptr() as *const u8,
|
|
1105
|
+
b.as_ptr() as *const u8,
|
|
1106
|
+
element_count,
|
|
1107
|
+
&mut result,
|
|
1108
|
+
)
|
|
1109
|
+
};
|
|
1110
|
+
Some(result)
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
impl Euclidean for u4x2 {
|
|
1115
|
+
type SqEuclideanOutput = u32;
|
|
1116
|
+
type EuclideanOutput = f32;
|
|
1117
|
+
|
|
1118
|
+
fn sqeuclidean(a: &[Self], b: &[Self]) -> Option<Self::SqEuclideanOutput> {
|
|
1119
|
+
if a.len() != b.len() {
|
|
1120
|
+
return None;
|
|
1121
|
+
}
|
|
1122
|
+
let mut result: Self::SqEuclideanOutput = 0;
|
|
1123
|
+
let element_count = a.len() * 2; // Each u4x2 contains 2 elements
|
|
1124
|
+
unsafe {
|
|
1125
|
+
nk_sqeuclidean_u4(
|
|
1126
|
+
a.as_ptr() as *const u8,
|
|
1127
|
+
b.as_ptr() as *const u8,
|
|
1128
|
+
element_count,
|
|
1129
|
+
&mut result,
|
|
1130
|
+
)
|
|
1131
|
+
};
|
|
1132
|
+
Some(result)
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
fn euclidean(a: &[Self], b: &[Self]) -> Option<Self::EuclideanOutput> {
|
|
1136
|
+
if a.len() != b.len() {
|
|
1137
|
+
return None;
|
|
1138
|
+
}
|
|
1139
|
+
let mut result: Self::EuclideanOutput = 0.0;
|
|
1140
|
+
let element_count = a.len() * 2; // Each u4x2 contains 2 elements
|
|
1141
|
+
unsafe {
|
|
1142
|
+
nk_euclidean_u4(
|
|
1143
|
+
a.as_ptr() as *const u8,
|
|
1144
|
+
b.as_ptr() as *const u8,
|
|
1145
|
+
element_count,
|
|
1146
|
+
&mut result,
|
|
1147
|
+
)
|
|
1148
|
+
};
|
|
1149
|
+
Some(result)
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// endregion: Euclidean
|
|
1154
|
+
|
|
1155
|
+
// region: VDot
|
|
1156
|
+
|
|
1157
|
+
/// Computes the conjugating (Hermitian) dot product.
|
|
1158
|
+
///
|
|
1159
|
+
/// For real-valued types this is identical to [`Dot::dot`]. For complex-valued
|
|
1160
|
+
/// types it computes `∑ᵢ conj(aᵢ) × bᵢ`, which is the convention used in quantum
|
|
1161
|
+
/// mechanics and most linear-algebra textbooks (BLAS `cdotc`, NumPy `vdot`).
|
|
1162
|
+
///
|
|
1163
|
+
/// Unlike an ordinary complex dot product, the Hermitian form produces a proper
|
|
1164
|
+
/// inner product: `vdot(x, x)` is always a non-negative real number equal to
|
|
1165
|
+
/// `‖x‖²`, and `vdot(x, y) == conj(vdot(y, x))`. This matters in retrieval and
|
|
1166
|
+
/// signal-processing pipelines where cancellation between a vector and its
|
|
1167
|
+
/// conjugate should leave the norm intact.
|
|
1168
|
+
pub trait VDot: Dot {
|
|
1169
|
+
/// Hermitian inner product. On real-valued types this falls back to `Dot::dot`;
|
|
1170
|
+
/// on complex types it returns `∑ᵢ conj(aᵢ) × bᵢ` computed in the widened
|
|
1171
|
+
/// accumulator described by `Dot::Output`.
|
|
1172
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
1173
|
+
Self::dot(a, b)
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
impl VDot for f64 {}
|
|
1178
|
+
impl VDot for f32 {}
|
|
1179
|
+
impl VDot for f16 {}
|
|
1180
|
+
impl VDot for bf16 {}
|
|
1181
|
+
impl VDot for i8 {}
|
|
1182
|
+
impl VDot for u8 {}
|
|
1183
|
+
impl VDot for e4m3 {}
|
|
1184
|
+
impl VDot for e5m2 {}
|
|
1185
|
+
impl VDot for e2m3 {}
|
|
1186
|
+
impl VDot for e3m2 {}
|
|
1187
|
+
impl VDot for i4x2 {}
|
|
1188
|
+
impl VDot for u4x2 {}
|
|
1189
|
+
|
|
1190
|
+
impl VDot for f16c {
|
|
1191
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
1192
|
+
if a.len() != b.len() {
|
|
1193
|
+
return None;
|
|
1194
|
+
}
|
|
1195
|
+
let mut result = [0.0f32; 2];
|
|
1196
|
+
unsafe {
|
|
1197
|
+
nk_vdot_f16c(
|
|
1198
|
+
a.as_ptr() as *const u16,
|
|
1199
|
+
b.as_ptr() as *const u16,
|
|
1200
|
+
a.len(),
|
|
1201
|
+
result.as_mut_ptr(),
|
|
1202
|
+
)
|
|
1203
|
+
};
|
|
1204
|
+
Some(f32c {
|
|
1205
|
+
re: result[0],
|
|
1206
|
+
im: result[1],
|
|
1207
|
+
})
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
impl VDot for bf16c {
|
|
1212
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
1213
|
+
if a.len() != b.len() {
|
|
1214
|
+
return None;
|
|
1215
|
+
}
|
|
1216
|
+
let mut result = [0.0f32; 2];
|
|
1217
|
+
unsafe {
|
|
1218
|
+
nk_vdot_bf16c(
|
|
1219
|
+
a.as_ptr() as *const u16,
|
|
1220
|
+
b.as_ptr() as *const u16,
|
|
1221
|
+
a.len(),
|
|
1222
|
+
result.as_mut_ptr(),
|
|
1223
|
+
)
|
|
1224
|
+
};
|
|
1225
|
+
Some(f32c {
|
|
1226
|
+
re: result[0],
|
|
1227
|
+
im: result[1],
|
|
1228
|
+
})
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
impl VDot for f32c {
|
|
1233
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
1234
|
+
if a.len() != b.len() {
|
|
1235
|
+
return None;
|
|
1236
|
+
}
|
|
1237
|
+
let mut result = [0.0f64; 2];
|
|
1238
|
+
unsafe {
|
|
1239
|
+
nk_vdot_f32c(
|
|
1240
|
+
a.as_ptr() as *const f32,
|
|
1241
|
+
b.as_ptr() as *const f32,
|
|
1242
|
+
a.len(),
|
|
1243
|
+
result.as_mut_ptr(),
|
|
1244
|
+
)
|
|
1245
|
+
};
|
|
1246
|
+
Some(f64c {
|
|
1247
|
+
re: result[0],
|
|
1248
|
+
im: result[1],
|
|
1249
|
+
})
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
impl VDot for f64c {
|
|
1254
|
+
fn vdot(a: &[Self], b: &[Self]) -> Option<Self::Output> {
|
|
1255
|
+
if a.len() != b.len() {
|
|
1256
|
+
return None;
|
|
1257
|
+
}
|
|
1258
|
+
let mut result = [0.0f64; 2];
|
|
1259
|
+
unsafe {
|
|
1260
|
+
nk_vdot_f64c(
|
|
1261
|
+
a.as_ptr() as *const f64,
|
|
1262
|
+
b.as_ptr() as *const f64,
|
|
1263
|
+
a.len(),
|
|
1264
|
+
result.as_mut_ptr(),
|
|
1265
|
+
)
|
|
1266
|
+
};
|
|
1267
|
+
Some(f64c {
|
|
1268
|
+
re: result[0],
|
|
1269
|
+
im: result[1],
|
|
1270
|
+
})
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// endregion: VDot
|
|
1275
|
+
|
|
1276
|
+
/// `SpatialSimilarity` bundles spatial distance metrics: Dot, Angular, and Euclidean.
|
|
1277
|
+
pub trait SpatialSimilarity: Dot + Angular + Euclidean {}
|
|
1278
|
+
impl<Scalar: Dot + Angular + Euclidean> SpatialSimilarity for Scalar {}
|
|
1279
|
+
|
|
1280
|
+
#[cfg(test)]
|
|
1281
|
+
mod tests {
|
|
1282
|
+
use super::*;
|
|
1283
|
+
use crate::curved::Bilinear;
|
|
1284
|
+
use crate::types::{
|
|
1285
|
+
assert_close, bf16, bf16c, e2m3, e3m2, e4m3, e5m2, f16, f16c, f32c, f64c, i4x2, u4x2,
|
|
1286
|
+
FloatLike, NumberLike, StorageElement, TestableType,
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
/// Test a two-input metric: convert f32 inputs to Scalar, call `op`, compare to `expected`.
|
|
1290
|
+
pub(crate) fn check_binary<Scalar, R, F>(
|
|
1291
|
+
a_vals: &[f32],
|
|
1292
|
+
b_vals: &[f32],
|
|
1293
|
+
op: F,
|
|
1294
|
+
expected: f64,
|
|
1295
|
+
label: &str,
|
|
1296
|
+
) where
|
|
1297
|
+
Scalar: FloatLike + TestableType,
|
|
1298
|
+
R: FloatLike,
|
|
1299
|
+
F: FnOnce(&[Scalar], &[Scalar]) -> Option<R>,
|
|
1300
|
+
{
|
|
1301
|
+
let a: Vec<Scalar> = a_vals.iter().map(|&v| Scalar::from_f32(v)).collect();
|
|
1302
|
+
let b: Vec<Scalar> = b_vals.iter().map(|&v| Scalar::from_f32(v)).collect();
|
|
1303
|
+
let result = op(&a, &b).unwrap().to_f64();
|
|
1304
|
+
assert_close(
|
|
1305
|
+
result,
|
|
1306
|
+
expected,
|
|
1307
|
+
Scalar::atol(),
|
|
1308
|
+
Scalar::rtol(),
|
|
1309
|
+
&format!("{}<{}>", label, core::any::type_name::<Scalar>()),
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// region: Dot Products
|
|
1314
|
+
|
|
1315
|
+
fn check_dot<Scalar>(a_vals: &[f32], b_vals: &[f32], expected: f64)
|
|
1316
|
+
where
|
|
1317
|
+
Scalar: FloatLike + TestableType + Dot,
|
|
1318
|
+
Scalar::Output: FloatLike,
|
|
1319
|
+
{
|
|
1320
|
+
check_binary::<Scalar, Scalar::Output, _>(a_vals, b_vals, Scalar::dot, expected, "dot");
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
#[test]
|
|
1324
|
+
fn dot() {
|
|
1325
|
+
check_dot::<f32>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1326
|
+
check_dot::<f64>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1327
|
+
check_dot::<f16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1328
|
+
check_dot::<bf16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1329
|
+
check_dot::<i8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1330
|
+
check_dot::<u8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1331
|
+
check_dot::<e4m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1332
|
+
check_dot::<e5m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1333
|
+
check_dot::<e2m3>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], 6.0);
|
|
1334
|
+
check_dot::<e3m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 32.0);
|
|
1335
|
+
check_dot::<i4x2>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], 12.0);
|
|
1336
|
+
check_dot::<u4x2>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], 12.0);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// endregion
|
|
1340
|
+
|
|
1341
|
+
// region: Angular Distances
|
|
1342
|
+
|
|
1343
|
+
fn check_angular<Scalar>(a_vals: &[f32], b_vals: &[f32], expected: f64)
|
|
1344
|
+
where
|
|
1345
|
+
Scalar: FloatLike + TestableType + Angular,
|
|
1346
|
+
Scalar::Output: FloatLike,
|
|
1347
|
+
{
|
|
1348
|
+
check_binary::<Scalar, Scalar::Output, _>(
|
|
1349
|
+
a_vals,
|
|
1350
|
+
b_vals,
|
|
1351
|
+
Scalar::angular,
|
|
1352
|
+
expected,
|
|
1353
|
+
"angular",
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
#[test]
|
|
1358
|
+
fn angular() {
|
|
1359
|
+
// angular([1,2,3],[4,5,6]) = 1 - 32/sqrt(14*77) ≈ 0.025368
|
|
1360
|
+
let expected = 1.0 - 32.0 / (14.0_f64.sqrt() * 77.0_f64.sqrt());
|
|
1361
|
+
check_angular::<f32>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1362
|
+
check_angular::<f64>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1363
|
+
check_angular::<f16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1364
|
+
check_angular::<bf16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1365
|
+
check_angular::<i8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1366
|
+
check_angular::<u8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1367
|
+
check_angular::<e4m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1368
|
+
check_angular::<e5m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1369
|
+
// e2m3 max is 7.5, use values in range
|
|
1370
|
+
let expected_e2m3 = 1.0 - 6.0 / (14.0_f64.sqrt() * 3.0_f64.sqrt());
|
|
1371
|
+
check_angular::<e2m3>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], expected_e2m3);
|
|
1372
|
+
check_angular::<e3m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1373
|
+
check_angular::<i4x2>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], expected_e2m3);
|
|
1374
|
+
check_angular::<u4x2>(&[1.0, 2.0, 3.0], &[1.0, 1.0, 1.0], expected_e2m3);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// endregion
|
|
1378
|
+
|
|
1379
|
+
// region: Euclidean Distances
|
|
1380
|
+
|
|
1381
|
+
fn check_sqeuclidean<Scalar>(a_vals: &[f32], b_vals: &[f32], expected: f64)
|
|
1382
|
+
where
|
|
1383
|
+
Scalar: FloatLike + TestableType + Euclidean,
|
|
1384
|
+
Scalar::SqEuclideanOutput: FloatLike,
|
|
1385
|
+
{
|
|
1386
|
+
check_binary::<Scalar, Scalar::SqEuclideanOutput, _>(
|
|
1387
|
+
a_vals,
|
|
1388
|
+
b_vals,
|
|
1389
|
+
Scalar::sqeuclidean,
|
|
1390
|
+
expected,
|
|
1391
|
+
"sqeuclidean",
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
fn check_euclidean<Scalar>(a_vals: &[f32], b_vals: &[f32], expected: f64)
|
|
1396
|
+
where
|
|
1397
|
+
Scalar: FloatLike + TestableType + Euclidean,
|
|
1398
|
+
Scalar::EuclideanOutput: FloatLike,
|
|
1399
|
+
{
|
|
1400
|
+
check_binary::<Scalar, Scalar::EuclideanOutput, _>(
|
|
1401
|
+
a_vals,
|
|
1402
|
+
b_vals,
|
|
1403
|
+
Scalar::euclidean,
|
|
1404
|
+
expected,
|
|
1405
|
+
"euclidean",
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
#[test]
|
|
1410
|
+
fn sqeuclidean() {
|
|
1411
|
+
check_sqeuclidean::<f32>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1412
|
+
check_sqeuclidean::<f64>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1413
|
+
check_sqeuclidean::<f16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1414
|
+
check_sqeuclidean::<bf16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1415
|
+
check_sqeuclidean::<i8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1416
|
+
check_sqeuclidean::<u8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1417
|
+
check_sqeuclidean::<e4m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1418
|
+
check_sqeuclidean::<e5m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1419
|
+
check_sqeuclidean::<e2m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1420
|
+
check_sqeuclidean::<e3m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], 27.0);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
#[test]
|
|
1424
|
+
fn euclidean() {
|
|
1425
|
+
let expected = 27.0_f64.sqrt();
|
|
1426
|
+
check_euclidean::<f32>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1427
|
+
check_euclidean::<f64>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1428
|
+
check_euclidean::<f16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1429
|
+
check_euclidean::<bf16>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1430
|
+
check_euclidean::<i8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1431
|
+
check_euclidean::<u8>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1432
|
+
check_euclidean::<e4m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1433
|
+
check_euclidean::<e5m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1434
|
+
check_euclidean::<e2m3>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1435
|
+
check_euclidean::<e3m2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected);
|
|
1436
|
+
let expected_packed = 54.0_f64.sqrt(); // i4x2 duplicates each value into both nibbles
|
|
1437
|
+
check_euclidean::<i4x2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected_packed);
|
|
1438
|
+
check_euclidean::<u4x2>(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], expected_packed);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// endregion
|
|
1442
|
+
|
|
1443
|
+
// region: Complex Products
|
|
1444
|
+
|
|
1445
|
+
trait ComplexValue {
|
|
1446
|
+
fn real(&self) -> f64;
|
|
1447
|
+
fn imag(&self) -> f64;
|
|
1448
|
+
}
|
|
1449
|
+
impl ComplexValue for f32c {
|
|
1450
|
+
fn real(&self) -> f64 {
|
|
1451
|
+
self.re as f64
|
|
1452
|
+
}
|
|
1453
|
+
fn imag(&self) -> f64 {
|
|
1454
|
+
self.im as f64
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
impl ComplexValue for f64c {
|
|
1458
|
+
fn real(&self) -> f64 {
|
|
1459
|
+
self.re
|
|
1460
|
+
}
|
|
1461
|
+
fn imag(&self) -> f64 {
|
|
1462
|
+
self.im
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
trait ComplexSample: Copy + StorageElement + Dot + VDot + Bilinear {
|
|
1467
|
+
fn from_real_imag(re: f32, im: f32) -> Self;
|
|
1468
|
+
fn atol() -> f64;
|
|
1469
|
+
fn rtol() -> f64;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
impl ComplexSample for f16c {
|
|
1473
|
+
fn from_real_imag(re: f32, im: f32) -> Self {
|
|
1474
|
+
Self {
|
|
1475
|
+
re: f16::from_f32(re),
|
|
1476
|
+
im: f16::from_f32(im),
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
fn atol() -> f64 {
|
|
1480
|
+
5e-2
|
|
1481
|
+
}
|
|
1482
|
+
fn rtol() -> f64 {
|
|
1483
|
+
5e-2
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
impl ComplexSample for bf16c {
|
|
1488
|
+
fn from_real_imag(re: f32, im: f32) -> Self {
|
|
1489
|
+
Self {
|
|
1490
|
+
re: bf16::from_f32(re),
|
|
1491
|
+
im: bf16::from_f32(im),
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
fn atol() -> f64 {
|
|
1495
|
+
5e-2
|
|
1496
|
+
}
|
|
1497
|
+
fn rtol() -> f64 {
|
|
1498
|
+
5e-2
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
impl ComplexSample for f32c {
|
|
1503
|
+
fn from_real_imag(re: f32, im: f32) -> Self {
|
|
1504
|
+
Self { re, im }
|
|
1505
|
+
}
|
|
1506
|
+
fn atol() -> f64 {
|
|
1507
|
+
1e-6
|
|
1508
|
+
}
|
|
1509
|
+
fn rtol() -> f64 {
|
|
1510
|
+
1e-6
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
impl ComplexSample for f64c {
|
|
1515
|
+
fn from_real_imag(re: f32, im: f32) -> Self {
|
|
1516
|
+
Self {
|
|
1517
|
+
re: re as f64,
|
|
1518
|
+
im: im as f64,
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
fn atol() -> f64 {
|
|
1522
|
+
1e-12
|
|
1523
|
+
}
|
|
1524
|
+
fn rtol() -> f64 {
|
|
1525
|
+
1e-12
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/// Test a complex two-input operation with real + imaginary expected outputs.
|
|
1530
|
+
fn check_complex<Scalar, R, F>(
|
|
1531
|
+
a: &[(f32, f32)],
|
|
1532
|
+
b: &[(f32, f32)],
|
|
1533
|
+
op: F,
|
|
1534
|
+
expected_re: f64,
|
|
1535
|
+
expected_im: f64,
|
|
1536
|
+
label: &str,
|
|
1537
|
+
) where
|
|
1538
|
+
Scalar: ComplexSample,
|
|
1539
|
+
R: ComplexValue,
|
|
1540
|
+
F: FnOnce(&[Scalar], &[Scalar]) -> Option<R>,
|
|
1541
|
+
{
|
|
1542
|
+
let a_t: Vec<Scalar> = a
|
|
1543
|
+
.iter()
|
|
1544
|
+
.map(|&(re, im)| Scalar::from_real_imag(re, im))
|
|
1545
|
+
.collect();
|
|
1546
|
+
let b_t: Vec<Scalar> = b
|
|
1547
|
+
.iter()
|
|
1548
|
+
.map(|&(re, im)| Scalar::from_real_imag(re, im))
|
|
1549
|
+
.collect();
|
|
1550
|
+
let result = op(&a_t, &b_t).unwrap();
|
|
1551
|
+
let tol = Scalar::atol() + Scalar::rtol() * expected_re.abs().max(expected_im.abs());
|
|
1552
|
+
assert_close(
|
|
1553
|
+
result.real(),
|
|
1554
|
+
expected_re,
|
|
1555
|
+
tol,
|
|
1556
|
+
0.0,
|
|
1557
|
+
&format!("{}<{}> real", label, core::any::type_name::<Scalar>()),
|
|
1558
|
+
);
|
|
1559
|
+
assert_close(
|
|
1560
|
+
result.imag(),
|
|
1561
|
+
expected_im,
|
|
1562
|
+
tol,
|
|
1563
|
+
0.0,
|
|
1564
|
+
&format!("{}<{}> imag", label, core::any::type_name::<Scalar>()),
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
fn check_complex_dot<Scalar>(a: &[f32], b: &[f32], expected_re: f64, expected_im: f64)
|
|
1569
|
+
where
|
|
1570
|
+
Scalar: ComplexSample,
|
|
1571
|
+
<Scalar as Dot>::Output: ComplexValue,
|
|
1572
|
+
{
|
|
1573
|
+
let a_pairs: Vec<(f32, f32)> = a
|
|
1574
|
+
.chunks_exact(2)
|
|
1575
|
+
.map(|chunk| (chunk[0], chunk[1]))
|
|
1576
|
+
.collect();
|
|
1577
|
+
let b_pairs: Vec<(f32, f32)> = b
|
|
1578
|
+
.chunks_exact(2)
|
|
1579
|
+
.map(|chunk| (chunk[0], chunk[1]))
|
|
1580
|
+
.collect();
|
|
1581
|
+
check_complex::<Scalar, <Scalar as Dot>::Output, _>(
|
|
1582
|
+
&a_pairs,
|
|
1583
|
+
&b_pairs,
|
|
1584
|
+
<Scalar as Dot>::dot,
|
|
1585
|
+
expected_re,
|
|
1586
|
+
expected_im,
|
|
1587
|
+
"complex_dot",
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
fn check_complex_vdot<Scalar>(a: &[f32], b: &[f32], expected_re: f64, expected_im: f64)
|
|
1592
|
+
where
|
|
1593
|
+
Scalar: ComplexSample,
|
|
1594
|
+
<Scalar as Dot>::Output: ComplexValue,
|
|
1595
|
+
{
|
|
1596
|
+
let a_pairs: Vec<(f32, f32)> = a
|
|
1597
|
+
.chunks_exact(2)
|
|
1598
|
+
.map(|chunk| (chunk[0], chunk[1]))
|
|
1599
|
+
.collect();
|
|
1600
|
+
let b_pairs: Vec<(f32, f32)> = b
|
|
1601
|
+
.chunks_exact(2)
|
|
1602
|
+
.map(|chunk| (chunk[0], chunk[1]))
|
|
1603
|
+
.collect();
|
|
1604
|
+
check_complex::<Scalar, <Scalar as Dot>::Output, _>(
|
|
1605
|
+
&a_pairs,
|
|
1606
|
+
&b_pairs,
|
|
1607
|
+
Scalar::vdot,
|
|
1608
|
+
expected_re,
|
|
1609
|
+
expected_im,
|
|
1610
|
+
"complex_vdot",
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
fn check_complex_bilinear_identity<Scalar>(element_count: usize)
|
|
1615
|
+
where
|
|
1616
|
+
Scalar: ComplexSample,
|
|
1617
|
+
<Scalar as Bilinear>::Output: ComplexValue,
|
|
1618
|
+
{
|
|
1619
|
+
let mut a = vec![Scalar::zero(); element_count];
|
|
1620
|
+
let mut b = vec![Scalar::zero(); element_count];
|
|
1621
|
+
a[0] = Scalar::one();
|
|
1622
|
+
b[0] = Scalar::one();
|
|
1623
|
+
let mut c = vec![Scalar::zero(); element_count * element_count];
|
|
1624
|
+
for i in 0..element_count {
|
|
1625
|
+
c[i * element_count + i] = Scalar::one();
|
|
1626
|
+
}
|
|
1627
|
+
let result = Scalar::bilinear(&a, &b, &c).unwrap();
|
|
1628
|
+
let tol = Scalar::atol() + Scalar::rtol();
|
|
1629
|
+
assert_close(
|
|
1630
|
+
result.real(),
|
|
1631
|
+
1.0,
|
|
1632
|
+
tol,
|
|
1633
|
+
0.0,
|
|
1634
|
+
&format!(
|
|
1635
|
+
"complex_bilinear<{}> real",
|
|
1636
|
+
core::any::type_name::<Scalar>()
|
|
1637
|
+
),
|
|
1638
|
+
);
|
|
1639
|
+
assert_close(
|
|
1640
|
+
result.imag(),
|
|
1641
|
+
0.0,
|
|
1642
|
+
tol,
|
|
1643
|
+
0.0,
|
|
1644
|
+
&format!(
|
|
1645
|
+
"complex_bilinear<{}> imag",
|
|
1646
|
+
core::any::type_name::<Scalar>()
|
|
1647
|
+
),
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
#[test]
|
|
1652
|
+
fn complex_dot_vdot_bilinear() {
|
|
1653
|
+
// [1+2i, 3+4i] · [5+6i, 7+8i]
|
|
1654
|
+
let a = &[1.0_f32, 2.0, 3.0, 4.0];
|
|
1655
|
+
let b = &[5.0_f32, 6.0, 7.0, 8.0];
|
|
1656
|
+
|
|
1657
|
+
// dot: (-18, 68)
|
|
1658
|
+
check_complex_dot::<f64c>(a, b, -18.0, 68.0);
|
|
1659
|
+
check_complex_dot::<f32c>(a, b, -18.0, 68.0);
|
|
1660
|
+
check_complex_dot::<f16c>(a, b, -18.0, 68.0);
|
|
1661
|
+
check_complex_dot::<bf16c>(a, b, -18.0, 68.0);
|
|
1662
|
+
|
|
1663
|
+
// vdot (conjugate): (70, -8)
|
|
1664
|
+
check_complex_vdot::<f64c>(a, b, 70.0, -8.0);
|
|
1665
|
+
check_complex_vdot::<f32c>(a, b, 70.0, -8.0);
|
|
1666
|
+
check_complex_vdot::<f16c>(a, b, 70.0, -8.0);
|
|
1667
|
+
check_complex_vdot::<bf16c>(a, b, 70.0, -8.0);
|
|
1668
|
+
|
|
1669
|
+
// bilinear: identity matrix, unit vector → (1, 0)
|
|
1670
|
+
check_complex_bilinear_identity::<f64c>(4);
|
|
1671
|
+
check_complex_bilinear_identity::<f32c>(4);
|
|
1672
|
+
check_complex_bilinear_identity::<f16c>(4);
|
|
1673
|
+
check_complex_bilinear_identity::<bf16c>(4);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// endregion
|
|
1677
|
+
|
|
1678
|
+
// region: Denormal (subnormal) inputs
|
|
1679
|
+
//
|
|
1680
|
+
// Verify that distance kernels produce correct results when fed IEEE-754
|
|
1681
|
+
// denormal (subnormal) values. This guards against FTZ/DAZ silently
|
|
1682
|
+
// flushing tiny values to zero.
|
|
1683
|
+
|
|
1684
|
+
#[test]
|
|
1685
|
+
fn dot_f32_denormals() {
|
|
1686
|
+
// Largest f32 denormal: exp=0, mantissa=0x7FFFFF → ≈ 1.1754942e-38
|
|
1687
|
+
let d = f32::from_bits(0x007F_FFFF);
|
|
1688
|
+
// dot([d,d,d], [d,d,d]) = 3 * d * d
|
|
1689
|
+
// F32 dot accumulates in F64 internally, so the result is precise.
|
|
1690
|
+
let expected = 3.0 * (d as f64) * (d as f64);
|
|
1691
|
+
let a = [d, d, d];
|
|
1692
|
+
let b = [d, d, d];
|
|
1693
|
+
let result = f32::dot(&a, &b).unwrap();
|
|
1694
|
+
assert_close(result as f64, expected, 1e-50, 1e-6, "dot<f32> denormal");
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
#[test]
|
|
1698
|
+
fn dot_f64_denormals() {
|
|
1699
|
+
// Largest f64 denormal: exp=0, mantissa all-ones → ≈ 2.225e-308
|
|
1700
|
+
let d = f64::from_bits(0x000F_FFFF_FFFF_FFFF);
|
|
1701
|
+
let expected = 3.0 * d * d;
|
|
1702
|
+
let a = [d, d, d];
|
|
1703
|
+
let b = [d, d, d];
|
|
1704
|
+
let result = f64::dot(&a, &b).unwrap();
|
|
1705
|
+
// F64 dot uses Neumaier compensation; the product d*d underflows to 0 in f64
|
|
1706
|
+
// (since d ≈ 2.2e-308, d*d ≈ 5e-616 which is below f64 min denormal ~5e-324).
|
|
1707
|
+
// So expected = 0.0 and result should also be 0.0 — the key test is that
|
|
1708
|
+
// the kernel doesn't crash or produce NaN/Inf.
|
|
1709
|
+
assert!(
|
|
1710
|
+
result.is_finite(),
|
|
1711
|
+
"dot<f64> denormal produced non-finite: {result}"
|
|
1712
|
+
);
|
|
1713
|
+
assert_close(result, expected, 1e-300, 1e-6, "dot<f64> denormal");
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
#[test]
|
|
1717
|
+
fn dot_f16_denormals() {
|
|
1718
|
+
// F16 denormal: exp=0, mantissa=0x0100 → 2^-14 * 256/1024 = 2^-16 ≈ 1.526e-5
|
|
1719
|
+
// F16 denormals upcast to normal f32 values, so no penalty or flushing.
|
|
1720
|
+
let d = f16(0x0100);
|
|
1721
|
+
let d_f64 = d.to_f64();
|
|
1722
|
+
let expected = 3.0 * d_f64 * d_f64;
|
|
1723
|
+
check_dot::<f16>(
|
|
1724
|
+
&[d.to_f32(), d.to_f32(), d.to_f32()],
|
|
1725
|
+
&[d.to_f32(), d.to_f32(), d.to_f32()],
|
|
1726
|
+
expected,
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
#[test]
|
|
1731
|
+
fn dot_bf16_denormals() {
|
|
1732
|
+
// BF16 denormal: exp=0, mantissa=0x7F (largest BF16 denormal)
|
|
1733
|
+
// BF16 shares f32's exponent, so this IS a f32 denormal after upcast.
|
|
1734
|
+
let d = bf16(0x007F);
|
|
1735
|
+
let d_f64 = d.to_f64();
|
|
1736
|
+
let expected = 3.0 * d_f64 * d_f64;
|
|
1737
|
+
check_dot::<bf16>(
|
|
1738
|
+
&[d.to_f32(), d.to_f32(), d.to_f32()],
|
|
1739
|
+
&[d.to_f32(), d.to_f32(), d.to_f32()],
|
|
1740
|
+
expected,
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
#[test]
|
|
1745
|
+
fn sqeuclidean_f32_denormals() {
|
|
1746
|
+
// Two vectors of denormals: differences are also denormal.
|
|
1747
|
+
let a_val = f32::from_bits(0x007F_FFFF); // largest f32 denormal
|
|
1748
|
+
let b_val = f32::from_bits(0x003F_FFFF); // half-way f32 denormal
|
|
1749
|
+
let diff = (a_val as f64) - (b_val as f64);
|
|
1750
|
+
let expected = 3.0 * diff * diff;
|
|
1751
|
+
let a = [a_val; 3];
|
|
1752
|
+
let b = [b_val; 3];
|
|
1753
|
+
let result = f32::sqeuclidean(&a, &b).unwrap();
|
|
1754
|
+
assert!(
|
|
1755
|
+
result.is_finite(),
|
|
1756
|
+
"sqeuclidean<f32> denormal produced non-finite: {result}"
|
|
1757
|
+
);
|
|
1758
|
+
assert_close(
|
|
1759
|
+
result as f64,
|
|
1760
|
+
expected,
|
|
1761
|
+
1e-50,
|
|
1762
|
+
1e-6,
|
|
1763
|
+
"sqeuclidean<f32> denormal",
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
#[test]
|
|
1768
|
+
fn sqeuclidean_f64_denormals() {
|
|
1769
|
+
let a_val = f64::from_bits(0x000F_FFFF_FFFF_FFFF);
|
|
1770
|
+
let b_val = f64::from_bits(0x0007_FFFF_FFFF_FFFF);
|
|
1771
|
+
let diff = a_val - b_val;
|
|
1772
|
+
let expected = 3.0 * diff * diff;
|
|
1773
|
+
let a = [a_val; 3];
|
|
1774
|
+
let b = [b_val; 3];
|
|
1775
|
+
let result = f64::sqeuclidean(&a, &b).unwrap();
|
|
1776
|
+
assert!(
|
|
1777
|
+
result.is_finite(),
|
|
1778
|
+
"sqeuclidean<f64> denormal produced non-finite: {result}"
|
|
1779
|
+
);
|
|
1780
|
+
assert_close(result, expected, 1e-300, 1e-6, "sqeuclidean<f64> denormal");
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
#[test]
|
|
1784
|
+
fn angular_f32_denormals() {
|
|
1785
|
+
// Two identical denormal vectors: angular distance should be 0 (cosine = 1).
|
|
1786
|
+
let d = f32::from_bits(0x007F_FFFF);
|
|
1787
|
+
let a = [d, d, d];
|
|
1788
|
+
let result = f32::angular(&a, &a).unwrap();
|
|
1789
|
+
assert!(
|
|
1790
|
+
result.is_finite(),
|
|
1791
|
+
"angular<f32> denormal produced non-finite: {result}"
|
|
1792
|
+
);
|
|
1793
|
+
assert_close(
|
|
1794
|
+
result as f64,
|
|
1795
|
+
0.0,
|
|
1796
|
+
1e-4,
|
|
1797
|
+
0.0,
|
|
1798
|
+
"angular<f32> identical denormals",
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
#[test]
|
|
1803
|
+
fn angular_f64_denormals() {
|
|
1804
|
+
let d = f64::from_bits(0x000F_FFFF_FFFF_FFFF);
|
|
1805
|
+
let a = [d, d, d];
|
|
1806
|
+
let result = f64::angular(&a, &a).unwrap();
|
|
1807
|
+
assert!(
|
|
1808
|
+
result.is_finite(),
|
|
1809
|
+
"angular<f64> denormal produced non-finite: {result}"
|
|
1810
|
+
);
|
|
1811
|
+
assert_close(result, 0.0, 1e-9, 0.0, "angular<f64> identical denormals");
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// endregion
|
|
1815
|
+
}
|