usearch 2.26.1 → 2.26.2
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 +1 -1
- package/include/usearch/index.hpp +5 -1
- package/numkong/CITATION.cff +13 -9
- package/numkong/CONTRIBUTING.md +29 -24
- package/numkong/Cargo.toml +1 -1
- package/numkong/README.md +21 -7
- package/numkong/binding.gyp +47 -31
- package/numkong/build.rs +15 -5
- package/numkong/c/dispatch.h +0 -1
- package/numkong/c/parallel.c +139 -0
- package/numkong/c/parallel.h +47 -0
- package/numkong/cmake/toolchain-wasi.cmake +39 -5
- package/numkong/include/README.md +24 -17
- package/numkong/include/numkong/attention/sapphireamx.h +6 -6
- package/numkong/include/numkong/attention/sme.h +2 -2
- package/numkong/include/numkong/capabilities.h +2 -2
- package/numkong/include/numkong/cast/README.md +9 -7
- package/numkong/include/numkong/cast/loongsonasx.h +48 -40
- package/numkong/include/numkong/cast/serial.h +89 -11
- package/numkong/include/numkong/cast.h +3 -3
- package/numkong/include/numkong/curved/README.md +10 -10
- package/numkong/include/numkong/curved/neon.h +1 -0
- package/numkong/include/numkong/curved/neonbfdot.h +1 -0
- package/numkong/include/numkong/curved/smef64.h +1 -0
- package/numkong/include/numkong/dot/README.md +18 -18
- package/numkong/include/numkong/dot/alder.h +8 -8
- package/numkong/include/numkong/dot/haswell.h +8 -8
- package/numkong/include/numkong/dot/icelake.h +12 -12
- package/numkong/include/numkong/dot/loongsonasx.h +6 -6
- package/numkong/include/numkong/dot/neon.h +2 -1
- package/numkong/include/numkong/dot/neonbfdot.h +1 -0
- package/numkong/include/numkong/dot/neonfhm.h +1 -0
- package/numkong/include/numkong/dot/neonsdot.h +1 -0
- package/numkong/include/numkong/dot/powervsx.h +3 -3
- package/numkong/include/numkong/dot/serial.h +19 -6
- package/numkong/include/numkong/dot/skylake.h +4 -4
- package/numkong/include/numkong/dot/svehalf.h +1 -1
- package/numkong/include/numkong/dot/v128relaxed.h +24 -23
- package/numkong/include/numkong/dots/README.md +26 -26
- package/numkong/include/numkong/dots/genoa.h +3 -1
- package/numkong/include/numkong/dots/icelake.h +1 -0
- package/numkong/include/numkong/dots/powervsx.h +1 -1
- package/numkong/include/numkong/dots/serial.h +32 -33
- package/numkong/include/numkong/dots/sierra.h +4 -3
- package/numkong/include/numkong/each/README.md +5 -3
- package/numkong/include/numkong/each/sapphire.h +2 -0
- package/numkong/include/numkong/each/skylake.h +1 -0
- package/numkong/include/numkong/geospatial/README.md +6 -6
- package/numkong/include/numkong/geospatial/v128relaxed.h +1 -0
- package/numkong/include/numkong/maxsim/README.md +5 -5
- package/numkong/include/numkong/maxsim/genoa.h +4 -0
- package/numkong/include/numkong/maxsim/icelake.h +1 -0
- package/numkong/include/numkong/maxsim/serial.h +22 -9
- package/numkong/include/numkong/maxsim/sme.h +3 -2
- package/numkong/include/numkong/mesh/README.md +7 -4
- package/numkong/include/numkong/mesh/genoa.h +2 -2
- package/numkong/include/numkong/mesh/haswell.h +8 -8
- package/numkong/include/numkong/mesh/neon.h +6 -6
- package/numkong/include/numkong/mesh/neonbfdot.h +3 -2
- package/numkong/include/numkong/mesh/neonfhm.h +3 -2
- package/numkong/include/numkong/mesh/rvv.h +4 -4
- package/numkong/include/numkong/mesh/serial.h +17 -17
- package/numkong/include/numkong/mesh/skylake.h +4 -4
- package/numkong/include/numkong/mesh/v128relaxed.h +4 -4
- package/numkong/include/numkong/mesh.hpp +2 -2
- package/numkong/include/numkong/numkong.h +1 -1
- package/numkong/include/numkong/numkong.hpp +1 -1
- package/numkong/include/numkong/probability/README.md +17 -15
- package/numkong/include/numkong/probability/skylake.h +1 -1
- package/numkong/include/numkong/reduce/README.md +27 -21
- package/numkong/include/numkong/reduce/genoa.h +2 -1
- package/numkong/include/numkong/reduce/icelake.h +1 -0
- package/numkong/include/numkong/reduce/neon.h +1 -1
- package/numkong/include/numkong/reduce/serial.h +9 -9
- package/numkong/include/numkong/reduce/skylake.h +4 -4
- package/numkong/include/numkong/reduce.hpp +38 -0
- package/numkong/include/numkong/scalar/README.md +29 -28
- package/numkong/include/numkong/set/README.md +17 -14
- package/numkong/include/numkong/set/haswell.h +15 -16
- package/numkong/include/numkong/set/icelake.h +15 -16
- package/numkong/include/numkong/set/loongsonasx.h +12 -10
- package/numkong/include/numkong/set/neon.h +15 -19
- package/numkong/include/numkong/set/powervsx.h +15 -21
- package/numkong/include/numkong/set/serial.h +19 -20
- package/numkong/include/numkong/set/v128relaxed.h +10 -8
- package/numkong/include/numkong/sets/icelake.h +1 -0
- package/numkong/include/numkong/sparse/README.md +4 -4
- package/numkong/include/numkong/sparse/icelake.h +1 -0
- package/numkong/include/numkong/sparse/neon.h +1 -0
- package/numkong/include/numkong/sparse/serial.h +10 -3
- package/numkong/include/numkong/sparse/turin.h +1 -0
- package/numkong/include/numkong/spatial/README.md +25 -24
- package/numkong/include/numkong/spatial/alder.h +2 -1
- package/numkong/include/numkong/spatial/haswell.h +48 -40
- package/numkong/include/numkong/spatial/icelake.h +2 -1
- package/numkong/include/numkong/spatial/neon.h +54 -46
- package/numkong/include/numkong/spatial/neonbfdot.h +1 -1
- package/numkong/include/numkong/spatial/powervsx.h +54 -46
- package/numkong/include/numkong/spatial/serial.h +56 -46
- package/numkong/include/numkong/spatial/sierra.h +4 -3
- package/numkong/include/numkong/spatial/skylake.h +27 -22
- package/numkong/include/numkong/spatial/sve.h +6 -6
- package/numkong/include/numkong/spatial/svebfdot.h +1 -1
- package/numkong/include/numkong/spatial/svehalf.h +1 -1
- package/numkong/include/numkong/spatial/v128relaxed.h +36 -30
- package/numkong/include/numkong/spatials/README.md +15 -15
- package/numkong/include/numkong/spatials/genoa.h +3 -0
- package/numkong/include/numkong/spatials/graniteamx.h +2 -0
- package/numkong/include/numkong/spatials/icelake.h +1 -0
- package/numkong/include/numkong/spatials/sierra.h +1 -0
- package/numkong/include/numkong/tensor.hpp +236 -123
- package/numkong/include/numkong/trigonometry/README.md +4 -4
- package/numkong/include/numkong/trigonometry/serial.h +6 -6
- package/numkong/include/numkong/trigonometry/v128relaxed.h +1 -0
- package/numkong/include/numkong/types.hpp +46 -16
- package/numkong/include/numkong/vector.hpp +116 -23
- package/numkong/javascript/@numkong-darwin-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-darwin-x64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-linux-x64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-arm64/package.json +1 -1
- package/numkong/javascript/@numkong-win32-x64/package.json +1 -1
- package/numkong/javascript/README.md +18 -16
- package/numkong/javascript/numkong.c +70 -40
- package/numkong/package.json +8 -8
- package/numkong/rust/README.md +19 -9
- package/numkong/rust/cast.rs +52 -1
- package/numkong/rust/each.rs +480 -7
- package/numkong/rust/matrix.rs +877 -216
- package/numkong/rust/numkong.rs +12 -8
- package/numkong/rust/reduce.rs +433 -5
- package/numkong/rust/tensor.rs +450 -842
- package/numkong/rust/vector.rs +520 -101
- package/numkong/swift/README.md +5 -3
- package/numkong/test/README.md +4 -4
- package/numkong/test/test-wasi.mjs +17 -0
- package/numkong/test/test_each.cpp +30 -1
- package/numkong/test/test_each.py +74 -1
- package/numkong/test/test_reduce.cpp +51 -0
- package/numkong/test/test_tensor.cpp +2 -63
- package/numkong/test/test_tensor.py +93 -0
- package/package.json +1 -1
- 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/prebuilds/win32-x64/usearch.node +0 -0
package/numkong/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "numkong",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.8.1",
|
|
4
4
|
"description": "Portable mixed-precision math, linear-algebra, & retrieval library with 2000+ SIMD kernels for x86, Arm, RISC-V, LoongArch, Power, & WebAssembly",
|
|
5
5
|
"homepage": "https://github.com/ashvardanian/NumKong",
|
|
6
6
|
"author": "Ash Vardanian",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@playwright/test": "^1.58.0",
|
|
73
73
|
"@types/bindings": "^1.5.5",
|
|
74
74
|
"@types/node": "^24.10.0",
|
|
75
|
-
"node-gyp": "^
|
|
75
|
+
"node-gyp": "^13.0.1",
|
|
76
76
|
"playwright": "^1.58.0",
|
|
77
77
|
"http-server": "^14.1.1",
|
|
78
78
|
"esbuild": "^0.25.0",
|
|
@@ -98,11 +98,11 @@
|
|
|
98
98
|
"printWidth": 120
|
|
99
99
|
},
|
|
100
100
|
"optionalDependencies": {
|
|
101
|
-
"@numkong/darwin-arm64": "7.
|
|
102
|
-
"@numkong/darwin-x64": "7.
|
|
103
|
-
"@numkong/linux-arm64": "7.
|
|
104
|
-
"@numkong/linux-x64": "7.
|
|
105
|
-
"@numkong/win32-arm64": "7.
|
|
106
|
-
"@numkong/win32-x64": "7.
|
|
101
|
+
"@numkong/darwin-arm64": "7.8.1",
|
|
102
|
+
"@numkong/darwin-x64": "7.8.1",
|
|
103
|
+
"@numkong/linux-arm64": "7.8.1",
|
|
104
|
+
"@numkong/linux-x64": "7.8.1",
|
|
105
|
+
"@numkong/win32-arm64": "7.8.1",
|
|
106
|
+
"@numkong/win32-x64": "7.8.1"
|
|
107
107
|
}
|
|
108
108
|
}
|
package/numkong/rust/README.md
CHANGED
|
@@ -42,7 +42,7 @@ The `parallel` feature is the intended native orchestration layer.
|
|
|
42
42
|
|
|
43
43
|
## Ecosystem Comparison
|
|
44
44
|
|
|
45
|
-
| Feature | NumKong | [nalgebra]
|
|
45
|
+
| Feature | NumKong | [nalgebra][nalgebra] | [ndarray][ndarray] |
|
|
46
46
|
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- |
|
|
47
47
|
| Operation families | dots, distances, binary, probability, geospatial, curved, mesh, sparse, MaxSim, elementwise, reductions, cast, trig | linear algebra, decompositions | general n-dimensional arithmetic |
|
|
48
48
|
| Precision | BFloat16 through sub-byte; automatic widening; Kahan summation; 0 ULP in Float32/Float64 | Float32/Float64 only; no widening; standard accuracy | Float32/Float64 only; no widening; standard accuracy |
|
|
@@ -52,6 +52,9 @@ The `parallel` feature is the intended native orchestration layer.
|
|
|
52
52
|
| Memory model | Caller-owned; `Tensor`/`PackedMatrix` support custom allocators | Heap-allocated matrices; custom storage trait | Heap-allocated; no custom allocator support |
|
|
53
53
|
| Host-side parallelism | row-range partitioning via reusable `ThreadPool`; no hidden threads | Rayon-based parallelism possible | Rayon-based parallelism possible |
|
|
54
54
|
|
|
55
|
+
[nalgebra]: https://github.com/dimforge/nalgebra
|
|
56
|
+
[ndarray]: https://github.com/rust-ndarray/ndarray
|
|
57
|
+
|
|
55
58
|
NumKong validates `f16` and `bf16` interop against the `half` crate in its own test suite.
|
|
56
59
|
That lets you move between ecosystem-standard half types and NumKong's kernel-facing wrappers without ambiguity.
|
|
57
60
|
|
|
@@ -235,7 +238,8 @@ assert!(jaccard_sets > 0.0 && jaccard_sets < 1.0); // |A ∩ B| / |A ∪ B|
|
|
|
235
238
|
```rust
|
|
236
239
|
use numkong::{JensenShannon, KullbackLeibler};
|
|
237
240
|
|
|
238
|
-
let p = [0.2_f32, 0.3, 0.5]
|
|
241
|
+
let p = [0.2_f32, 0.3, 0.5];
|
|
242
|
+
let q = [0.1_f32, 0.3, 0.6];
|
|
239
243
|
let kl_forward = f32::kullbackleibler(&p, &q).unwrap();
|
|
240
244
|
let kl_reverse = f32::kullbackleibler(&q, &p).unwrap();
|
|
241
245
|
assert!(kl_forward != kl_reverse); // KLD is asymmetric
|
|
@@ -254,15 +258,19 @@ Outputs are meters.
|
|
|
254
258
|
use numkong::{Haversine, Vincenty};
|
|
255
259
|
|
|
256
260
|
// Statue of Liberty (40.6892°N, 74.0445°W) → Big Ben (51.5007°N, 0.1246°W)
|
|
257
|
-
let liberty_lat = [0.7101605100_f64]
|
|
258
|
-
let
|
|
261
|
+
let liberty_lat = [0.7101605100_f64];
|
|
262
|
+
let liberty_lon = [-1.2923203180_f64];
|
|
263
|
+
let big_ben_lat = [0.8988567821_f64];
|
|
264
|
+
let big_ben_lon = [-0.0021746802_f64];
|
|
259
265
|
let mut distance = [0.0_f64; 1];
|
|
260
266
|
f64::vincenty(&liberty_lat, &liberty_lon, &big_ben_lat, &big_ben_lon, &mut distance).unwrap(); // ≈ 5,589,857 m (ellipsoidal, baseline)
|
|
261
267
|
f64::haversine(&liberty_lat, &liberty_lon, &big_ben_lat, &big_ben_lon, &mut distance).unwrap(); // ≈ 5,543,723 m (spherical, ~46 km less)
|
|
262
268
|
|
|
263
269
|
// Vincenty in f32 — drifts ~2 m from f64
|
|
264
|
-
let liberty_lat32 = [0.7101605100_f32]
|
|
265
|
-
let
|
|
270
|
+
let liberty_lat32 = [0.7101605100_f32];
|
|
271
|
+
let liberty_lon32 = [-1.2923203180_f32];
|
|
272
|
+
let big_ben_lat32 = [0.8988567821_f32];
|
|
273
|
+
let big_ben_lon32 = [-0.0021746802_f32];
|
|
266
274
|
let mut distance_f32 = [0.0_f32; 1];
|
|
267
275
|
f32::vincenty(&liberty_lat32, &liberty_lon32, &big_ben_lat32, &big_ben_lon32, &mut distance_f32).unwrap(); // ≈ 5,589,859 m (+2 m drift)
|
|
268
276
|
```
|
|
@@ -466,12 +474,14 @@ Sparse helpers cover both sorted-index intersection and weighted sparse dot prod
|
|
|
466
474
|
```rust
|
|
467
475
|
use numkong::{SparseIntersect, SparseDot};
|
|
468
476
|
|
|
469
|
-
let a_idx = [1_u32, 3, 5, 7]
|
|
477
|
+
let a_idx = [1_u32, 3, 5, 7];
|
|
478
|
+
let b_idx = [3_u32, 4, 5, 8];
|
|
470
479
|
let count = u32::sparse_intersection_size(&a_idx, &b_idx);
|
|
471
480
|
assert_eq!(count, 2); // indices 3 and 5
|
|
472
481
|
|
|
473
|
-
let a_weights = [1.0_f32, 2.0, 3.0, 4.0]
|
|
474
|
-
let
|
|
482
|
+
let a_weights = [1.0_f32, 2.0, 3.0, 4.0];
|
|
483
|
+
let b_weights = [5.0_f32, 6.0, 7.0, 8.0];
|
|
484
|
+
let dot = u32::sparse_dot(&a_idx, &b_idx, &a_weights, &b_weights);
|
|
475
485
|
assert!(dot > 0.0); // weighted dot over shared indices
|
|
476
486
|
```
|
|
477
487
|
|
package/numkong/rust/cast.rs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
//! Type casting between scalar formats.
|
|
1
|
+
//! Type casting between scalar formats — slice traits and tensor-shaped wrappers.
|
|
2
2
|
//!
|
|
3
3
|
//! This module provides:
|
|
4
4
|
//!
|
|
5
5
|
//! - [`CastDtype`]: Trait marking types eligible for bulk casting
|
|
6
6
|
//! - [`cast`]: Bulk-converts a slice from one scalar format to another
|
|
7
|
+
//! - [`CastOps`]: Tensor-shaped extension trait — auto-implemented on every
|
|
8
|
+
//! [`crate::tensor::TensorRef`] so any container can do `tensor.try_cast_dtype::<Destination>()`
|
|
7
9
|
|
|
8
10
|
use crate::types::{bf16, bf16c, e2m3, e3m2, e4m3, e5m2, f16, f16c, f32c, f64c, StorageElement};
|
|
9
11
|
|
|
@@ -214,6 +216,34 @@ pub fn cast<S: CastDtype, D: CastDtype>(source: &[S], dest: &mut [D]) -> Option<
|
|
|
214
216
|
Some(())
|
|
215
217
|
}
|
|
216
218
|
|
|
219
|
+
// region: Tensor-shaped cast (moved from crate::tensor)
|
|
220
|
+
|
|
221
|
+
use crate::tensor::{try_reborrow_tensor_into, Global, Tensor, TensorError, TensorRef};
|
|
222
|
+
|
|
223
|
+
/// Extension trait: type casting for any [`TensorRef`] implementor.
|
|
224
|
+
pub trait CastOps<Source: Clone + CastDtype, const MAX_RANK: usize>:
|
|
225
|
+
TensorRef<Source, MAX_RANK>
|
|
226
|
+
{
|
|
227
|
+
fn try_cast_dtype<Destination: Clone + CastDtype>(
|
|
228
|
+
&self,
|
|
229
|
+
) -> Result<Tensor<Destination, Global, MAX_RANK>, TensorError> {
|
|
230
|
+
self.view().try_cast_dtype()
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
impl<Source: Clone + CastDtype, const R: usize, C: TensorRef<Source, R>> CastOps<Source, R> for C {}
|
|
235
|
+
|
|
236
|
+
impl<Source: Clone + CastDtype, const MAX_RANK: usize> Tensor<Source, Global, MAX_RANK> {
|
|
237
|
+
pub fn try_cast_dtype_into<Destination: Clone + CastDtype>(
|
|
238
|
+
&self,
|
|
239
|
+
out: &mut Tensor<Destination, Global, MAX_RANK>,
|
|
240
|
+
) -> Result<(), TensorError> {
|
|
241
|
+
try_reborrow_tensor_into(self, out, |view, span| view.try_cast_dtype_into(span))
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// endregion: Tensor-shaped cast
|
|
246
|
+
|
|
217
247
|
#[cfg(test)]
|
|
218
248
|
mod tests {
|
|
219
249
|
use super::*;
|
|
@@ -311,4 +341,25 @@ mod tests {
|
|
|
311
341
|
assert_eq!(narrowed[1].re.to_f32(), bf16::from_f32(-3.5).to_f32());
|
|
312
342
|
assert_eq!(narrowed[1].im.to_f32(), bf16::from_f32(4.25).to_f32());
|
|
313
343
|
}
|
|
344
|
+
|
|
345
|
+
#[test]
|
|
346
|
+
fn cast_via_tensor_view_round_trip() {
|
|
347
|
+
// Exercises `CastOps::try_cast_dtype` on a strided `TensorView`,
|
|
348
|
+
// mirroring how callers reach the trait through the tensor-shaped wrapper.
|
|
349
|
+
use crate::tensor::{SliceRange, Tensor};
|
|
350
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
351
|
+
let source = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
352
|
+
let even_columns = source
|
|
353
|
+
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
354
|
+
.unwrap();
|
|
355
|
+
|
|
356
|
+
let widened = even_columns.try_cast_dtype::<f64>().unwrap();
|
|
357
|
+
assert_eq!(widened.shape(), &[3, 2]);
|
|
358
|
+
assert_eq!(widened.as_slice(), &[0.0, 2.0, 4.0, 6.0, 8.0, 10.0]);
|
|
359
|
+
|
|
360
|
+
let complexified = even_columns.try_cast_dtype::<f32c>().unwrap();
|
|
361
|
+
assert_eq!(complexified.shape(), &[3, 2]);
|
|
362
|
+
assert_eq!(complexified.as_slice()[0], f32c::from_real_imag(0.0, 0.0));
|
|
363
|
+
assert_eq!(complexified.as_slice()[5], f32c::from_real_imag(10.0, 0.0));
|
|
364
|
+
}
|
|
314
365
|
}
|
package/numkong/rust/each.rs
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
//! Elementwise operations and trigonometry.
|
|
1
|
+
//! Elementwise operations and trigonometry — slice traits and tensor-shaped wrappers.
|
|
2
2
|
//!
|
|
3
3
|
//! This module provides:
|
|
4
4
|
//!
|
|
5
|
-
//! -
|
|
6
|
-
//!
|
|
7
|
-
//!
|
|
8
|
-
//!
|
|
9
|
-
//!
|
|
10
|
-
//!
|
|
5
|
+
//! - Slice-level traits:
|
|
6
|
+
//! - [`EachScale`]: Linear scaling (alpha * x + beta)
|
|
7
|
+
//! - [`EachSum`]: Elementwise addition of two vectors
|
|
8
|
+
//! - [`EachBlend`]: Weighted blend of two vectors
|
|
9
|
+
//! - [`EachFMA`]: Fused multiply-add (a * alpha + b * beta)
|
|
10
|
+
//! - [`EachSin`], [`EachCos`], [`EachATan`]: Trigonometric functions
|
|
11
|
+
//! - [`Trigonometry`]: Blanket trait combining `EachSin + EachCos + EachATan`
|
|
12
|
+
//! - Tensor-shaped extension traits (auto-implemented on every [`crate::tensor::TensorRef`]):
|
|
13
|
+
//! - [`ScaleOps`], [`SumOps`], [`BlendOps`], [`FmaOps`]: Tensor wrappers around the slice traits
|
|
14
|
+
//! - [`TrigSinOps`], [`TrigCosOps`], [`TrigAtanOps`]: Tensor wrappers around the trig traits
|
|
15
|
+
//! - [`AllCloseOps`]: Tolerance-based equality for any [`crate::tensor::TensorRef`]
|
|
11
16
|
//!
|
|
12
17
|
//! # In-Place vs Allocating Semantics
|
|
13
18
|
//!
|
|
@@ -43,6 +48,9 @@
|
|
|
43
48
|
//! assert_eq!(output, [2.5, 4.5, 6.5, 8.5]);
|
|
44
49
|
//! ```
|
|
45
50
|
|
|
51
|
+
use crate::tensor::{
|
|
52
|
+
try_reborrow_tensor_inplace, try_reborrow_tensor_into, Global, Tensor, TensorError, TensorRef,
|
|
53
|
+
};
|
|
46
54
|
use crate::types::{bf16, bf16c, e2m3, e3m2, e4m3, e5m2, f16, f16c, f32c, f64c, StorageElement};
|
|
47
55
|
|
|
48
56
|
#[link(name = "numkong")]
|
|
@@ -2403,6 +2411,395 @@ impl EachFMA for bf16c {
|
|
|
2403
2411
|
pub trait Trigonometry: EachSin + EachCos + EachATan {}
|
|
2404
2412
|
impl<Scalar: EachSin + EachCos + EachATan> Trigonometry for Scalar {}
|
|
2405
2413
|
|
|
2414
|
+
// region: Tensor-shaped trigonometry (moved from crate::tensor)
|
|
2415
|
+
|
|
2416
|
+
/// Extension trait: element-wise sine for any [`TensorRef`] implementor.
|
|
2417
|
+
pub trait TrigSinOps<Scalar: Clone + EachSin, const MAX_RANK: usize>:
|
|
2418
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2419
|
+
{
|
|
2420
|
+
fn try_sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2421
|
+
self.view().try_sin()
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
impl<Scalar: Clone + EachSin, const R: usize, C: TensorRef<Scalar, R>> TrigSinOps<Scalar, R> for C {}
|
|
2426
|
+
|
|
2427
|
+
/// Extension trait: element-wise cosine for any [`TensorRef`] implementor.
|
|
2428
|
+
pub trait TrigCosOps<Scalar: Clone + EachCos, const MAX_RANK: usize>:
|
|
2429
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2430
|
+
{
|
|
2431
|
+
fn try_cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2432
|
+
self.view().try_cos()
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
impl<Scalar: Clone + EachCos, const R: usize, C: TensorRef<Scalar, R>> TrigCosOps<Scalar, R> for C {}
|
|
2437
|
+
|
|
2438
|
+
/// Extension trait: element-wise arctangent for any [`TensorRef`] implementor.
|
|
2439
|
+
pub trait TrigAtanOps<Scalar: Clone + EachATan, const MAX_RANK: usize>:
|
|
2440
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2441
|
+
{
|
|
2442
|
+
fn try_atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2443
|
+
self.view().try_atan()
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
impl<Scalar: Clone + EachATan, const R: usize, C: TensorRef<Scalar, R>> TrigAtanOps<Scalar, R>
|
|
2448
|
+
for C
|
|
2449
|
+
{
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
impl<Scalar: Clone + EachSin, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
2453
|
+
/// Element-wise sine: result\[i\] = sin(self\[i\])
|
|
2454
|
+
pub fn sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2455
|
+
self.view().try_sin()
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
/// Element-wise sine in-place (infallible — self vs self always matches).
|
|
2459
|
+
pub fn sin_inplace(&mut self) {
|
|
2460
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_sin_into(span))
|
|
2461
|
+
.expect("inplace trig op on self cannot fail")
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
pub fn try_sin_inplace(&mut self) -> Result<(), TensorError> {
|
|
2465
|
+
self.sin_inplace();
|
|
2466
|
+
Ok(())
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
impl<Scalar: Clone + EachCos, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
2471
|
+
/// Element-wise cosine: result\[i\] = cos(self\[i\])
|
|
2472
|
+
pub fn cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2473
|
+
self.view().try_cos()
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
/// Element-wise cosine in-place (infallible — self vs self always matches).
|
|
2477
|
+
pub fn cos_inplace(&mut self) {
|
|
2478
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_cos_into(span))
|
|
2479
|
+
.expect("inplace trig op on self cannot fail")
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
pub fn try_cos_inplace(&mut self) -> Result<(), TensorError> {
|
|
2483
|
+
self.cos_inplace();
|
|
2484
|
+
Ok(())
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
impl<Scalar: Clone + EachATan, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
2489
|
+
/// Element-wise arctangent: result\[i\] = atan(self\[i\])
|
|
2490
|
+
pub fn atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2491
|
+
self.view().try_atan()
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
/// Element-wise arctangent in-place (infallible — self vs self always matches).
|
|
2495
|
+
pub fn atan_inplace(&mut self) {
|
|
2496
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_atan_into(span))
|
|
2497
|
+
.expect("inplace trig op on self cannot fail")
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
pub fn try_atan_inplace(&mut self) -> Result<(), TensorError> {
|
|
2501
|
+
self.atan_inplace();
|
|
2502
|
+
Ok(())
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// endregion: Tensor-shaped trigonometry
|
|
2507
|
+
|
|
2508
|
+
// region: Tensor-shaped tolerance equality (moved from crate::tensor)
|
|
2509
|
+
|
|
2510
|
+
use crate::types::{is_close, FloatConvertible, NumberLike};
|
|
2511
|
+
|
|
2512
|
+
/// Extension trait: tolerance-based equality for any [`TensorRef`] implementor.
|
|
2513
|
+
///
|
|
2514
|
+
/// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
|
|
2515
|
+
/// Returns `false` if shapes differ.
|
|
2516
|
+
pub trait AllCloseOps<Scalar: FloatConvertible, const MAX_RANK: usize>:
|
|
2517
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2518
|
+
where
|
|
2519
|
+
Scalar::DimScalar: NumberLike,
|
|
2520
|
+
{
|
|
2521
|
+
fn allclose(
|
|
2522
|
+
&self,
|
|
2523
|
+
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
2524
|
+
atol: f64,
|
|
2525
|
+
rtol: f64,
|
|
2526
|
+
) -> bool {
|
|
2527
|
+
let a = self.view();
|
|
2528
|
+
let b = other.view();
|
|
2529
|
+
a.ndim() == b.ndim()
|
|
2530
|
+
&& a.shape() == b.shape()
|
|
2531
|
+
&& a.iter()
|
|
2532
|
+
.dims()
|
|
2533
|
+
.zip(b.iter().dims())
|
|
2534
|
+
.all(|(x, y)| is_close((*x).to_f64(), (*y).to_f64(), atol, rtol))
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
impl<C, Scalar: FloatConvertible, const R: usize> AllCloseOps<Scalar, R> for C
|
|
2539
|
+
where
|
|
2540
|
+
C: TensorRef<Scalar, R>,
|
|
2541
|
+
Scalar::DimScalar: NumberLike,
|
|
2542
|
+
{
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
// endregion: Tensor-shaped tolerance equality
|
|
2546
|
+
|
|
2547
|
+
// region: Tensor-shaped scale / sum / blend / fma (moved from crate::tensor)
|
|
2548
|
+
|
|
2549
|
+
/// Extension trait: scalar arithmetic for any [`TensorRef`] implementor.
|
|
2550
|
+
pub trait ScaleOps<Scalar: Clone + EachScale, const MAX_RANK: usize>:
|
|
2551
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2552
|
+
where
|
|
2553
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
2554
|
+
{
|
|
2555
|
+
fn try_add_scalar(
|
|
2556
|
+
&self,
|
|
2557
|
+
scalar: Scalar::Scalar,
|
|
2558
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2559
|
+
self.view().try_add_scalar(scalar)
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
fn try_sub_scalar(
|
|
2563
|
+
&self,
|
|
2564
|
+
scalar: Scalar::Scalar,
|
|
2565
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2566
|
+
self.view().try_sub_scalar(scalar)
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
fn try_mul_scalar(
|
|
2570
|
+
&self,
|
|
2571
|
+
scalar: Scalar::Scalar,
|
|
2572
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2573
|
+
self.view().try_mul_scalar(scalar)
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
impl<Scalar: Clone + EachScale, const R: usize, C: TensorRef<Scalar, R>> ScaleOps<Scalar, R> for C where
|
|
2578
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy
|
|
2579
|
+
{
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
impl<Scalar: Clone + EachScale, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK>
|
|
2583
|
+
where
|
|
2584
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
2585
|
+
{
|
|
2586
|
+
pub fn try_add_scalar_into(
|
|
2587
|
+
&self,
|
|
2588
|
+
scalar: Scalar::Scalar,
|
|
2589
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2590
|
+
) -> Result<(), TensorError> {
|
|
2591
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2592
|
+
view.try_add_scalar_into(scalar, span)
|
|
2593
|
+
})
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
pub fn try_sub_scalar_into(
|
|
2597
|
+
&self,
|
|
2598
|
+
scalar: Scalar::Scalar,
|
|
2599
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2600
|
+
) -> Result<(), TensorError> {
|
|
2601
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2602
|
+
view.try_sub_scalar_into(scalar, span)
|
|
2603
|
+
})
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
pub fn try_mul_scalar_into(
|
|
2607
|
+
&self,
|
|
2608
|
+
scalar: Scalar::Scalar,
|
|
2609
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2610
|
+
) -> Result<(), TensorError> {
|
|
2611
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2612
|
+
view.try_mul_scalar_into(scalar, span)
|
|
2613
|
+
})
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
pub fn try_add_scalar_inplace(&mut self, scalar: Scalar::Scalar) -> Result<(), TensorError> {
|
|
2617
|
+
self.add_scalar_inplace(scalar);
|
|
2618
|
+
Ok(())
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
pub fn try_sub_scalar_inplace(&mut self, scalar: Scalar::Scalar) -> Result<(), TensorError> {
|
|
2622
|
+
self.sub_scalar_inplace(scalar);
|
|
2623
|
+
Ok(())
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
pub fn try_mul_scalar_inplace(&mut self, scalar: Scalar::Scalar) -> Result<(), TensorError> {
|
|
2627
|
+
self.mul_scalar_inplace(scalar);
|
|
2628
|
+
Ok(())
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
/// Element-wise add scalar in-place (infallible — self vs self always matches).
|
|
2632
|
+
pub fn add_scalar_inplace(&mut self, scalar: Scalar::Scalar) {
|
|
2633
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_add_scalar_into(scalar, span))
|
|
2634
|
+
.expect("inplace scalar op on self cannot fail")
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
/// Element-wise subtract scalar in-place (infallible — self vs self always matches).
|
|
2638
|
+
pub fn sub_scalar_inplace(&mut self, scalar: Scalar::Scalar) {
|
|
2639
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_sub_scalar_into(scalar, span))
|
|
2640
|
+
.expect("inplace scalar op on self cannot fail")
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
/// Element-wise multiply scalar in-place (infallible — self vs self always matches).
|
|
2644
|
+
pub fn mul_scalar_inplace(&mut self, scalar: Scalar::Scalar) {
|
|
2645
|
+
try_reborrow_tensor_inplace(self, |view, span| view.try_mul_scalar_into(scalar, span))
|
|
2646
|
+
.expect("inplace scalar op on self cannot fail")
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
impl<Scalar: Clone + EachScale, const MAX_RANK: usize> core::ops::AddAssign<Scalar::Scalar>
|
|
2651
|
+
for Tensor<Scalar, Global, MAX_RANK>
|
|
2652
|
+
where
|
|
2653
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
2654
|
+
{
|
|
2655
|
+
fn add_assign(&mut self, scalar: Scalar::Scalar) {
|
|
2656
|
+
self.add_scalar_inplace(scalar);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
impl<Scalar: Clone + EachScale, const MAX_RANK: usize> core::ops::SubAssign<Scalar::Scalar>
|
|
2661
|
+
for Tensor<Scalar, Global, MAX_RANK>
|
|
2662
|
+
where
|
|
2663
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
2664
|
+
{
|
|
2665
|
+
fn sub_assign(&mut self, scalar: Scalar::Scalar) {
|
|
2666
|
+
self.sub_scalar_inplace(scalar);
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
impl<Scalar: Clone + EachScale, const MAX_RANK: usize> core::ops::MulAssign<Scalar::Scalar>
|
|
2671
|
+
for Tensor<Scalar, Global, MAX_RANK>
|
|
2672
|
+
where
|
|
2673
|
+
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
2674
|
+
{
|
|
2675
|
+
fn mul_assign(&mut self, scalar: Scalar::Scalar) {
|
|
2676
|
+
self.mul_scalar_inplace(scalar);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
/// Extension trait: element-wise addition for any [`TensorRef`] implementor.
|
|
2681
|
+
pub trait SumOps<Scalar: Clone + EachSum, const MAX_RANK: usize>:
|
|
2682
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2683
|
+
{
|
|
2684
|
+
fn try_add_tensor(
|
|
2685
|
+
&self,
|
|
2686
|
+
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
2687
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2688
|
+
self.view().try_add_tensor(&other.view())
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
impl<Scalar: Clone + EachSum, const R: usize, C: TensorRef<Scalar, R>> SumOps<Scalar, R> for C {}
|
|
2693
|
+
|
|
2694
|
+
impl<Scalar: Clone + EachSum, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
2695
|
+
pub fn try_add_tensor_into(
|
|
2696
|
+
&self,
|
|
2697
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2698
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2699
|
+
) -> Result<(), TensorError> {
|
|
2700
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2701
|
+
view.try_add_tensor_into(&other.view(), span)
|
|
2702
|
+
})
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
pub fn try_add_tensor_inplace(
|
|
2706
|
+
&mut self,
|
|
2707
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2708
|
+
) -> Result<(), TensorError> {
|
|
2709
|
+
try_reborrow_tensor_inplace(self, |view, span| {
|
|
2710
|
+
view.try_add_tensor_into(&other.view(), span)
|
|
2711
|
+
})
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
/// Extension trait: element-wise subtraction for any [`TensorRef`] implementor.
|
|
2716
|
+
pub trait BlendOps<Scalar: Clone + EachBlend, const MAX_RANK: usize>:
|
|
2717
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2718
|
+
where
|
|
2719
|
+
Scalar::Scalar: From<f32> + Copy,
|
|
2720
|
+
{
|
|
2721
|
+
fn try_sub_tensor(
|
|
2722
|
+
&self,
|
|
2723
|
+
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
2724
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2725
|
+
self.view().try_sub_tensor(&other.view())
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
impl<Scalar: Clone + EachBlend, const R: usize, C: TensorRef<Scalar, R>> BlendOps<Scalar, R> for C where
|
|
2730
|
+
Scalar::Scalar: From<f32> + Copy
|
|
2731
|
+
{
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
impl<Scalar: Clone + EachBlend, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK>
|
|
2735
|
+
where
|
|
2736
|
+
Scalar::Scalar: From<f32> + Copy,
|
|
2737
|
+
{
|
|
2738
|
+
pub fn try_sub_tensor_into(
|
|
2739
|
+
&self,
|
|
2740
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2741
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2742
|
+
) -> Result<(), TensorError> {
|
|
2743
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2744
|
+
view.try_sub_tensor_into(&other.view(), span)
|
|
2745
|
+
})
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
pub fn try_sub_tensor_inplace(
|
|
2749
|
+
&mut self,
|
|
2750
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2751
|
+
) -> Result<(), TensorError> {
|
|
2752
|
+
try_reborrow_tensor_inplace(self, |view, span| {
|
|
2753
|
+
view.try_sub_tensor_into(&other.view(), span)
|
|
2754
|
+
})
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
/// Extension trait: element-wise multiplication for any [`TensorRef`] implementor.
|
|
2759
|
+
pub trait FmaOps<Scalar: Clone + EachFMA, const MAX_RANK: usize>:
|
|
2760
|
+
TensorRef<Scalar, MAX_RANK>
|
|
2761
|
+
where
|
|
2762
|
+
Scalar::Scalar: From<f32> + Copy,
|
|
2763
|
+
{
|
|
2764
|
+
fn try_mul_tensor(
|
|
2765
|
+
&self,
|
|
2766
|
+
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
2767
|
+
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
2768
|
+
self.view().try_mul_tensor(&other.view())
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
impl<Scalar: Clone + EachFMA, const R: usize, C: TensorRef<Scalar, R>> FmaOps<Scalar, R> for C where
|
|
2773
|
+
Scalar::Scalar: From<f32> + Copy
|
|
2774
|
+
{
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
impl<Scalar: Clone + EachFMA, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK>
|
|
2778
|
+
where
|
|
2779
|
+
Scalar::Scalar: From<f32> + Copy,
|
|
2780
|
+
{
|
|
2781
|
+
pub fn try_mul_tensor_into(
|
|
2782
|
+
&self,
|
|
2783
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2784
|
+
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
2785
|
+
) -> Result<(), TensorError> {
|
|
2786
|
+
try_reborrow_tensor_into(self, out, |view, span| {
|
|
2787
|
+
view.try_mul_tensor_into(&other.view(), span)
|
|
2788
|
+
})
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
pub fn try_mul_tensor_inplace(
|
|
2792
|
+
&mut self,
|
|
2793
|
+
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
2794
|
+
) -> Result<(), TensorError> {
|
|
2795
|
+
try_reborrow_tensor_inplace(self, |view, span| {
|
|
2796
|
+
view.try_mul_tensor_into(&other.view(), span)
|
|
2797
|
+
})
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
// endregion: Tensor-shaped scale / sum / blend / fma
|
|
2802
|
+
|
|
2406
2803
|
#[cfg(test)]
|
|
2407
2804
|
mod tests {
|
|
2408
2805
|
use super::*;
|
|
@@ -2718,4 +3115,80 @@ mod tests {
|
|
|
2718
3115
|
}
|
|
2719
3116
|
|
|
2720
3117
|
// endregion
|
|
3118
|
+
|
|
3119
|
+
// region: tensor-shaped wrappers (ScaleOps / SumOps / TrigSinOps)
|
|
3120
|
+
|
|
3121
|
+
#[test]
|
|
3122
|
+
fn tensor_add_tensor_via_sum_ops() {
|
|
3123
|
+
use crate::tensor::{SliceRange, Tensor};
|
|
3124
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
3125
|
+
let left = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
3126
|
+
let right = Tensor::<f32>::try_full(&[3, 4], 2.0).unwrap();
|
|
3127
|
+
|
|
3128
|
+
let left_even = left
|
|
3129
|
+
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
3130
|
+
.unwrap();
|
|
3131
|
+
let right_even = right
|
|
3132
|
+
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
3133
|
+
.unwrap();
|
|
3134
|
+
|
|
3135
|
+
let added = left_even.try_add_tensor(&right_even).unwrap();
|
|
3136
|
+
assert_eq!(added.shape(), &[3, 2]);
|
|
3137
|
+
assert_eq!(added.as_slice(), &[2.0, 4.0, 6.0, 8.0, 10.0, 12.0]);
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
#[test]
|
|
3141
|
+
fn tensor_add_tensor_into_owning_destination() {
|
|
3142
|
+
use crate::tensor::Tensor;
|
|
3143
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
3144
|
+
let left = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
3145
|
+
let right = Tensor::<f32>::try_full(&[3, 4], 2.0).unwrap();
|
|
3146
|
+
|
|
3147
|
+
let mut out = Tensor::<f32>::try_full(&[3, 4], 0.0).unwrap();
|
|
3148
|
+
left.try_add_tensor_into(&right, &mut out).unwrap();
|
|
3149
|
+
assert_eq!(out.as_slice()[0], 2.0);
|
|
3150
|
+
assert_eq!(out.as_slice()[11], 13.0);
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
#[test]
|
|
3154
|
+
fn tensor_mul_scalar_via_scale_ops() {
|
|
3155
|
+
use crate::tensor::{SliceRange, Tensor};
|
|
3156
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
3157
|
+
let source = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
3158
|
+
let even = source
|
|
3159
|
+
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
3160
|
+
.unwrap();
|
|
3161
|
+
let scaled = even.try_mul_scalar(0.5).unwrap();
|
|
3162
|
+
assert_eq!(scaled.as_slice(), &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
#[test]
|
|
3166
|
+
fn tensor_add_scalar_inplace_via_scale_ops() {
|
|
3167
|
+
use crate::tensor::Tensor;
|
|
3168
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
3169
|
+
let mut tensor = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
3170
|
+
tensor.try_add_scalar_inplace(1.0).unwrap();
|
|
3171
|
+
assert_eq!(tensor.as_slice()[0], 1.0);
|
|
3172
|
+
assert_eq!(tensor.as_slice()[11], 12.0);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
#[test]
|
|
3176
|
+
fn tensor_sin_into_via_trig_sin_ops() {
|
|
3177
|
+
use crate::tensor::{SliceRange, Tensor};
|
|
3178
|
+
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
3179
|
+
let source = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
3180
|
+
let even = source
|
|
3181
|
+
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
3182
|
+
.unwrap();
|
|
3183
|
+
let mut sin_out = Tensor::<f32>::try_full(&[3, 2], 0.0).unwrap();
|
|
3184
|
+
{
|
|
3185
|
+
let mut span = sin_out.span();
|
|
3186
|
+
even.try_sin_into(&mut span).unwrap();
|
|
3187
|
+
}
|
|
3188
|
+
assert_eq!(sin_out.shape(), &[3, 2]);
|
|
3189
|
+
// First element is sin(0) which is exactly 0.
|
|
3190
|
+
assert!((sin_out.as_slice()[0] - 0.0).abs() < 1e-6);
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// endregion
|
|
2721
3194
|
}
|