usearch 2.21.3 → 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 +235 -52
- 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,1899 @@
|
|
|
1
|
+
//! Owning and non-owning vector types with signed indexing and sub-byte support.
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides:
|
|
4
|
+
//!
|
|
5
|
+
//! - [`Vector`]: Owning, non-resizable, SIMD-aligned vector
|
|
6
|
+
//! - [`VectorView`]: Immutable, strided, non-owning view
|
|
7
|
+
//! - [`VectorSpan`]: Mutable, strided, non-owning view
|
|
8
|
+
//! - [`VectorIndex`]: Signed indexing trait (negative indices wrap from end)
|
|
9
|
+
//!
|
|
10
|
+
//! All types use [`StorageElement`] as their element bound, with sub-byte types
|
|
11
|
+
//! (i4x2, u4x2, u1x8) supported via `try_get`/`try_set` and iterators.
|
|
12
|
+
//!
|
|
13
|
+
//! # Signed (Python-style) indexing
|
|
14
|
+
//!
|
|
15
|
+
//! Negative indices count from the end, mirroring Python's `list[-1]` idiom:
|
|
16
|
+
//!
|
|
17
|
+
//! ```ignore
|
|
18
|
+
//! use numkong::vector::Vector;
|
|
19
|
+
//! let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
|
|
20
|
+
//! // Last element via negative index.
|
|
21
|
+
//! assert_eq!(v.try_get(-1_i32).unwrap(), 50.0);
|
|
22
|
+
//! // Second-to-last.
|
|
23
|
+
//! assert_eq!(v.try_get(-2_i32).unwrap(), 40.0);
|
|
24
|
+
//! // Out-of-range negatives return an error rather than panic.
|
|
25
|
+
//! assert!(v.try_get(-6_i32).is_err());
|
|
26
|
+
//! ```
|
|
27
|
+
//!
|
|
28
|
+
//! # Sub-byte element iteration
|
|
29
|
+
//!
|
|
30
|
+
//! For packed types such as `i4x2`, `u4x2`, and `u1x8`, iteration yields one
|
|
31
|
+
//! logical dimension per step (not one packed storage value):
|
|
32
|
+
//!
|
|
33
|
+
//! ```ignore
|
|
34
|
+
//! use numkong::vector::Vector;
|
|
35
|
+
//! use numkong::types::u1x8;
|
|
36
|
+
//! let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
|
|
37
|
+
//! for (i, mut bit) in v.iter_mut().enumerate() {
|
|
38
|
+
//! *bit = if i % 2 == 0 { 1 } else { 0 };
|
|
39
|
+
//! }
|
|
40
|
+
//! assert_eq!(v.try_get(0_usize).unwrap(), 1);
|
|
41
|
+
//! assert_eq!(v.try_get(1_usize).unwrap(), 0);
|
|
42
|
+
//! ```
|
|
43
|
+
|
|
44
|
+
extern crate alloc;
|
|
45
|
+
|
|
46
|
+
use core::marker::PhantomData;
|
|
47
|
+
use core::ptr::NonNull;
|
|
48
|
+
|
|
49
|
+
use crate::tensor::{Allocator, Global, Tensor, TensorError, SIMD_ALIGNMENT};
|
|
50
|
+
use crate::types::{DimMut, DimRef, FloatConvertible, NumberLike, StorageElement};
|
|
51
|
+
|
|
52
|
+
// region: VectorIndex — Signed Indexing
|
|
53
|
+
|
|
54
|
+
mod private {
|
|
55
|
+
pub trait Sealed {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// Trait for vector index types. Supports signed integers (negative = from end).
|
|
59
|
+
pub trait VectorIndex: private::Sealed + Copy {
|
|
60
|
+
/// Resolve this index to a `usize` offset, or `None` if out of bounds.
|
|
61
|
+
fn resolve(self, len: usize) -> Option<usize>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
macro_rules! impl_vec_index_unsigned {
|
|
65
|
+
($($t:ty),*) => {$(
|
|
66
|
+
impl private::Sealed for $t {}
|
|
67
|
+
impl VectorIndex for $t {
|
|
68
|
+
#[inline]
|
|
69
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
70
|
+
let index = self as usize;
|
|
71
|
+
if index < len { Some(index) } else { None }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
)*};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
macro_rules! impl_vec_index_signed {
|
|
78
|
+
($($t:ty),*) => {$(
|
|
79
|
+
impl private::Sealed for $t {}
|
|
80
|
+
impl VectorIndex for $t {
|
|
81
|
+
#[inline]
|
|
82
|
+
fn resolve(self, len: usize) -> Option<usize> {
|
|
83
|
+
let index = if self >= 0 {
|
|
84
|
+
self as usize
|
|
85
|
+
} else {
|
|
86
|
+
let neg = (-(self as isize)) as usize;
|
|
87
|
+
if neg > len { return None; }
|
|
88
|
+
len - neg
|
|
89
|
+
};
|
|
90
|
+
if index < len { Some(index) } else { None }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
)*};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
impl_vec_index_unsigned!(usize, u8, u16, u32, u64);
|
|
97
|
+
impl_vec_index_signed!(isize, i8, i16, i32, i64);
|
|
98
|
+
|
|
99
|
+
// endregion: VectorIndex
|
|
100
|
+
|
|
101
|
+
// region: Sub-byte Proxy Types
|
|
102
|
+
|
|
103
|
+
/// Immutable reference to a nibble (4-bit value) within a packed byte.
|
|
104
|
+
///
|
|
105
|
+
/// Obtained by iterating over or indexing into a `Vector<i4x2>` / `Vector<u4x2>`;
|
|
106
|
+
/// the proxy remembers whether it refers to the low or high nibble of the shared
|
|
107
|
+
/// byte and decodes accordingly. Prefer [`NibbleRef::get_unsigned`] for `u4x2`
|
|
108
|
+
/// storage and [`NibbleRef::get_signed`] for `i4x2` storage (the latter
|
|
109
|
+
/// sign-extends bit 3 so the return range is `-8..=7`).
|
|
110
|
+
///
|
|
111
|
+
/// # Example
|
|
112
|
+
///
|
|
113
|
+
/// ```ignore
|
|
114
|
+
/// use numkong::vector::Vector;
|
|
115
|
+
/// use numkong::types::i4x2;
|
|
116
|
+
/// let mut v = Vector::<i4x2>::try_zeros(2).unwrap();
|
|
117
|
+
/// v.try_set(0_usize, -3).unwrap();
|
|
118
|
+
/// // Reading via `try_get` returns the same signed value NibbleRef::get_signed yields.
|
|
119
|
+
/// assert_eq!(v.try_get(0_usize).unwrap(), -3);
|
|
120
|
+
/// ```
|
|
121
|
+
pub struct NibbleRef<'a> {
|
|
122
|
+
byte: *const u8,
|
|
123
|
+
high: bool,
|
|
124
|
+
_marker: PhantomData<&'a u8>,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
impl<'a> NibbleRef<'a> {
|
|
128
|
+
/// Read the nibble value as u8 (0..16).
|
|
129
|
+
#[inline]
|
|
130
|
+
pub fn get_unsigned(&self) -> u8 {
|
|
131
|
+
// SAFETY: byte pointer is valid for the lifetime 'a
|
|
132
|
+
let byte_value = unsafe { *self.byte };
|
|
133
|
+
if self.high {
|
|
134
|
+
byte_value >> 4
|
|
135
|
+
} else {
|
|
136
|
+
byte_value & 0x0F
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// Read the nibble value as i8 (-8..7), sign-extending bit 3.
|
|
141
|
+
#[inline]
|
|
142
|
+
pub fn get_signed(&self) -> i8 {
|
|
143
|
+
let nibble = self.get_unsigned();
|
|
144
|
+
if nibble & 0x08 != 0 {
|
|
145
|
+
nibble as i8 | !0x0Fi8
|
|
146
|
+
} else {
|
|
147
|
+
nibble as i8
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/// Mutable reference to a nibble (4-bit value) within a packed byte.
|
|
153
|
+
///
|
|
154
|
+
/// Obtained via mutable iteration / indexing of a `Vector<i4x2>` or
|
|
155
|
+
/// `Vector<u4x2>`. Writes perform a read-modify-write on the shared byte so the
|
|
156
|
+
/// sibling nibble is preserved. Pair `get_unsigned` / `set_unsigned` for
|
|
157
|
+
/// unsigned storage and `get_signed` / `set_signed` for signed storage.
|
|
158
|
+
///
|
|
159
|
+
/// # Example
|
|
160
|
+
///
|
|
161
|
+
/// ```ignore
|
|
162
|
+
/// use numkong::vector::Vector;
|
|
163
|
+
/// use numkong::types::u4x2;
|
|
164
|
+
/// let mut v = Vector::<u4x2>::try_zeros(4).unwrap();
|
|
165
|
+
/// for (i, mut nibble) in v.iter_mut().enumerate() {
|
|
166
|
+
/// *nibble = i as u8;
|
|
167
|
+
/// }
|
|
168
|
+
/// assert_eq!(v.try_get(3_usize).unwrap(), 3);
|
|
169
|
+
/// ```
|
|
170
|
+
pub struct NibbleRefMut<'a> {
|
|
171
|
+
byte: *mut u8,
|
|
172
|
+
high: bool,
|
|
173
|
+
_marker: PhantomData<&'a mut u8>,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
impl<'a> NibbleRefMut<'a> {
|
|
177
|
+
/// Read the nibble value as u8.
|
|
178
|
+
#[inline]
|
|
179
|
+
pub fn get_unsigned(&self) -> u8 {
|
|
180
|
+
let byte_value = unsafe { *self.byte };
|
|
181
|
+
if self.high {
|
|
182
|
+
byte_value >> 4
|
|
183
|
+
} else {
|
|
184
|
+
byte_value & 0x0F
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Read the nibble value as i8, sign-extending bit 3.
|
|
189
|
+
#[inline]
|
|
190
|
+
pub fn get_signed(&self) -> i8 {
|
|
191
|
+
let nibble = self.get_unsigned();
|
|
192
|
+
if nibble & 0x08 != 0 {
|
|
193
|
+
nibble as i8 | !0x0Fi8
|
|
194
|
+
} else {
|
|
195
|
+
nibble as i8
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/// Set the nibble to an unsigned value (low 4 bits used).
|
|
200
|
+
#[inline]
|
|
201
|
+
pub fn set_unsigned(&self, value: u8) {
|
|
202
|
+
// SAFETY: byte pointer is valid and mutable for the lifetime 'a
|
|
203
|
+
unsafe {
|
|
204
|
+
let byte_value = *self.byte;
|
|
205
|
+
if self.high {
|
|
206
|
+
*self.byte = (byte_value & 0x0F) | ((value & 0x0F) << 4);
|
|
207
|
+
} else {
|
|
208
|
+
*self.byte = (byte_value & 0xF0) | (value & 0x0F);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// Set the nibble to a signed value (low 4 bits used).
|
|
214
|
+
#[inline]
|
|
215
|
+
pub fn set_signed(&self, value: i8) {
|
|
216
|
+
self.set_unsigned(value as u8);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/// Immutable reference to a single bit within a packed byte.
|
|
221
|
+
///
|
|
222
|
+
/// Obtained by iterating over or indexing into a `Vector<u1x8>`. The `mask`
|
|
223
|
+
/// selects which of the eight bits the proxy represents; the byte itself is
|
|
224
|
+
/// shared with the seven sibling proxies for the same byte.
|
|
225
|
+
///
|
|
226
|
+
/// # Example
|
|
227
|
+
///
|
|
228
|
+
/// ```ignore
|
|
229
|
+
/// use numkong::vector::Vector;
|
|
230
|
+
/// use numkong::types::u1x8;
|
|
231
|
+
/// let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
|
|
232
|
+
/// v.try_set(3_usize, 1).unwrap();
|
|
233
|
+
/// // Immutable iteration yields BitRef-like proxies that deref to the bit value.
|
|
234
|
+
/// let bits: Vec<u8> = v.iter().map(|b| *b).collect();
|
|
235
|
+
/// assert_eq!(bits, vec![0, 0, 0, 1, 0, 0, 0, 0]);
|
|
236
|
+
/// ```
|
|
237
|
+
pub struct BitRef<'a> {
|
|
238
|
+
byte: *const u8,
|
|
239
|
+
mask: u8,
|
|
240
|
+
_marker: PhantomData<&'a u8>,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
impl<'a> BitRef<'a> {
|
|
244
|
+
/// Read the bit as bool.
|
|
245
|
+
#[inline]
|
|
246
|
+
pub fn get(&self) -> bool {
|
|
247
|
+
// SAFETY: byte pointer is valid for the lifetime 'a
|
|
248
|
+
(unsafe { *self.byte } & self.mask) != 0
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// Mutable reference to a single bit within a packed byte.
|
|
253
|
+
///
|
|
254
|
+
/// Obtained via mutable iteration / indexing of a `Vector<u1x8>`. Writes OR or
|
|
255
|
+
/// AND the stored byte so sibling bits are preserved; the `mask` selects which
|
|
256
|
+
/// bit this proxy represents.
|
|
257
|
+
///
|
|
258
|
+
/// # Example
|
|
259
|
+
///
|
|
260
|
+
/// ```ignore
|
|
261
|
+
/// use numkong::vector::Vector;
|
|
262
|
+
/// use numkong::types::u1x8;
|
|
263
|
+
/// let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
|
|
264
|
+
/// for (i, mut bit) in v.iter_mut().enumerate() {
|
|
265
|
+
/// *bit = (i % 2 == 0) as u8;
|
|
266
|
+
/// }
|
|
267
|
+
/// assert_eq!(v.try_get(0_usize).unwrap(), 1);
|
|
268
|
+
/// assert_eq!(v.try_get(1_usize).unwrap(), 0);
|
|
269
|
+
/// ```
|
|
270
|
+
pub struct BitRefMut<'a> {
|
|
271
|
+
byte: *mut u8,
|
|
272
|
+
mask: u8,
|
|
273
|
+
_marker: PhantomData<&'a mut u8>,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
impl<'a> BitRefMut<'a> {
|
|
277
|
+
/// Read the bit as bool.
|
|
278
|
+
#[inline]
|
|
279
|
+
pub fn get(&self) -> bool {
|
|
280
|
+
// SAFETY: byte pointer is valid for the lifetime 'a
|
|
281
|
+
(unsafe { *self.byte } & self.mask) != 0
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/// Set the bit.
|
|
285
|
+
#[inline]
|
|
286
|
+
pub fn set(&self, value: bool) {
|
|
287
|
+
// SAFETY: byte pointer is valid and mutable for the lifetime 'a
|
|
288
|
+
unsafe {
|
|
289
|
+
if value {
|
|
290
|
+
*self.byte |= self.mask;
|
|
291
|
+
} else {
|
|
292
|
+
*self.byte &= !self.mask;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// endregion: Sub-byte Proxy Types
|
|
299
|
+
|
|
300
|
+
// region: Vector
|
|
301
|
+
|
|
302
|
+
/// Owning, non-resizable, SIMD-aligned vector.
|
|
303
|
+
///
|
|
304
|
+
/// Size is fixed at construction. Uses [`StorageElement`] for element types,
|
|
305
|
+
/// including sub-byte packed types via `Scalar::dimensions_per_value()`.
|
|
306
|
+
///
|
|
307
|
+
/// For normal types (`dimensions_per_value() == 1`), supports `Index`/`IndexMut`.
|
|
308
|
+
/// For sub-byte types, use `try_get`/`try_set` or iterators.
|
|
309
|
+
pub struct Vector<Scalar: StorageElement, Alloc: Allocator = Global> {
|
|
310
|
+
/// Pointer to the allocated buffer (typed as `Scalar` for alignment).
|
|
311
|
+
data: NonNull<Scalar>,
|
|
312
|
+
/// Number of logical dimensions.
|
|
313
|
+
dims: usize,
|
|
314
|
+
/// Number of storage values (dims / dimensions_per_value, rounded up).
|
|
315
|
+
values: usize,
|
|
316
|
+
/// Allocator instance.
|
|
317
|
+
alloc: Alloc,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
unsafe impl<Scalar: StorageElement + Send, Alloc: Allocator + Send> Send for Vector<Scalar, Alloc> {}
|
|
321
|
+
unsafe impl<Scalar: StorageElement + Sync, Alloc: Allocator + Sync> Sync for Vector<Scalar, Alloc> {}
|
|
322
|
+
|
|
323
|
+
impl<Scalar: StorageElement, Alloc: Allocator> Drop for Vector<Scalar, Alloc> {
|
|
324
|
+
fn drop(&mut self) {
|
|
325
|
+
if self.values > 0 {
|
|
326
|
+
let layout = alloc::alloc::Layout::from_size_align(
|
|
327
|
+
self.values * core::mem::size_of::<Scalar>(),
|
|
328
|
+
SIMD_ALIGNMENT,
|
|
329
|
+
)
|
|
330
|
+
.unwrap();
|
|
331
|
+
// SAFETY: data was allocated with this layout in try_zeros_in,
|
|
332
|
+
// and values > 0 guarantees the pointer is non-dangling.
|
|
333
|
+
unsafe {
|
|
334
|
+
self.alloc.deallocate(
|
|
335
|
+
NonNull::new_unchecked(self.data.as_ptr() as *mut u8),
|
|
336
|
+
layout,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/// Convert dimension count to value count for `Scalar`.
|
|
344
|
+
///
|
|
345
|
+
/// For sub-byte types where `dimensions_per_value() > 1`, this is a ceiling
|
|
346
|
+
/// division via [`usize::div_ceil`].
|
|
347
|
+
#[inline]
|
|
348
|
+
fn dims_to_values<Scalar: StorageElement>(dims: usize) -> usize {
|
|
349
|
+
dims.div_ceil(Scalar::dimensions_per_value())
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
353
|
+
/// Construct a vector from raw parts, taking ownership of the allocation.
|
|
354
|
+
///
|
|
355
|
+
/// # Safety
|
|
356
|
+
/// - `data` must point to a valid allocation of `values * size_of::<Scalar>()` bytes
|
|
357
|
+
/// obtained from `alloc`, aligned to [`SIMD_ALIGNMENT`].
|
|
358
|
+
/// - `dims` and `values` must be consistent (`values == ceil(dims / dimensions_per_value())`).
|
|
359
|
+
/// - The caller must not free the memory (this vector takes ownership).
|
|
360
|
+
pub unsafe fn from_raw_parts(
|
|
361
|
+
data: NonNull<Scalar>,
|
|
362
|
+
dims: usize,
|
|
363
|
+
values: usize,
|
|
364
|
+
alloc: Alloc,
|
|
365
|
+
) -> Self {
|
|
366
|
+
Self {
|
|
367
|
+
data,
|
|
368
|
+
dims,
|
|
369
|
+
values,
|
|
370
|
+
alloc,
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/// Try to create a zero-initialized vector with the given number of dimensions.
|
|
375
|
+
pub fn try_zeros_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
|
|
376
|
+
let values = dims_to_values::<Scalar>(dims);
|
|
377
|
+
if values == 0 {
|
|
378
|
+
return Ok(Self {
|
|
379
|
+
data: NonNull::dangling(),
|
|
380
|
+
dims: 0,
|
|
381
|
+
values: 0,
|
|
382
|
+
alloc,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
let size = values * core::mem::size_of::<Scalar>();
|
|
386
|
+
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
387
|
+
.map_err(|_| TensorError::AllocationFailed)?;
|
|
388
|
+
let ptr = alloc
|
|
389
|
+
.allocate(layout)
|
|
390
|
+
.ok_or(TensorError::AllocationFailed)?;
|
|
391
|
+
unsafe { core::ptr::write_bytes(ptr.as_ptr(), 0, size) };
|
|
392
|
+
Ok(Self {
|
|
393
|
+
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
394
|
+
dims,
|
|
395
|
+
values,
|
|
396
|
+
alloc,
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/// Try to create a vector filled with `value`.
|
|
401
|
+
pub fn try_full_in(dims: usize, value: Scalar, alloc: Alloc) -> Result<Self, TensorError> {
|
|
402
|
+
let v = Self::try_zeros_in(dims, alloc)?;
|
|
403
|
+
if v.values > 0 {
|
|
404
|
+
let ptr = v.data.as_ptr();
|
|
405
|
+
for i in 0..v.values {
|
|
406
|
+
unsafe { ptr.add(i).write(value) };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
Ok(v)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/// Try to create a vector filled with ones.
|
|
413
|
+
pub fn try_ones_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError>
|
|
414
|
+
where
|
|
415
|
+
Scalar: NumberLike,
|
|
416
|
+
{
|
|
417
|
+
Self::try_full_in(dims, Scalar::one(), alloc)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/// Try to create an uninitialized vector.
|
|
421
|
+
///
|
|
422
|
+
/// # Safety
|
|
423
|
+
/// The returned vector's contents are uninitialized. Reading from it before
|
|
424
|
+
/// writing is undefined behavior.
|
|
425
|
+
pub unsafe fn try_empty_in(dims: usize, alloc: Alloc) -> Result<Self, TensorError> {
|
|
426
|
+
let values = dims_to_values::<Scalar>(dims);
|
|
427
|
+
if values == 0 {
|
|
428
|
+
return Ok(Self {
|
|
429
|
+
data: NonNull::dangling(),
|
|
430
|
+
dims: 0,
|
|
431
|
+
values: 0,
|
|
432
|
+
alloc,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
let size = values * core::mem::size_of::<Scalar>();
|
|
436
|
+
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
437
|
+
.map_err(|_| TensorError::AllocationFailed)?;
|
|
438
|
+
let ptr = alloc
|
|
439
|
+
.allocate(layout)
|
|
440
|
+
.ok_or(TensorError::AllocationFailed)?;
|
|
441
|
+
Ok(Self {
|
|
442
|
+
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
443
|
+
dims,
|
|
444
|
+
values,
|
|
445
|
+
alloc,
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/// Try to create a vector from a slice of scalars (f32 values).
|
|
450
|
+
///
|
|
451
|
+
/// Each f32 value is converted through `DimScalar::from_f32()` before storage.
|
|
452
|
+
pub fn try_from_scalars_in(scalars: &[f32], alloc: Alloc) -> Result<Self, TensorError>
|
|
453
|
+
where
|
|
454
|
+
Scalar: FloatConvertible,
|
|
455
|
+
{
|
|
456
|
+
let element_count = scalars.len();
|
|
457
|
+
let mut v = Self::try_zeros_in(element_count, alloc)?;
|
|
458
|
+
for (i, &s) in scalars.iter().enumerate() {
|
|
459
|
+
v.try_set(i, Scalar::DimScalar::from_f32(s))?;
|
|
460
|
+
}
|
|
461
|
+
Ok(v)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/// Try to create a vector from a slice of per-dimension scalars.
|
|
465
|
+
///
|
|
466
|
+
/// Each element in `dim_values` corresponds to one logical dimension.
|
|
467
|
+
pub fn try_from_dims_in(
|
|
468
|
+
dim_values: &[Scalar::DimScalar],
|
|
469
|
+
alloc: Alloc,
|
|
470
|
+
) -> Result<Self, TensorError>
|
|
471
|
+
where
|
|
472
|
+
Scalar: FloatConvertible,
|
|
473
|
+
{
|
|
474
|
+
let element_count = dim_values.len();
|
|
475
|
+
let mut v = Self::try_zeros_in(element_count, alloc)?;
|
|
476
|
+
for (i, &d) in dim_values.iter().enumerate() {
|
|
477
|
+
v.try_set(i, d)?;
|
|
478
|
+
}
|
|
479
|
+
Ok(v)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/// Number of logical dimensions.
|
|
483
|
+
#[inline]
|
|
484
|
+
pub fn dims(&self) -> usize {
|
|
485
|
+
self.dims
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/// Number of logical dimensions (same as `dims()`).
|
|
489
|
+
#[inline]
|
|
490
|
+
pub fn size(&self) -> usize {
|
|
491
|
+
self.dims
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/// Number of underlying storage values (`Scalar` instances).
|
|
495
|
+
#[inline]
|
|
496
|
+
pub fn size_values(&self) -> usize {
|
|
497
|
+
self.values
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/// Returns true if the vector has zero dimensions.
|
|
501
|
+
#[inline]
|
|
502
|
+
pub fn is_empty(&self) -> bool {
|
|
503
|
+
self.dims == 0
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/// Raw pointer to the underlying data.
|
|
507
|
+
#[inline]
|
|
508
|
+
pub fn as_ptr(&self) -> *const Scalar {
|
|
509
|
+
self.data.as_ptr()
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/// Mutable raw pointer to the underlying data.
|
|
513
|
+
#[inline]
|
|
514
|
+
pub fn as_mut_ptr(&mut self) -> *mut Scalar {
|
|
515
|
+
self.data.as_ptr()
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/// Size in bytes.
|
|
519
|
+
#[inline]
|
|
520
|
+
pub fn size_bytes(&self) -> usize {
|
|
521
|
+
self.values * core::mem::size_of::<Scalar>()
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/// Create an immutable view of this vector.
|
|
525
|
+
#[inline]
|
|
526
|
+
pub fn view(&self) -> VectorView<'_, Scalar> {
|
|
527
|
+
VectorView {
|
|
528
|
+
data: self.data.as_ptr() as *const Scalar,
|
|
529
|
+
dims: self.dims,
|
|
530
|
+
stride_bytes: core::mem::size_of::<Scalar>() as isize,
|
|
531
|
+
_marker: PhantomData,
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/// Create a mutable span of this vector.
|
|
536
|
+
#[inline]
|
|
537
|
+
pub fn span(&mut self) -> VectorSpan<'_, Scalar> {
|
|
538
|
+
VectorSpan {
|
|
539
|
+
data: self.data.as_ptr(),
|
|
540
|
+
dims: self.dims,
|
|
541
|
+
stride_bytes: core::mem::size_of::<Scalar>() as isize,
|
|
542
|
+
_marker: PhantomData,
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/// Try to get the logical dimension at `index` (supports signed indexing).
|
|
547
|
+
///
|
|
548
|
+
/// Returns the native `DimScalar` type (e.g., `f64` for `Vector<f64>`, `i8` for `Vector<i4x2>`).
|
|
549
|
+
/// For sub-byte types, unpacks the appropriate sub-dimension from the packed storage value.
|
|
550
|
+
///
|
|
551
|
+
/// # Examples
|
|
552
|
+
///
|
|
553
|
+
/// Positive and Python-style negative indexing:
|
|
554
|
+
///
|
|
555
|
+
/// ```ignore
|
|
556
|
+
/// use numkong::vector::Vector;
|
|
557
|
+
/// let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
|
|
558
|
+
/// assert_eq!(v.try_get(0_usize).unwrap(), 10.0);
|
|
559
|
+
/// assert_eq!(v.try_get(-1_i32).unwrap(), 50.0);
|
|
560
|
+
/// assert_eq!(v.try_get(-5_i32).unwrap(), 10.0);
|
|
561
|
+
/// // Out-of-range indices return an error instead of panicking.
|
|
562
|
+
/// assert!(v.try_get(5_usize).is_err());
|
|
563
|
+
/// assert!(v.try_get(-6_i32).is_err());
|
|
564
|
+
/// ```
|
|
565
|
+
#[inline]
|
|
566
|
+
pub fn try_get<AnyIndex: VectorIndex>(
|
|
567
|
+
&self,
|
|
568
|
+
index: AnyIndex,
|
|
569
|
+
) -> Result<Scalar::DimScalar, TensorError>
|
|
570
|
+
where
|
|
571
|
+
Scalar: FloatConvertible,
|
|
572
|
+
{
|
|
573
|
+
let i = index
|
|
574
|
+
.resolve(self.dims)
|
|
575
|
+
.ok_or(TensorError::IndexOutOfBounds {
|
|
576
|
+
index: 0,
|
|
577
|
+
size: self.dims,
|
|
578
|
+
})?;
|
|
579
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
580
|
+
let value_index = i / dims_per_value;
|
|
581
|
+
let sub_index = i % dims_per_value;
|
|
582
|
+
// SAFETY: value_index < self.values, guaranteed by dims/values invariant
|
|
583
|
+
let packed = unsafe { *self.data.as_ptr().add(value_index) };
|
|
584
|
+
Ok(packed.unpack().as_ref()[sub_index])
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// Try to set the logical dimension at `index`.
|
|
588
|
+
///
|
|
589
|
+
/// Accepts the native `DimScalar` type. For sub-byte types, reads the current packed value,
|
|
590
|
+
/// updates the targeted sub-dimension, and writes back the modified packed value.
|
|
591
|
+
///
|
|
592
|
+
/// # Examples
|
|
593
|
+
///
|
|
594
|
+
/// ```ignore
|
|
595
|
+
/// use numkong::vector::Vector;
|
|
596
|
+
/// let mut v = Vector::<f32>::try_zeros(4).unwrap();
|
|
597
|
+
/// v.try_set(0_usize, 1.0).unwrap();
|
|
598
|
+
/// // Signed indices let you write to the tail without computing the length.
|
|
599
|
+
/// v.try_set(-1_i32, 4.0).unwrap();
|
|
600
|
+
/// assert_eq!(v.try_get(0_usize).unwrap(), 1.0);
|
|
601
|
+
/// assert_eq!(v.try_get(3_usize).unwrap(), 4.0);
|
|
602
|
+
/// ```
|
|
603
|
+
#[inline]
|
|
604
|
+
pub fn try_set<AnyIndex: VectorIndex>(
|
|
605
|
+
&mut self,
|
|
606
|
+
index: AnyIndex,
|
|
607
|
+
value: Scalar::DimScalar,
|
|
608
|
+
) -> Result<(), TensorError>
|
|
609
|
+
where
|
|
610
|
+
Scalar: FloatConvertible,
|
|
611
|
+
{
|
|
612
|
+
let i = index
|
|
613
|
+
.resolve(self.dims)
|
|
614
|
+
.ok_or(TensorError::IndexOutOfBounds {
|
|
615
|
+
index: 0,
|
|
616
|
+
size: self.dims,
|
|
617
|
+
})?;
|
|
618
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
619
|
+
let value_index = i / dims_per_value;
|
|
620
|
+
let sub_index = i % dims_per_value;
|
|
621
|
+
// SAFETY: value_index < self.values, guaranteed by dims/values invariant
|
|
622
|
+
let ptr = unsafe { self.data.as_ptr().add(value_index) };
|
|
623
|
+
let mut unpacked = unsafe { *ptr }.unpack();
|
|
624
|
+
unpacked.as_mut()[sub_index] = value;
|
|
625
|
+
unsafe { ptr.write(Scalar::pack(unpacked)) };
|
|
626
|
+
Ok(())
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/// Get a slice of the underlying storage values (only for normal types).
|
|
630
|
+
#[inline]
|
|
631
|
+
pub fn as_slice(&self) -> &[Scalar] {
|
|
632
|
+
if Scalar::dimensions_per_value() == 1 {
|
|
633
|
+
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.values) }
|
|
634
|
+
} else {
|
|
635
|
+
// For sub-byte types, return the packed values
|
|
636
|
+
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), self.values) }
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/// Get a mutable slice of the underlying storage values.
|
|
641
|
+
#[inline]
|
|
642
|
+
pub fn as_mut_slice(&mut self) -> &mut [Scalar] {
|
|
643
|
+
unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr(), self.values) }
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/// Returns an iterator over the logical dimension values, yielding [`DimRef`] proxies.
|
|
647
|
+
pub fn iter(&self) -> VectorViewIterator<'_, Scalar>
|
|
648
|
+
where
|
|
649
|
+
Scalar: FloatConvertible,
|
|
650
|
+
{
|
|
651
|
+
self.view().iter()
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/// Returns a mutable iterator over the logical dimension values, yielding [`DimMut`] proxies.
|
|
655
|
+
pub fn iter_mut(&mut self) -> VectorSpanIterator<'_, Scalar>
|
|
656
|
+
where
|
|
657
|
+
Scalar: FloatConvertible,
|
|
658
|
+
{
|
|
659
|
+
VectorSpanIterator {
|
|
660
|
+
data: self.data.as_ptr(),
|
|
661
|
+
stride_bytes: core::mem::size_of::<Scalar>() as isize,
|
|
662
|
+
front: 0,
|
|
663
|
+
back: self.dims,
|
|
664
|
+
_marker: PhantomData,
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
impl<Scalar: StorageElement, Alloc: Allocator> Vector<Scalar, Alloc> {
|
|
670
|
+
/// Convert this vector into a 1D tensor, transferring ownership without copying.
|
|
671
|
+
pub fn try_into_tensor<const MAX_RANK: usize>(
|
|
672
|
+
self,
|
|
673
|
+
) -> Result<Tensor<Scalar, Alloc, MAX_RANK>, TensorError> {
|
|
674
|
+
if MAX_RANK == 0 {
|
|
675
|
+
return Err(TensorError::TooManyRanks { got: 1 });
|
|
676
|
+
}
|
|
677
|
+
let mut shape = [0usize; MAX_RANK];
|
|
678
|
+
shape[0] = self.dims;
|
|
679
|
+
let mut strides = [0isize; MAX_RANK];
|
|
680
|
+
strides[0] = core::mem::size_of::<Scalar>() as isize;
|
|
681
|
+
let alloc_bytes = self.values * core::mem::size_of::<Scalar>();
|
|
682
|
+
let data = self.data;
|
|
683
|
+
// SAFETY: we read the allocator out before forget, transferring ownership
|
|
684
|
+
let alloc = unsafe { core::ptr::read(&self.alloc) };
|
|
685
|
+
core::mem::forget(self);
|
|
686
|
+
// SAFETY: data/alloc_bytes match the original allocation
|
|
687
|
+
let tensor = unsafe { Tensor::from_raw_parts(data, alloc_bytes, shape, strides, 1, alloc) };
|
|
688
|
+
Ok(tensor)
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
impl<Scalar: StorageElement> Vector<Scalar, Global> {
|
|
693
|
+
/// Create a zero-initialized vector with the global allocator.
|
|
694
|
+
pub fn try_zeros(dims: usize) -> Result<Self, TensorError> {
|
|
695
|
+
Self::try_zeros_in(dims, Global)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/// Create a vector filled with `value`.
|
|
699
|
+
pub fn try_full(dims: usize, value: Scalar) -> Result<Self, TensorError> {
|
|
700
|
+
Self::try_full_in(dims, value, Global)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/// Create a vector filled with ones.
|
|
704
|
+
pub fn try_ones(dims: usize) -> Result<Self, TensorError>
|
|
705
|
+
where
|
|
706
|
+
Scalar: NumberLike,
|
|
707
|
+
{
|
|
708
|
+
Self::try_full(dims, Scalar::one())
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/// Create an uninitialized vector.
|
|
712
|
+
///
|
|
713
|
+
/// # Safety
|
|
714
|
+
/// The returned vector's contents are uninitialized. Reading from it before
|
|
715
|
+
/// writing is undefined behavior.
|
|
716
|
+
pub unsafe fn try_empty(dims: usize) -> Result<Self, TensorError> {
|
|
717
|
+
unsafe { Self::try_empty_in(dims, Global) }
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/// Create a vector from scalar f32 values.
|
|
721
|
+
pub fn try_from_scalars(scalars: &[f32]) -> Result<Self, TensorError>
|
|
722
|
+
where
|
|
723
|
+
Scalar: FloatConvertible,
|
|
724
|
+
{
|
|
725
|
+
Self::try_from_scalars_in(scalars, Global)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/// Create a vector from per-dimension scalars.
|
|
729
|
+
pub fn try_from_dims(dims: &[Scalar::DimScalar]) -> Result<Self, TensorError>
|
|
730
|
+
where
|
|
731
|
+
Scalar: FloatConvertible,
|
|
732
|
+
{
|
|
733
|
+
Self::try_from_dims_in(dims, Global)
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Index for normal types (dimensions_per_value == 1)
|
|
738
|
+
impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator> core::ops::Index<AnyIndex>
|
|
739
|
+
for Vector<Scalar, Alloc>
|
|
740
|
+
{
|
|
741
|
+
type Output = Scalar;
|
|
742
|
+
|
|
743
|
+
#[inline]
|
|
744
|
+
fn index(&self, index: AnyIndex) -> &Scalar {
|
|
745
|
+
let i = index
|
|
746
|
+
.resolve(self.dims)
|
|
747
|
+
.expect("vector index out of bounds");
|
|
748
|
+
debug_assert_eq!(
|
|
749
|
+
Scalar::dimensions_per_value(),
|
|
750
|
+
1,
|
|
751
|
+
"Index trait not supported for sub-byte types"
|
|
752
|
+
);
|
|
753
|
+
unsafe { &*self.data.as_ptr().add(i) }
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator> core::ops::IndexMut<AnyIndex>
|
|
758
|
+
for Vector<Scalar, Alloc>
|
|
759
|
+
{
|
|
760
|
+
#[inline]
|
|
761
|
+
fn index_mut(&mut self, index: AnyIndex) -> &mut Scalar {
|
|
762
|
+
let i = index
|
|
763
|
+
.resolve(self.dims)
|
|
764
|
+
.expect("vector index out of bounds");
|
|
765
|
+
debug_assert_eq!(
|
|
766
|
+
Scalar::dimensions_per_value(),
|
|
767
|
+
1,
|
|
768
|
+
"IndexMut trait not supported for sub-byte types"
|
|
769
|
+
);
|
|
770
|
+
unsafe { &mut *self.data.as_ptr().add(i) }
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Vector<Scalar, Alloc> {
|
|
775
|
+
/// Try to clone this vector, returning an error on allocation failure.
|
|
776
|
+
pub fn try_clone(&self) -> Result<Self, TensorError> {
|
|
777
|
+
if self.values == 0 {
|
|
778
|
+
return Ok(Self {
|
|
779
|
+
data: NonNull::dangling(),
|
|
780
|
+
dims: 0,
|
|
781
|
+
values: 0,
|
|
782
|
+
alloc: self.alloc.clone(),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
let size = self.values * core::mem::size_of::<Scalar>();
|
|
786
|
+
let layout = alloc::alloc::Layout::from_size_align(size, SIMD_ALIGNMENT)
|
|
787
|
+
.map_err(|_| TensorError::AllocationFailed)?;
|
|
788
|
+
let ptr = self
|
|
789
|
+
.alloc
|
|
790
|
+
.allocate(layout)
|
|
791
|
+
.ok_or(TensorError::AllocationFailed)?;
|
|
792
|
+
unsafe {
|
|
793
|
+
core::ptr::copy_nonoverlapping(self.data.as_ptr() as *const u8, ptr.as_ptr(), size);
|
|
794
|
+
}
|
|
795
|
+
Ok(Self {
|
|
796
|
+
data: unsafe { NonNull::new_unchecked(ptr.as_ptr() as *mut Scalar) },
|
|
797
|
+
dims: self.dims,
|
|
798
|
+
values: self.values,
|
|
799
|
+
alloc: self.alloc.clone(),
|
|
800
|
+
})
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
impl<Scalar: StorageElement + Clone, Alloc: Allocator + Clone> Clone for Vector<Scalar, Alloc> {
|
|
805
|
+
fn clone(&self) -> Self {
|
|
806
|
+
self.try_clone().expect("vector clone allocation failed")
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
impl<Scalar: StorageElement> Default for Vector<Scalar, Global> {
|
|
811
|
+
fn default() -> Self {
|
|
812
|
+
Self {
|
|
813
|
+
data: NonNull::dangling(),
|
|
814
|
+
dims: 0,
|
|
815
|
+
values: 0,
|
|
816
|
+
alloc: Global,
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// endregion: Vector
|
|
822
|
+
|
|
823
|
+
// region: VectorView
|
|
824
|
+
|
|
825
|
+
/// Immutable, possibly strided, non-owning view into a vector.
|
|
826
|
+
///
|
|
827
|
+
/// `VectorView` is a zero-copy borrow: it holds a raw pointer, a dimension
|
|
828
|
+
/// count, and a byte stride between consecutive logical elements. It never
|
|
829
|
+
/// frees its memory and is tied to the lifetime `'a` of whatever owns the
|
|
830
|
+
/// storage (typically a [`Vector`] obtained via [`Vector::view`], or a row /
|
|
831
|
+
/// column of a tensor).
|
|
832
|
+
///
|
|
833
|
+
/// Because the stride is stored in bytes and may be negative, views can walk
|
|
834
|
+
/// memory in either direction, skip entries (see [`VectorView::try_strided`]),
|
|
835
|
+
/// or expose a reversed iteration order (see [`VectorView::rev`]).
|
|
836
|
+
///
|
|
837
|
+
/// Use [`VectorView`] when only read access is needed. Its mutable counterpart
|
|
838
|
+
/// is [`VectorSpan`], which offers the same striding semantics plus element
|
|
839
|
+
/// writes. Both types support the [`VectorIndex`] trait, so `view[0_usize]`,
|
|
840
|
+
/// `view[-1_i32]`, and friends all resolve via the same Python-style rules.
|
|
841
|
+
pub struct VectorView<'a, Scalar: StorageElement> {
|
|
842
|
+
data: *const Scalar,
|
|
843
|
+
dims: usize,
|
|
844
|
+
stride_bytes: isize,
|
|
845
|
+
_marker: PhantomData<&'a Scalar>,
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
unsafe impl<'a, Scalar: StorageElement + Sync> Send for VectorView<'a, Scalar> {}
|
|
849
|
+
unsafe impl<'a, Scalar: StorageElement + Sync> Sync for VectorView<'a, Scalar> {}
|
|
850
|
+
|
|
851
|
+
impl<'a, Scalar: StorageElement> Clone for VectorView<'a, Scalar> {
|
|
852
|
+
fn clone(&self) -> Self {
|
|
853
|
+
*self
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
impl<'a, Scalar: StorageElement> Copy for VectorView<'a, Scalar> {}
|
|
857
|
+
|
|
858
|
+
impl<'a, Scalar: StorageElement> VectorView<'a, Scalar> {
|
|
859
|
+
/// Create a view from a raw pointer, dimension count, and byte stride.
|
|
860
|
+
///
|
|
861
|
+
/// # Safety
|
|
862
|
+
/// - `data` must be valid for reads of `dims` elements at the given stride.
|
|
863
|
+
/// - The pointed-to memory must outlive `'a`.
|
|
864
|
+
/// - `stride_bytes` must be non-zero for non-empty views.
|
|
865
|
+
#[inline]
|
|
866
|
+
pub unsafe fn from_raw_parts(data: *const Scalar, dims: usize, stride_bytes: isize) -> Self {
|
|
867
|
+
Self {
|
|
868
|
+
data,
|
|
869
|
+
dims,
|
|
870
|
+
stride_bytes,
|
|
871
|
+
_marker: PhantomData,
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/// Number of logical dimensions.
|
|
876
|
+
#[inline]
|
|
877
|
+
pub fn dims(&self) -> usize {
|
|
878
|
+
self.dims
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/// Number of logical dimensions (alias for dims).
|
|
882
|
+
#[inline]
|
|
883
|
+
pub fn size(&self) -> usize {
|
|
884
|
+
self.dims
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/// Returns true if empty.
|
|
888
|
+
#[inline]
|
|
889
|
+
pub fn is_empty(&self) -> bool {
|
|
890
|
+
self.dims == 0
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/// Stride in bytes between consecutive elements.
|
|
894
|
+
#[inline]
|
|
895
|
+
pub fn stride_bytes(&self) -> isize {
|
|
896
|
+
self.stride_bytes
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/// Returns true if elements are stored contiguously (stride == sizeof(Scalar)).
|
|
900
|
+
#[inline]
|
|
901
|
+
pub fn is_contiguous(&self) -> bool {
|
|
902
|
+
self.stride_bytes == core::mem::size_of::<Scalar>() as isize
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/// Get the underlying pointer.
|
|
906
|
+
#[inline]
|
|
907
|
+
pub fn as_ptr(&self) -> *const Scalar {
|
|
908
|
+
self.data
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/// Get a contiguous slice, if this view is contiguous.
|
|
912
|
+
#[inline]
|
|
913
|
+
pub fn as_contiguous_slice(&self) -> Option<&'a [Scalar]> {
|
|
914
|
+
if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
|
|
915
|
+
Some(unsafe { core::slice::from_raw_parts(self.data, self.dims) })
|
|
916
|
+
} else {
|
|
917
|
+
None
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/// Try to get element at index (supports signed indexing).
|
|
922
|
+
///
|
|
923
|
+
/// Returns the native `DimScalar` type. For sub-byte types, uses value_index
|
|
924
|
+
/// for stride-based pointer walks to avoid buffer overread.
|
|
925
|
+
#[inline]
|
|
926
|
+
pub fn try_get<AnyIndex: VectorIndex>(
|
|
927
|
+
&self,
|
|
928
|
+
index: AnyIndex,
|
|
929
|
+
) -> Result<Scalar::DimScalar, TensorError>
|
|
930
|
+
where
|
|
931
|
+
Scalar: FloatConvertible,
|
|
932
|
+
{
|
|
933
|
+
let i = index
|
|
934
|
+
.resolve(self.dims)
|
|
935
|
+
.ok_or(TensorError::IndexOutOfBounds {
|
|
936
|
+
index: 0,
|
|
937
|
+
size: self.dims,
|
|
938
|
+
})?;
|
|
939
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
940
|
+
let value_index = i / dims_per_value;
|
|
941
|
+
let sub_index = i % dims_per_value;
|
|
942
|
+
// SAFETY: stride * value_index stays within allocation
|
|
943
|
+
let ptr = unsafe {
|
|
944
|
+
(self.data as *const u8).offset(self.stride_bytes * value_index as isize)
|
|
945
|
+
as *const Scalar
|
|
946
|
+
};
|
|
947
|
+
Ok(unsafe { *ptr }.unpack().as_ref()[sub_index])
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/// Create a reversed view by negating the stride and pointing to the last element.
|
|
951
|
+
///
|
|
952
|
+
/// The returned view has the same number of dimensions but iterates in the
|
|
953
|
+
/// opposite direction. For an empty view, returns a copy unchanged.
|
|
954
|
+
pub fn rev(&self) -> Self {
|
|
955
|
+
if self.dims == 0 {
|
|
956
|
+
return *self;
|
|
957
|
+
}
|
|
958
|
+
let last_offset = self.stride_bytes * (self.dims as isize - 1);
|
|
959
|
+
Self {
|
|
960
|
+
data: unsafe { (self.data as *const u8).offset(last_offset) as *const Scalar },
|
|
961
|
+
dims: self.dims,
|
|
962
|
+
stride_bytes: -self.stride_bytes,
|
|
963
|
+
_marker: PhantomData,
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/// Create a sub-view with start, end, and step (Python-style slicing).
|
|
968
|
+
///
|
|
969
|
+
/// Supports negative steps for reverse iteration. `step` must be non-zero.
|
|
970
|
+
/// Returns an error if `start` or `end` exceed `dims()`, or if `step == 0`.
|
|
971
|
+
pub fn try_strided(&self, start: usize, end: usize, step: isize) -> Result<Self, TensorError> {
|
|
972
|
+
if start > self.dims || end > self.dims || step == 0 {
|
|
973
|
+
return Err(TensorError::IndexOutOfBounds {
|
|
974
|
+
index: start.max(end),
|
|
975
|
+
size: self.dims,
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
let count = if step > 0 {
|
|
979
|
+
if end > start {
|
|
980
|
+
(end - start + step as usize - 1) / step as usize
|
|
981
|
+
} else {
|
|
982
|
+
0
|
|
983
|
+
}
|
|
984
|
+
} else if start > end {
|
|
985
|
+
let abs_step = (-step) as usize;
|
|
986
|
+
(start - end + abs_step - 1) / abs_step
|
|
987
|
+
} else {
|
|
988
|
+
0
|
|
989
|
+
};
|
|
990
|
+
let new_data = unsafe {
|
|
991
|
+
(self.data as *const u8).offset(self.stride_bytes * start as isize) as *const Scalar
|
|
992
|
+
};
|
|
993
|
+
Ok(Self {
|
|
994
|
+
data: new_data,
|
|
995
|
+
dims: count,
|
|
996
|
+
stride_bytes: self.stride_bytes * step,
|
|
997
|
+
_marker: PhantomData,
|
|
998
|
+
})
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/// Returns an iterator over logical dimension values, yielding [`DimRef`] proxies.
|
|
1002
|
+
pub fn iter(&self) -> VectorViewIterator<'a, Scalar>
|
|
1003
|
+
where
|
|
1004
|
+
Scalar: FloatConvertible,
|
|
1005
|
+
{
|
|
1006
|
+
VectorViewIterator {
|
|
1007
|
+
data: self.data,
|
|
1008
|
+
stride_bytes: self.stride_bytes,
|
|
1009
|
+
front: 0,
|
|
1010
|
+
back: self.dims,
|
|
1011
|
+
_marker: PhantomData,
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::Index<AnyIndex>
|
|
1017
|
+
for VectorView<'a, Scalar>
|
|
1018
|
+
{
|
|
1019
|
+
type Output = Scalar;
|
|
1020
|
+
|
|
1021
|
+
#[inline]
|
|
1022
|
+
fn index(&self, index: AnyIndex) -> &Scalar {
|
|
1023
|
+
let i = index.resolve(self.dims).expect("view index out of bounds");
|
|
1024
|
+
debug_assert_eq!(
|
|
1025
|
+
Scalar::dimensions_per_value(),
|
|
1026
|
+
1,
|
|
1027
|
+
"Index trait not supported for sub-byte types"
|
|
1028
|
+
);
|
|
1029
|
+
unsafe {
|
|
1030
|
+
&*((self.data as *const u8).offset(self.stride_bytes * i as isize) as *const Scalar)
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// endregion: VectorView
|
|
1036
|
+
|
|
1037
|
+
// region: VectorSpan
|
|
1038
|
+
|
|
1039
|
+
/// Mutable, possibly strided, non-owning view into a vector.
|
|
1040
|
+
///
|
|
1041
|
+
/// `VectorSpan` is the read-write counterpart of [`VectorView`]: it still
|
|
1042
|
+
/// performs zero-copy borrowing via a raw pointer, dimension count, and byte
|
|
1043
|
+
/// stride, but additionally allows element writes through `try_set`, `fill`,
|
|
1044
|
+
/// `iter_mut`, and `IndexMut`. Spans are typically obtained via
|
|
1045
|
+
/// [`Vector::span`] or a mutable slice of a tensor row.
|
|
1046
|
+
///
|
|
1047
|
+
/// Like views, spans support signed ([`VectorIndex`]) indexing, negative
|
|
1048
|
+
/// strides via [`VectorSpan::as_view`] + [`VectorView::rev`], and Python-style
|
|
1049
|
+
/// slicing through their view projection. The `'a` lifetime mirrors the owner
|
|
1050
|
+
/// of the underlying memory, and Rust's borrow checker prevents aliasing a
|
|
1051
|
+
/// single span with any other reference for the duration of `'a`.
|
|
1052
|
+
pub struct VectorSpan<'a, Scalar: StorageElement> {
|
|
1053
|
+
data: *mut Scalar,
|
|
1054
|
+
dims: usize,
|
|
1055
|
+
stride_bytes: isize,
|
|
1056
|
+
_marker: PhantomData<&'a mut Scalar>,
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
unsafe impl<'a, Scalar: StorageElement + Send> Send for VectorSpan<'a, Scalar> {}
|
|
1060
|
+
unsafe impl<'a, Scalar: StorageElement + Sync> Sync for VectorSpan<'a, Scalar> {}
|
|
1061
|
+
|
|
1062
|
+
impl<'a, Scalar: StorageElement> VectorSpan<'a, Scalar> {
|
|
1063
|
+
/// Create a mutable view from a raw pointer, dimension count, and byte stride.
|
|
1064
|
+
///
|
|
1065
|
+
/// # Safety
|
|
1066
|
+
/// - `data` must be valid for reads and writes of `dims` elements at the given stride.
|
|
1067
|
+
/// - The pointed-to memory must outlive `'a`.
|
|
1068
|
+
/// - `stride_bytes` must be non-zero for non-empty views.
|
|
1069
|
+
/// - No other references to the memory may exist for the duration of `'a`.
|
|
1070
|
+
#[inline]
|
|
1071
|
+
pub unsafe fn from_raw_parts(data: *mut Scalar, dims: usize, stride_bytes: isize) -> Self {
|
|
1072
|
+
Self {
|
|
1073
|
+
data,
|
|
1074
|
+
dims,
|
|
1075
|
+
stride_bytes,
|
|
1076
|
+
_marker: PhantomData,
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/// Number of logical dimensions.
|
|
1081
|
+
#[inline]
|
|
1082
|
+
pub fn dims(&self) -> usize {
|
|
1083
|
+
self.dims
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/// Number of logical dimensions (alias for dims).
|
|
1087
|
+
#[inline]
|
|
1088
|
+
pub fn size(&self) -> usize {
|
|
1089
|
+
self.dims
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/// Returns true if empty.
|
|
1093
|
+
#[inline]
|
|
1094
|
+
pub fn is_empty(&self) -> bool {
|
|
1095
|
+
self.dims == 0
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/// Stride in bytes.
|
|
1099
|
+
#[inline]
|
|
1100
|
+
pub fn stride_bytes(&self) -> isize {
|
|
1101
|
+
self.stride_bytes
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/// Returns true if contiguous.
|
|
1105
|
+
#[inline]
|
|
1106
|
+
pub fn is_contiguous(&self) -> bool {
|
|
1107
|
+
self.stride_bytes == core::mem::size_of::<Scalar>() as isize
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/// Get the underlying pointer.
|
|
1111
|
+
#[inline]
|
|
1112
|
+
pub fn as_ptr(&self) -> *const Scalar {
|
|
1113
|
+
self.data
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/// Get the mutable underlying pointer.
|
|
1117
|
+
#[inline]
|
|
1118
|
+
pub fn as_mut_ptr(&mut self) -> *mut Scalar {
|
|
1119
|
+
self.data
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/// Reborrow as an immutable view, sharing the same data pointer and stride.
|
|
1123
|
+
pub fn as_view(&self) -> VectorView<'_, Scalar> {
|
|
1124
|
+
VectorView {
|
|
1125
|
+
data: self.data,
|
|
1126
|
+
dims: self.dims,
|
|
1127
|
+
stride_bytes: self.stride_bytes,
|
|
1128
|
+
_marker: PhantomData,
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/// Try to get element at index.
|
|
1133
|
+
#[inline]
|
|
1134
|
+
pub fn try_get<AnyIndex: VectorIndex>(
|
|
1135
|
+
&self,
|
|
1136
|
+
index: AnyIndex,
|
|
1137
|
+
) -> Result<Scalar::DimScalar, TensorError>
|
|
1138
|
+
where
|
|
1139
|
+
Scalar: FloatConvertible,
|
|
1140
|
+
{
|
|
1141
|
+
self.as_view().try_get(index)
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/// Try to set the element at `index`.
|
|
1145
|
+
///
|
|
1146
|
+
/// Accepts the native `DimScalar` type. For sub-byte types, uses value_index
|
|
1147
|
+
/// for stride-based pointer walks to avoid buffer overwrite.
|
|
1148
|
+
#[inline]
|
|
1149
|
+
pub fn try_set<AnyIndex: VectorIndex>(
|
|
1150
|
+
&mut self,
|
|
1151
|
+
index: AnyIndex,
|
|
1152
|
+
value: Scalar::DimScalar,
|
|
1153
|
+
) -> Result<(), TensorError>
|
|
1154
|
+
where
|
|
1155
|
+
Scalar: FloatConvertible,
|
|
1156
|
+
{
|
|
1157
|
+
let i = index
|
|
1158
|
+
.resolve(self.dims)
|
|
1159
|
+
.ok_or(TensorError::IndexOutOfBounds {
|
|
1160
|
+
index: 0,
|
|
1161
|
+
size: self.dims,
|
|
1162
|
+
})?;
|
|
1163
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1164
|
+
let value_index = i / dims_per_value;
|
|
1165
|
+
let sub_index = i % dims_per_value;
|
|
1166
|
+
// SAFETY: stride * value_index stays within allocation
|
|
1167
|
+
let ptr = unsafe {
|
|
1168
|
+
(self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
|
|
1169
|
+
};
|
|
1170
|
+
let mut unpacked = unsafe { *ptr }.unpack();
|
|
1171
|
+
unpacked.as_mut()[sub_index] = value;
|
|
1172
|
+
unsafe { ptr.write(Scalar::pack(unpacked)) };
|
|
1173
|
+
Ok(())
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/// Fill all elements with a value, respecting the stride between elements.
|
|
1177
|
+
///
|
|
1178
|
+
/// Iterates over storage values (not logical dimensions) to avoid buffer
|
|
1179
|
+
/// overwrite for sub-byte types.
|
|
1180
|
+
pub fn fill(&mut self, value: Scalar) {
|
|
1181
|
+
let values = self.dims.div_ceil(Scalar::dimensions_per_value());
|
|
1182
|
+
for i in 0..values {
|
|
1183
|
+
// SAFETY: i < values; stride * i stays within the allocation
|
|
1184
|
+
let ptr = unsafe {
|
|
1185
|
+
(self.data as *mut u8).offset(self.stride_bytes * i as isize) as *mut Scalar
|
|
1186
|
+
};
|
|
1187
|
+
unsafe { ptr.write(value) };
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/// Get a contiguous slice, if this span is contiguous.
|
|
1192
|
+
#[inline]
|
|
1193
|
+
pub fn as_contiguous_slice(&self) -> Option<&[Scalar]> {
|
|
1194
|
+
if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
|
|
1195
|
+
Some(unsafe { core::slice::from_raw_parts(self.data, self.dims) })
|
|
1196
|
+
} else {
|
|
1197
|
+
None
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/// Get a mutable contiguous slice, if this span is contiguous.
|
|
1202
|
+
#[inline]
|
|
1203
|
+
pub fn as_contiguous_slice_mut(&mut self) -> Option<&mut [Scalar]> {
|
|
1204
|
+
if self.is_contiguous() && Scalar::dimensions_per_value() == 1 {
|
|
1205
|
+
Some(unsafe { core::slice::from_raw_parts_mut(self.data, self.dims) })
|
|
1206
|
+
} else {
|
|
1207
|
+
None
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/// Returns an iterator over logical dimension values, yielding [`DimRef`] proxies.
|
|
1212
|
+
pub fn iter(&self) -> VectorViewIterator<'_, Scalar>
|
|
1213
|
+
where
|
|
1214
|
+
Scalar: FloatConvertible,
|
|
1215
|
+
{
|
|
1216
|
+
VectorViewIterator {
|
|
1217
|
+
data: self.data,
|
|
1218
|
+
stride_bytes: self.stride_bytes,
|
|
1219
|
+
front: 0,
|
|
1220
|
+
back: self.dims,
|
|
1221
|
+
_marker: PhantomData,
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/// Returns a mutable iterator over logical dimension values, yielding [`DimMut`] proxies.
|
|
1226
|
+
pub fn iter_mut(&mut self) -> VectorSpanIterator<'_, Scalar>
|
|
1227
|
+
where
|
|
1228
|
+
Scalar: FloatConvertible,
|
|
1229
|
+
{
|
|
1230
|
+
VectorSpanIterator {
|
|
1231
|
+
data: self.data,
|
|
1232
|
+
stride_bytes: self.stride_bytes,
|
|
1233
|
+
front: 0,
|
|
1234
|
+
back: self.dims,
|
|
1235
|
+
_marker: PhantomData,
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::Index<AnyIndex>
|
|
1241
|
+
for VectorSpan<'a, Scalar>
|
|
1242
|
+
{
|
|
1243
|
+
type Output = Scalar;
|
|
1244
|
+
|
|
1245
|
+
#[inline]
|
|
1246
|
+
fn index(&self, index: AnyIndex) -> &Scalar {
|
|
1247
|
+
let i = index.resolve(self.dims).expect("span index out of bounds");
|
|
1248
|
+
debug_assert_eq!(
|
|
1249
|
+
Scalar::dimensions_per_value(),
|
|
1250
|
+
1,
|
|
1251
|
+
"Index trait not supported for sub-byte types"
|
|
1252
|
+
);
|
|
1253
|
+
unsafe {
|
|
1254
|
+
&*((self.data as *const u8).offset(self.stride_bytes * i as isize) as *const Scalar)
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
impl<'a, AnyIndex: VectorIndex, Scalar: StorageElement> core::ops::IndexMut<AnyIndex>
|
|
1260
|
+
for VectorSpan<'a, Scalar>
|
|
1261
|
+
{
|
|
1262
|
+
#[inline]
|
|
1263
|
+
fn index_mut(&mut self, index: AnyIndex) -> &mut Scalar {
|
|
1264
|
+
let i = index.resolve(self.dims).expect("span index out of bounds");
|
|
1265
|
+
debug_assert_eq!(
|
|
1266
|
+
Scalar::dimensions_per_value(),
|
|
1267
|
+
1,
|
|
1268
|
+
"IndexMut trait not supported for sub-byte types"
|
|
1269
|
+
);
|
|
1270
|
+
unsafe {
|
|
1271
|
+
&mut *((self.data as *mut u8).offset(self.stride_bytes * i as isize) as *mut Scalar)
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// endregion: VectorSpan
|
|
1277
|
+
|
|
1278
|
+
// region: Iterators
|
|
1279
|
+
|
|
1280
|
+
/// Stride-aware immutable iterator over logical dimensions, yielding [`DimRef`] proxies.
|
|
1281
|
+
///
|
|
1282
|
+
/// For normal types (`dimensions_per_value=1`), yields one proxy per storage value.
|
|
1283
|
+
/// For sub-byte types, unpacks each storage value and yields sub-dimensions individually.
|
|
1284
|
+
/// Implements `ExactSizeIterator`, `FusedIterator`, and `DoubleEndedIterator`.
|
|
1285
|
+
pub struct VectorViewIterator<'a, Scalar: FloatConvertible> {
|
|
1286
|
+
data: *const Scalar,
|
|
1287
|
+
stride_bytes: isize,
|
|
1288
|
+
front: usize,
|
|
1289
|
+
back: usize,
|
|
1290
|
+
_marker: PhantomData<&'a Scalar>,
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/// Backward-compatible alias for [`VectorViewIterator`].
|
|
1294
|
+
pub type VectorIterator<'a, Scalar> = VectorViewIterator<'a, Scalar>;
|
|
1295
|
+
|
|
1296
|
+
impl<'a, Scalar: FloatConvertible> Iterator for VectorViewIterator<'a, Scalar> {
|
|
1297
|
+
type Item = DimRef<'a, Scalar>;
|
|
1298
|
+
|
|
1299
|
+
#[inline]
|
|
1300
|
+
fn next(&mut self) -> Option<DimRef<'a, Scalar>> {
|
|
1301
|
+
if self.front >= self.back {
|
|
1302
|
+
return None;
|
|
1303
|
+
}
|
|
1304
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1305
|
+
let value_index = self.front / dims_per_value;
|
|
1306
|
+
let sub_index = self.front % dims_per_value;
|
|
1307
|
+
// SAFETY: value_index < values, stride * value_index within allocation
|
|
1308
|
+
let ptr = unsafe {
|
|
1309
|
+
(self.data as *const u8).offset(self.stride_bytes * value_index as isize)
|
|
1310
|
+
as *const Scalar
|
|
1311
|
+
};
|
|
1312
|
+
let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
|
|
1313
|
+
self.front += 1;
|
|
1314
|
+
Some(DimRef::new(scalar))
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
#[inline]
|
|
1318
|
+
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
1319
|
+
let element_count = self.back - self.front;
|
|
1320
|
+
(element_count, Some(element_count))
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
impl<'a, Scalar: FloatConvertible> ExactSizeIterator for VectorViewIterator<'a, Scalar> {}
|
|
1325
|
+
impl<'a, Scalar: FloatConvertible> core::iter::FusedIterator for VectorViewIterator<'a, Scalar> {}
|
|
1326
|
+
|
|
1327
|
+
impl<'a, Scalar: FloatConvertible> DoubleEndedIterator for VectorViewIterator<'a, Scalar> {
|
|
1328
|
+
#[inline]
|
|
1329
|
+
fn next_back(&mut self) -> Option<DimRef<'a, Scalar>> {
|
|
1330
|
+
if self.front >= self.back {
|
|
1331
|
+
return None;
|
|
1332
|
+
}
|
|
1333
|
+
self.back -= 1;
|
|
1334
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1335
|
+
let value_index = self.back / dims_per_value;
|
|
1336
|
+
let sub_index = self.back % dims_per_value;
|
|
1337
|
+
let ptr = unsafe {
|
|
1338
|
+
(self.data as *const u8).offset(self.stride_bytes * value_index as isize)
|
|
1339
|
+
as *const Scalar
|
|
1340
|
+
};
|
|
1341
|
+
Some(DimRef::new(unsafe { *ptr }.unpack().as_ref()[sub_index]))
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/// Stride-aware mutable iterator over logical dimensions, yielding [`DimMut`] proxies.
|
|
1346
|
+
///
|
|
1347
|
+
/// For normal types (`dimensions_per_value=1`), yields one proxy per storage value.
|
|
1348
|
+
/// For sub-byte types, each proxy performs a read-modify-write on drop.
|
|
1349
|
+
/// Implements `ExactSizeIterator`, `FusedIterator`, and `DoubleEndedIterator`.
|
|
1350
|
+
pub struct VectorSpanIterator<'a, Scalar: FloatConvertible> {
|
|
1351
|
+
data: *mut Scalar,
|
|
1352
|
+
stride_bytes: isize,
|
|
1353
|
+
front: usize,
|
|
1354
|
+
back: usize,
|
|
1355
|
+
_marker: PhantomData<&'a mut Scalar>,
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
impl<'a, Scalar: FloatConvertible> Iterator for VectorSpanIterator<'a, Scalar> {
|
|
1359
|
+
type Item = DimMut<'a, Scalar>;
|
|
1360
|
+
|
|
1361
|
+
#[inline]
|
|
1362
|
+
fn next(&mut self) -> Option<DimMut<'a, Scalar>> {
|
|
1363
|
+
if self.front >= self.back {
|
|
1364
|
+
return None;
|
|
1365
|
+
}
|
|
1366
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1367
|
+
let value_index = self.front / dims_per_value;
|
|
1368
|
+
let sub_index = self.front % dims_per_value;
|
|
1369
|
+
let ptr = unsafe {
|
|
1370
|
+
(self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
|
|
1371
|
+
};
|
|
1372
|
+
let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
|
|
1373
|
+
self.front += 1;
|
|
1374
|
+
Some(unsafe { DimMut::new(ptr, sub_index, scalar) })
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
#[inline]
|
|
1378
|
+
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
1379
|
+
let element_count = self.back - self.front;
|
|
1380
|
+
(element_count, Some(element_count))
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
impl<'a, Scalar: FloatConvertible> ExactSizeIterator for VectorSpanIterator<'a, Scalar> {}
|
|
1385
|
+
impl<'a, Scalar: FloatConvertible> core::iter::FusedIterator for VectorSpanIterator<'a, Scalar> {}
|
|
1386
|
+
|
|
1387
|
+
impl<'a, Scalar: FloatConvertible> DoubleEndedIterator for VectorSpanIterator<'a, Scalar> {
|
|
1388
|
+
#[inline]
|
|
1389
|
+
fn next_back(&mut self) -> Option<DimMut<'a, Scalar>> {
|
|
1390
|
+
if self.front >= self.back {
|
|
1391
|
+
return None;
|
|
1392
|
+
}
|
|
1393
|
+
self.back -= 1;
|
|
1394
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1395
|
+
let value_index = self.back / dims_per_value;
|
|
1396
|
+
let sub_index = self.back % dims_per_value;
|
|
1397
|
+
let ptr = unsafe {
|
|
1398
|
+
(self.data as *mut u8).offset(self.stride_bytes * value_index as isize) as *mut Scalar
|
|
1399
|
+
};
|
|
1400
|
+
let scalar = unsafe { *ptr }.unpack().as_ref()[sub_index];
|
|
1401
|
+
Some(unsafe { DimMut::new(ptr, sub_index, scalar) })
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// endregion: Iterators
|
|
1406
|
+
|
|
1407
|
+
// region: IntoIterator (immutable)
|
|
1408
|
+
|
|
1409
|
+
impl<'a, Scalar: FloatConvertible, Alloc: Allocator> IntoIterator for &'a Vector<Scalar, Alloc> {
|
|
1410
|
+
type Item = DimRef<'a, Scalar>;
|
|
1411
|
+
type IntoIter = VectorViewIterator<'a, Scalar>;
|
|
1412
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
1413
|
+
self.iter()
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
impl<'a, Scalar: FloatConvertible> IntoIterator for &'a VectorView<'a, Scalar> {
|
|
1418
|
+
type Item = DimRef<'a, Scalar>;
|
|
1419
|
+
type IntoIter = VectorViewIterator<'a, Scalar>;
|
|
1420
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
1421
|
+
self.iter()
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
impl<'a, Scalar: FloatConvertible> IntoIterator for &'a VectorSpan<'a, Scalar> {
|
|
1426
|
+
type Item = DimRef<'a, Scalar>;
|
|
1427
|
+
type IntoIter = VectorViewIterator<'a, Scalar>;
|
|
1428
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
1429
|
+
self.iter()
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// endregion: IntoIterator (immutable)
|
|
1434
|
+
|
|
1435
|
+
// region: IntoIterator (mutable)
|
|
1436
|
+
|
|
1437
|
+
impl<'a, Scalar: FloatConvertible, Alloc: Allocator> IntoIterator
|
|
1438
|
+
for &'a mut Vector<Scalar, Alloc>
|
|
1439
|
+
{
|
|
1440
|
+
type Item = DimMut<'a, Scalar>;
|
|
1441
|
+
type IntoIter = VectorSpanIterator<'a, Scalar>;
|
|
1442
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
1443
|
+
self.iter_mut()
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
impl<'a, Scalar: FloatConvertible> IntoIterator for &'a mut VectorSpan<'a, Scalar> {
|
|
1448
|
+
type Item = DimMut<'a, Scalar>;
|
|
1449
|
+
type IntoIter = VectorSpanIterator<'a, Scalar>;
|
|
1450
|
+
fn into_iter(self) -> Self::IntoIter {
|
|
1451
|
+
self.iter_mut()
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
// endregion: IntoIterator (mutable)
|
|
1456
|
+
|
|
1457
|
+
// region: AsRef
|
|
1458
|
+
|
|
1459
|
+
impl<Scalar: StorageElement, Alloc: Allocator> AsRef<[Scalar]> for Vector<Scalar, Alloc> {
|
|
1460
|
+
fn as_ref(&self) -> &[Scalar] {
|
|
1461
|
+
self.as_slice()
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// endregion: AsRef
|
|
1466
|
+
|
|
1467
|
+
// region: PartialEq
|
|
1468
|
+
|
|
1469
|
+
impl<Scalar: FloatConvertible, Alloc: Allocator> PartialEq for Vector<Scalar, Alloc>
|
|
1470
|
+
where
|
|
1471
|
+
Scalar::DimScalar: PartialEq,
|
|
1472
|
+
{
|
|
1473
|
+
fn eq(&self, other: &Self) -> bool {
|
|
1474
|
+
self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
impl<Scalar: FloatConvertible, Alloc: Allocator> PartialEq<[Scalar::DimScalar]>
|
|
1479
|
+
for Vector<Scalar, Alloc>
|
|
1480
|
+
where
|
|
1481
|
+
Scalar::DimScalar: PartialEq,
|
|
1482
|
+
{
|
|
1483
|
+
fn eq(&self, other: &[Scalar::DimScalar]) -> bool {
|
|
1484
|
+
self.dims == other.len() && self.iter().zip(other.iter()).all(|(a, b)| *a == *b)
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
impl<'a, Scalar: FloatConvertible> PartialEq for VectorView<'a, Scalar>
|
|
1489
|
+
where
|
|
1490
|
+
Scalar::DimScalar: PartialEq,
|
|
1491
|
+
{
|
|
1492
|
+
fn eq(&self, other: &Self) -> bool {
|
|
1493
|
+
self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
impl<'a, Scalar: FloatConvertible> PartialEq for VectorSpan<'a, Scalar>
|
|
1498
|
+
where
|
|
1499
|
+
Scalar::DimScalar: PartialEq,
|
|
1500
|
+
{
|
|
1501
|
+
fn eq(&self, other: &Self) -> bool {
|
|
1502
|
+
self.dims == other.dims && self.iter().zip(other.iter()).all(|(a, b)| a == b)
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// endregion: PartialEq
|
|
1507
|
+
|
|
1508
|
+
// region: Tolerance Equality
|
|
1509
|
+
|
|
1510
|
+
impl<Scalar: FloatConvertible, Alloc: Allocator> Vector<Scalar, Alloc>
|
|
1511
|
+
where
|
|
1512
|
+
Scalar::DimScalar: NumberLike,
|
|
1513
|
+
{
|
|
1514
|
+
/// Check if all elements are within tolerance of `other`.
|
|
1515
|
+
///
|
|
1516
|
+
/// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
|
|
1517
|
+
/// Returns `false` if dimensions differ.
|
|
1518
|
+
pub fn allclose<OtherAlloc: Allocator>(
|
|
1519
|
+
&self,
|
|
1520
|
+
other: &Vector<Scalar, OtherAlloc>,
|
|
1521
|
+
atol: f64,
|
|
1522
|
+
rtol: f64,
|
|
1523
|
+
) -> bool {
|
|
1524
|
+
self.dims == other.dims
|
|
1525
|
+
&& self
|
|
1526
|
+
.iter()
|
|
1527
|
+
.zip(other.iter())
|
|
1528
|
+
.all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
impl<'a, Scalar: FloatConvertible> VectorView<'a, Scalar>
|
|
1533
|
+
where
|
|
1534
|
+
Scalar::DimScalar: NumberLike,
|
|
1535
|
+
{
|
|
1536
|
+
/// Check if all elements are within tolerance of `other`.
|
|
1537
|
+
///
|
|
1538
|
+
/// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
|
|
1539
|
+
/// Returns `false` if dimensions differ.
|
|
1540
|
+
pub fn allclose(&self, other: &Self, atol: f64, rtol: f64) -> bool {
|
|
1541
|
+
self.dims == other.dims
|
|
1542
|
+
&& self
|
|
1543
|
+
.iter()
|
|
1544
|
+
.zip(other.iter())
|
|
1545
|
+
.all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
impl<'a, Scalar: FloatConvertible> VectorSpan<'a, Scalar>
|
|
1550
|
+
where
|
|
1551
|
+
Scalar::DimScalar: NumberLike,
|
|
1552
|
+
{
|
|
1553
|
+
/// Check if all elements are within tolerance of `other`.
|
|
1554
|
+
///
|
|
1555
|
+
/// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
|
|
1556
|
+
/// Returns `false` if dimensions differ.
|
|
1557
|
+
pub fn allclose(&self, other: &Self, atol: f64, rtol: f64) -> bool {
|
|
1558
|
+
self.dims == other.dims
|
|
1559
|
+
&& self
|
|
1560
|
+
.iter()
|
|
1561
|
+
.zip(other.iter())
|
|
1562
|
+
.all(|(a, b)| crate::types::is_close(a.to_f64(), b.to_f64(), atol, rtol))
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// endregion: Tolerance Equality
|
|
1567
|
+
|
|
1568
|
+
// region: Debug and Display
|
|
1569
|
+
|
|
1570
|
+
/// Write a truncated, debug-formatted list from an iterator.
|
|
1571
|
+
fn fmt_debug_list<I: Iterator>(
|
|
1572
|
+
f: &mut core::fmt::Formatter<'_>,
|
|
1573
|
+
name: &str,
|
|
1574
|
+
dims: usize,
|
|
1575
|
+
iter: I,
|
|
1576
|
+
limit: usize,
|
|
1577
|
+
) -> core::fmt::Result
|
|
1578
|
+
where
|
|
1579
|
+
I::Item: core::fmt::Debug,
|
|
1580
|
+
{
|
|
1581
|
+
write!(f, "{}(dims={}, [", name, dims)?;
|
|
1582
|
+
for (i, value) in iter.enumerate() {
|
|
1583
|
+
if i >= limit {
|
|
1584
|
+
write!(f, ", ...")?;
|
|
1585
|
+
break;
|
|
1586
|
+
}
|
|
1587
|
+
if i > 0 {
|
|
1588
|
+
write!(f, ", ")?;
|
|
1589
|
+
}
|
|
1590
|
+
write!(f, "{:?}", value)?;
|
|
1591
|
+
}
|
|
1592
|
+
write!(f, "])")
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/// Write a truncated, display-formatted list from an iterator.
|
|
1596
|
+
fn fmt_display_list<I: Iterator>(
|
|
1597
|
+
f: &mut core::fmt::Formatter<'_>,
|
|
1598
|
+
iter: I,
|
|
1599
|
+
limit: usize,
|
|
1600
|
+
) -> core::fmt::Result
|
|
1601
|
+
where
|
|
1602
|
+
I::Item: core::fmt::Display,
|
|
1603
|
+
{
|
|
1604
|
+
let prec = f.precision();
|
|
1605
|
+
write!(f, "[")?;
|
|
1606
|
+
for (i, value) in iter.enumerate() {
|
|
1607
|
+
if i >= limit {
|
|
1608
|
+
write!(f, ", ...")?;
|
|
1609
|
+
break;
|
|
1610
|
+
}
|
|
1611
|
+
if i > 0 {
|
|
1612
|
+
write!(f, ", ")?;
|
|
1613
|
+
}
|
|
1614
|
+
if let Some(p) = prec {
|
|
1615
|
+
write!(f, "{:.p$}", value)?;
|
|
1616
|
+
} else {
|
|
1617
|
+
write!(f, "{}", value)?;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
write!(f, "]")
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
impl<Scalar: FloatConvertible, Alloc: Allocator> core::fmt::Debug for Vector<Scalar, Alloc>
|
|
1624
|
+
where
|
|
1625
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
1626
|
+
{
|
|
1627
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1628
|
+
fmt_debug_list(f, "Vector", self.dims, self.iter(), 8)
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
impl<'a, Scalar: FloatConvertible> core::fmt::Debug for VectorView<'a, Scalar>
|
|
1633
|
+
where
|
|
1634
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
1635
|
+
{
|
|
1636
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1637
|
+
fmt_debug_list(f, "VectorView", self.dims, self.iter(), 8)
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
impl<'a, Scalar: FloatConvertible> core::fmt::Debug for VectorSpan<'a, Scalar>
|
|
1642
|
+
where
|
|
1643
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
1644
|
+
{
|
|
1645
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1646
|
+
fmt_debug_list(f, "VectorSpan", self.dims, self.iter(), 8)
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
impl<Scalar: FloatConvertible, Alloc: Allocator> core::fmt::Display for Vector<Scalar, Alloc>
|
|
1651
|
+
where
|
|
1652
|
+
Scalar::DimScalar: core::fmt::Display,
|
|
1653
|
+
{
|
|
1654
|
+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
1655
|
+
fmt_display_list(f, self.iter(), 20)
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// endregion: Debug and Display
|
|
1660
|
+
|
|
1661
|
+
// region: Tests
|
|
1662
|
+
|
|
1663
|
+
#[cfg(test)]
|
|
1664
|
+
mod tests {
|
|
1665
|
+
use super::*;
|
|
1666
|
+
use crate::types::{bf16, f16, i4x2, u1x8, u4x2};
|
|
1667
|
+
|
|
1668
|
+
fn check_vector_roundtrip<Scalar: FloatConvertible>() {
|
|
1669
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1670
|
+
let test_dims = 16 * dims_per_value;
|
|
1671
|
+
let v = Vector::<Scalar>::try_zeros(test_dims).unwrap();
|
|
1672
|
+
assert_eq!(v.dims(), test_dims);
|
|
1673
|
+
assert_eq!(v.size_values(), test_dims / dims_per_value);
|
|
1674
|
+
let count = v.iter().count();
|
|
1675
|
+
assert_eq!(count, test_dims);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
fn check_vector_try_get_set<Scalar: FloatConvertible>()
|
|
1679
|
+
where
|
|
1680
|
+
Scalar::DimScalar: core::fmt::Debug,
|
|
1681
|
+
{
|
|
1682
|
+
let dims_per_value = Scalar::dimensions_per_value();
|
|
1683
|
+
let test_dims = 4 * dims_per_value;
|
|
1684
|
+
let mut v = Vector::<Scalar>::try_zeros(test_dims).unwrap();
|
|
1685
|
+
let one = Scalar::DimScalar::from_f32(1.0);
|
|
1686
|
+
v.try_set(0_usize, one).unwrap();
|
|
1687
|
+
v.try_set((test_dims - 1) as i32, one).unwrap();
|
|
1688
|
+
let first = v.try_get(0_usize).unwrap();
|
|
1689
|
+
let last = v.try_get(-1_i32).unwrap();
|
|
1690
|
+
assert!(
|
|
1691
|
+
first.to_f32() >= 0.5,
|
|
1692
|
+
"first dim should be ~1.0, got {:?}",
|
|
1693
|
+
first
|
|
1694
|
+
);
|
|
1695
|
+
assert!(
|
|
1696
|
+
last.to_f32() >= 0.5,
|
|
1697
|
+
"last dim should be ~1.0, got {:?}",
|
|
1698
|
+
last
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
#[test]
|
|
1703
|
+
fn vector_roundtrip_all_types() {
|
|
1704
|
+
check_vector_roundtrip::<f32>();
|
|
1705
|
+
check_vector_roundtrip::<f64>();
|
|
1706
|
+
check_vector_roundtrip::<f16>();
|
|
1707
|
+
check_vector_roundtrip::<bf16>();
|
|
1708
|
+
check_vector_roundtrip::<i4x2>();
|
|
1709
|
+
check_vector_roundtrip::<u4x2>();
|
|
1710
|
+
check_vector_roundtrip::<u1x8>();
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
#[test]
|
|
1714
|
+
fn vector_try_get_set_all_types() {
|
|
1715
|
+
check_vector_try_get_set::<f32>();
|
|
1716
|
+
check_vector_try_get_set::<f64>();
|
|
1717
|
+
check_vector_try_get_set::<i4x2>();
|
|
1718
|
+
check_vector_try_get_set::<u4x2>();
|
|
1719
|
+
check_vector_try_get_set::<u1x8>();
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
#[test]
|
|
1723
|
+
fn vec_index_signed() {
|
|
1724
|
+
let v = Vector::<f32>::try_from_dims(&[10.0, 20.0, 30.0, 40.0, 50.0]).unwrap();
|
|
1725
|
+
// Positive indexing
|
|
1726
|
+
assert_eq!(v[0], 10.0);
|
|
1727
|
+
assert_eq!(v[4], 50.0);
|
|
1728
|
+
// Negative indexing (i32 default for integer literals)
|
|
1729
|
+
assert_eq!(v[-1_i32], 50.0);
|
|
1730
|
+
assert_eq!(v[-2_i32], 40.0);
|
|
1731
|
+
assert_eq!(v[-5_i32], 10.0);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
#[test]
|
|
1735
|
+
fn vector_view_stride() {
|
|
1736
|
+
let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
|
|
1737
|
+
let view = v.view();
|
|
1738
|
+
assert!(view.is_contiguous());
|
|
1739
|
+
assert_eq!(view.size(), 5);
|
|
1740
|
+
|
|
1741
|
+
// Reversed view
|
|
1742
|
+
let rev = view.rev();
|
|
1743
|
+
assert_eq!(rev.try_get(0_usize).unwrap(), 5.0);
|
|
1744
|
+
assert_eq!(rev.try_get(4_usize).unwrap(), 1.0);
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
#[test]
|
|
1748
|
+
fn vector_span_fill() {
|
|
1749
|
+
let mut v = Vector::<f32>::try_zeros(4).unwrap();
|
|
1750
|
+
{
|
|
1751
|
+
let mut span = v.span();
|
|
1752
|
+
span.fill(42.0);
|
|
1753
|
+
}
|
|
1754
|
+
assert_eq!(v[0], 42.0);
|
|
1755
|
+
assert_eq!(v[3], 42.0);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
#[test]
|
|
1759
|
+
fn vector_iter() {
|
|
1760
|
+
let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
|
|
1761
|
+
let values: Vec<f32> = v.iter().map(|x| *x).collect();
|
|
1762
|
+
assert_eq!(values, vec![1.0, 2.0, 3.0]);
|
|
1763
|
+
|
|
1764
|
+
// Double-ended
|
|
1765
|
+
let reversed_values: Vec<f32> = v.iter().rev().map(|x| *x).collect();
|
|
1766
|
+
assert_eq!(reversed_values, vec![3.0, 2.0, 1.0]);
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
#[test]
|
|
1770
|
+
fn view_strided_iteration() {
|
|
1771
|
+
let v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0, 4.0, 5.0]).unwrap();
|
|
1772
|
+
let view = v.view();
|
|
1773
|
+
|
|
1774
|
+
// Every other element
|
|
1775
|
+
let strided = view.try_strided(0, 5, 2).unwrap();
|
|
1776
|
+
assert_eq!(strided.size(), 3);
|
|
1777
|
+
let values: Vec<f32> = strided.iter().map(|x| *x).collect();
|
|
1778
|
+
assert_eq!(values, vec![1.0, 3.0, 5.0]);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
#[test]
|
|
1782
|
+
fn vector_filled() {
|
|
1783
|
+
let v = Vector::<f32>::try_full(3, 7.5).unwrap();
|
|
1784
|
+
assert_eq!(v[0], 7.5);
|
|
1785
|
+
assert_eq!(v[1], 7.5);
|
|
1786
|
+
assert_eq!(v[2], 7.5);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
#[test]
|
|
1790
|
+
fn empty_vector() {
|
|
1791
|
+
let v = Vector::<f32>::try_zeros(0).unwrap();
|
|
1792
|
+
assert!(v.is_empty());
|
|
1793
|
+
assert_eq!(v.size(), 0);
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
#[test]
|
|
1797
|
+
#[should_panic]
|
|
1798
|
+
fn index_out_of_bounds() {
|
|
1799
|
+
let v = Vector::<f32>::try_zeros(3).unwrap();
|
|
1800
|
+
let _ = v[3_usize];
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
#[test]
|
|
1804
|
+
fn vector_allclose_matching() {
|
|
1805
|
+
let a = Vector::<f32>::try_full(4, 1.0).unwrap();
|
|
1806
|
+
let b = Vector::<f32>::try_full(4, 1.0 + 1e-7).unwrap();
|
|
1807
|
+
assert!(a.allclose(&b, 1e-6, 0.0));
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
#[test]
|
|
1811
|
+
fn vector_allclose_mismatching() {
|
|
1812
|
+
let a = Vector::<f32>::try_full(4, 1.0).unwrap();
|
|
1813
|
+
let b = Vector::<f32>::try_full(4, 2.0).unwrap();
|
|
1814
|
+
assert!(!a.allclose(&b, 1e-6, 0.0));
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
#[test]
|
|
1818
|
+
fn vector_allclose_different_dims() {
|
|
1819
|
+
let a = Vector::<f32>::try_full(3, 1.0).unwrap();
|
|
1820
|
+
let b = Vector::<f32>::try_full(4, 1.0).unwrap();
|
|
1821
|
+
assert!(!a.allclose(&b, 1e-6, 1e-6));
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
#[test]
|
|
1825
|
+
fn display_precision_forwarding() {
|
|
1826
|
+
let v = Vector::<f32>::try_full(3, 1.0).unwrap();
|
|
1827
|
+
let s = format!("{:.2}", v);
|
|
1828
|
+
assert_eq!(s, "[1.00, 1.00, 1.00]");
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
#[test]
|
|
1832
|
+
fn vector_span_iter_mut_f32() {
|
|
1833
|
+
let mut v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
|
|
1834
|
+
for mut value in &mut v {
|
|
1835
|
+
*value += 10.0;
|
|
1836
|
+
}
|
|
1837
|
+
let values: Vec<f32> = v.iter().map(|x| *x).collect();
|
|
1838
|
+
assert_eq!(values, vec![11.0, 12.0, 13.0]);
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
#[test]
|
|
1842
|
+
fn vector_span_iter_mut_i4x2() {
|
|
1843
|
+
let mut v = Vector::<i4x2>::try_zeros(4).unwrap();
|
|
1844
|
+
{
|
|
1845
|
+
let mut span = v.span();
|
|
1846
|
+
for (i, mut value) in span.iter_mut().enumerate() {
|
|
1847
|
+
*value = (i + 1) as i8;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
assert_eq!(v.try_get(0_usize).unwrap(), 1);
|
|
1851
|
+
assert_eq!(v.try_get(1_usize).unwrap(), 2);
|
|
1852
|
+
assert_eq!(v.try_get(2_usize).unwrap(), 3);
|
|
1853
|
+
assert_eq!(v.try_get(3_usize).unwrap(), 4);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
#[test]
|
|
1857
|
+
fn vector_span_iter_mut_u1x8() {
|
|
1858
|
+
let mut v = Vector::<u1x8>::try_zeros(8).unwrap();
|
|
1859
|
+
for (i, mut value) in v.iter_mut().enumerate() {
|
|
1860
|
+
if i % 2 == 0 {
|
|
1861
|
+
*value = 1;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
// Even indices should be 1, odd should be 0
|
|
1865
|
+
assert_eq!(v.try_get(0_usize).unwrap(), 1);
|
|
1866
|
+
assert_eq!(v.try_get(1_usize).unwrap(), 0);
|
|
1867
|
+
assert_eq!(v.try_get(2_usize).unwrap(), 1);
|
|
1868
|
+
assert_eq!(v.try_get(3_usize).unwrap(), 0);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
#[test]
|
|
1872
|
+
fn vector_span_iter_double_ended() {
|
|
1873
|
+
let mut v = Vector::<f32>::try_from_scalars(&[1.0, 2.0, 3.0]).unwrap();
|
|
1874
|
+
let mut span = v.span();
|
|
1875
|
+
let mut it = span.iter_mut();
|
|
1876
|
+
// Take from front
|
|
1877
|
+
let mut first = it.next().unwrap();
|
|
1878
|
+
*first = 10.0;
|
|
1879
|
+
drop(first);
|
|
1880
|
+
// Take from back
|
|
1881
|
+
let mut last = it.next_back().unwrap();
|
|
1882
|
+
*last = 30.0;
|
|
1883
|
+
drop(last);
|
|
1884
|
+
drop(it);
|
|
1885
|
+
drop(span);
|
|
1886
|
+
assert_eq!(v.try_get(0_usize).unwrap(), 10.0);
|
|
1887
|
+
assert_eq!(v.try_get(1_usize).unwrap(), 2.0);
|
|
1888
|
+
assert_eq!(v.try_get(2_usize).unwrap(), 30.0);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
#[test]
|
|
1892
|
+
fn vector_iterator_alias_compat() {
|
|
1893
|
+
// VectorIterator type alias should still work
|
|
1894
|
+
let v = Vector::<f32>::try_from_scalars(&[1.0]).unwrap();
|
|
1895
|
+
let _it: VectorIterator<'_, f32> = v.iter();
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// endregion: Tests
|