usearch 2.26.0 → 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 +2 -2
- package/cpp/test.cpp +66 -3
- package/include/usearch/index.hpp +9 -1
- package/include/usearch/index_dense.hpp +16 -5
- 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/rust/tensor.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//! Core N-dimensional tensor types
|
|
1
|
+
//! Core N-dimensional tensor types and the `TensorRef` / `TensorMut` structural traits.
|
|
2
2
|
//!
|
|
3
3
|
//! This module provides:
|
|
4
4
|
//!
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
//! - [`TensorView`]: Immutable view into a tensor
|
|
7
7
|
//! - [`TensorSpan`]: Mutable view into a tensor
|
|
8
8
|
//! - [`Matrix`]: Type alias for 2D tensors
|
|
9
|
+
//! - [`TensorRef`] / [`TensorMut`]: Structural traits implemented by all three above
|
|
10
|
+
//! - [`Fill`] / [`CopyFrom`]: Container-level traits for in-place fill and slice/view-to-span copies
|
|
11
|
+
//!
|
|
12
|
+
//! Operation extension traits live in their respective domain modules so each `<op>.rs`
|
|
13
|
+
//! mirrors the C++ `<op>.hpp` layout:
|
|
14
|
+
//! - [`crate::each`]: `ScaleOps` / `SumOps` / `BlendOps` / `FmaOps` / `TrigSinOps` /
|
|
15
|
+
//! `TrigCosOps` / `TrigAtanOps` / `AllCloseOps`
|
|
16
|
+
//! - [`crate::reduce`]: `MomentsOps` / `MinMaxOps` / `BitwiseReductions`
|
|
17
|
+
//! - [`crate::cast`]: `CastOps`
|
|
9
18
|
//!
|
|
10
19
|
//! Batch matrix operations (GEMM, packed spatial distances) live in [`crate::matrix`].
|
|
11
20
|
//!
|
|
@@ -56,9 +65,9 @@ use core::ptr::NonNull;
|
|
|
56
65
|
|
|
57
66
|
use crate::cast::{cast, CastDtype};
|
|
58
67
|
use crate::each::{EachATan, EachBlend, EachCos, EachFMA, EachScale, EachSin, EachSum};
|
|
59
|
-
use crate::reduce::{ReduceMinMax, ReduceMoments};
|
|
68
|
+
use crate::reduce::{MomentsOps, ReduceMinMax, ReduceMoments, SumSqToF64};
|
|
60
69
|
use crate::spatial::{Dot, Roots};
|
|
61
|
-
use crate::types::{DimMut, DimRef, FloatConvertible,
|
|
70
|
+
use crate::types::{DimMut, DimRef, FloatConvertible, StorageElement};
|
|
62
71
|
use crate::vector::{Vector, VectorIndex};
|
|
63
72
|
|
|
64
73
|
// region: Constants and Allocator
|
|
@@ -248,11 +257,13 @@ pub struct MinMaxResult<Value, AnyIndex = usize> {
|
|
|
248
257
|
/// let b_packed = PackedMatrix::try_pack(&b).unwrap();
|
|
249
258
|
/// let c = a.dots_packed(&b_packed); // Returns (1024 × 256)
|
|
250
259
|
/// ```
|
|
251
|
-
pub struct Tensor<
|
|
260
|
+
pub struct Tensor<
|
|
261
|
+
Scalar: StorageElement,
|
|
262
|
+
Alloc: Allocator = Global,
|
|
263
|
+
const MAX_RANK: usize = DEFAULT_MAX_RANK,
|
|
264
|
+
> {
|
|
252
265
|
/// Raw pointer to data buffer.
|
|
253
266
|
data: NonNull<Scalar>,
|
|
254
|
-
/// Total allocation size in bytes (used by Drop and as_slice).
|
|
255
|
-
alloc_bytes: usize,
|
|
256
267
|
/// Shape dimensions (always logical).
|
|
257
268
|
shape: [usize; MAX_RANK],
|
|
258
269
|
/// Strides in bytes.
|
|
@@ -264,28 +275,37 @@ pub struct Tensor<Scalar, Alloc: Allocator = Global, const MAX_RANK: usize = DEF
|
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
// Safety: Tensor owns its data and Scalar: Send implies the array is Send
|
|
267
|
-
unsafe impl<Scalar: Send, Alloc: Allocator + Send, const MAX_RANK: usize> Send
|
|
278
|
+
unsafe impl<Scalar: StorageElement + Send, Alloc: Allocator + Send, const MAX_RANK: usize> Send
|
|
268
279
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
269
280
|
{
|
|
270
281
|
}
|
|
271
282
|
// Safety: Tensor has no interior mutability, &Tensor<Scalar> is safe to share if Scalar: Sync
|
|
272
|
-
unsafe impl<Scalar: Sync, Alloc: Allocator + Sync, const MAX_RANK: usize> Sync
|
|
283
|
+
unsafe impl<Scalar: StorageElement + Sync, Alloc: Allocator + Sync, const MAX_RANK: usize> Sync
|
|
273
284
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
274
285
|
{
|
|
275
286
|
}
|
|
276
287
|
|
|
277
|
-
impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Drop
|
|
288
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> Drop
|
|
289
|
+
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
290
|
+
{
|
|
278
291
|
fn drop(&mut self) {
|
|
279
|
-
if self.
|
|
292
|
+
let storage_count = if self.ndim == 0 {
|
|
293
|
+
0
|
|
294
|
+
} else {
|
|
295
|
+
let total: usize = self.shape[..self.ndim].iter().product();
|
|
296
|
+
total / Scalar::dimensions_per_value()
|
|
297
|
+
};
|
|
298
|
+
if storage_count > 0 {
|
|
280
299
|
unsafe {
|
|
281
|
-
let count = self.alloc_bytes / core::mem::size_of::<Scalar>();
|
|
282
300
|
core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut(
|
|
283
301
|
self.data.as_ptr(),
|
|
284
|
-
|
|
302
|
+
storage_count,
|
|
285
303
|
));
|
|
286
|
-
let layout =
|
|
287
|
-
|
|
288
|
-
|
|
304
|
+
let layout = alloc::alloc::Layout::from_size_align(
|
|
305
|
+
storage_count * core::mem::size_of::<Scalar>(),
|
|
306
|
+
SIMD_ALIGNMENT,
|
|
307
|
+
)
|
|
308
|
+
.unwrap();
|
|
289
309
|
self.alloc.deallocate(
|
|
290
310
|
NonNull::new_unchecked(self.data.as_ptr() as *mut u8),
|
|
291
311
|
layout,
|
|
@@ -385,7 +405,6 @@ impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
|
385
405
|
|
|
386
406
|
Ok(Self {
|
|
387
407
|
data,
|
|
388
|
-
alloc_bytes: storage_count * core::mem::size_of::<Scalar>(),
|
|
389
408
|
shape: shape_arr,
|
|
390
409
|
strides: strides_arr,
|
|
391
410
|
ndim: shape.len(),
|
|
@@ -470,7 +489,6 @@ impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
|
470
489
|
|
|
471
490
|
Ok(Self {
|
|
472
491
|
data,
|
|
473
|
-
alloc_bytes: storage_count * core::mem::size_of::<Scalar>(),
|
|
474
492
|
shape: shape_arr,
|
|
475
493
|
strides: strides_arr,
|
|
476
494
|
ndim: shape.len(),
|
|
@@ -544,7 +562,6 @@ impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
|
544
562
|
|
|
545
563
|
Ok(Self {
|
|
546
564
|
data: ptr,
|
|
547
|
-
alloc_bytes: expected_storage * core::mem::size_of::<Scalar>(),
|
|
548
565
|
shape: shape_arr,
|
|
549
566
|
strides: strides_arr,
|
|
550
567
|
ndim: shape.len(),
|
|
@@ -667,27 +684,29 @@ impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
|
667
684
|
return Err(TensorError::NonContiguousRows);
|
|
668
685
|
}
|
|
669
686
|
let dims = self.shape[0];
|
|
670
|
-
let values = dims.div_ceil(Scalar::dimensions_per_value());
|
|
671
687
|
let data = self.data;
|
|
672
688
|
let alloc = unsafe { core::ptr::read(&self.alloc) };
|
|
673
689
|
core::mem::forget(self);
|
|
674
|
-
// SAFETY:
|
|
675
|
-
Ok(unsafe { Vector::from_raw_parts(data, dims,
|
|
690
|
+
// SAFETY: storage size derived from `dims` matches the original allocation.
|
|
691
|
+
Ok(unsafe { Vector::from_raw_parts(data, dims, alloc) })
|
|
676
692
|
}
|
|
677
693
|
}
|
|
678
694
|
|
|
679
695
|
// Methods that don't require Clone
|
|
680
|
-
impl<Scalar, Alloc: Allocator, const MAX_RANK: usize>
|
|
696
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
697
|
+
Tensor<Scalar, Alloc, MAX_RANK>
|
|
698
|
+
{
|
|
681
699
|
/// Construct a tensor from raw parts, taking ownership of the allocation.
|
|
682
700
|
///
|
|
683
701
|
/// # Safety
|
|
684
|
-
/// - `data` must point to
|
|
685
|
-
///
|
|
702
|
+
/// - `data` must point to an allocation obtained from `alloc`, aligned to
|
|
703
|
+
/// [`SIMD_ALIGNMENT`], whose backing buffer is sized for the storage count
|
|
704
|
+
/// implied by `shape` (`product(shape) / Scalar::dimensions_per_value()`
|
|
705
|
+
/// slots of `Scalar`).
|
|
686
706
|
/// - `shape`, `strides`, and `ndim` must be consistent with the data layout.
|
|
687
707
|
/// - The caller must not free the memory (this tensor takes ownership).
|
|
688
708
|
pub unsafe fn from_raw_parts(
|
|
689
709
|
data: NonNull<Scalar>,
|
|
690
|
-
alloc_bytes: usize,
|
|
691
710
|
shape: [usize; MAX_RANK],
|
|
692
711
|
strides: [isize; MAX_RANK],
|
|
693
712
|
ndim: usize,
|
|
@@ -695,7 +714,6 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_
|
|
|
695
714
|
) -> Self {
|
|
696
715
|
Self {
|
|
697
716
|
data,
|
|
698
|
-
alloc_bytes,
|
|
699
717
|
shape,
|
|
700
718
|
strides,
|
|
701
719
|
ndim,
|
|
@@ -728,7 +746,7 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_
|
|
|
728
746
|
|
|
729
747
|
/// Returns true if the array has no elements.
|
|
730
748
|
pub fn is_empty(&self) -> bool {
|
|
731
|
-
self.
|
|
749
|
+
self.numel() == 0
|
|
732
750
|
}
|
|
733
751
|
|
|
734
752
|
/// Returns the stride in bytes for the given dimension.
|
|
@@ -751,7 +769,7 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_
|
|
|
751
769
|
/// For sub-byte types, the slice contains packed storage values, not
|
|
752
770
|
/// individual logical elements.
|
|
753
771
|
pub fn as_slice(&self) -> &[Scalar] {
|
|
754
|
-
let count = self.
|
|
772
|
+
let count = self.numel() / Scalar::dimensions_per_value();
|
|
755
773
|
unsafe { core::slice::from_raw_parts(self.data.as_ptr(), count) }
|
|
756
774
|
}
|
|
757
775
|
|
|
@@ -760,7 +778,7 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_
|
|
|
760
778
|
/// For sub-byte types, the slice contains packed storage values, not
|
|
761
779
|
/// individual logical elements.
|
|
762
780
|
pub fn as_mut_slice(&mut self) -> &mut [Scalar] {
|
|
763
|
-
let count = self.
|
|
781
|
+
let count = self.numel() / Scalar::dimensions_per_value();
|
|
764
782
|
unsafe { core::slice::from_raw_parts_mut(self.data.as_ptr(), count) }
|
|
765
783
|
}
|
|
766
784
|
|
|
@@ -2570,6 +2588,37 @@ pub trait TensorMut<Scalar: StorageElement, const MAX_RANK: usize>:
|
|
|
2570
2588
|
fn as_mut_ptr(&mut self) -> *mut Scalar;
|
|
2571
2589
|
}
|
|
2572
2590
|
|
|
2591
|
+
/// In-place fill operations shared by every mutable container of `Scalar`.
|
|
2592
|
+
///
|
|
2593
|
+
/// Implemented for [`Tensor`], [`TensorSpan`], [`crate::vector::Vector`], and
|
|
2594
|
+
/// [`crate::vector::VectorSpan`]. Mirrors `tensor_span::fill_zeros / fill` and
|
|
2595
|
+
/// `vector_span::fill_zeros / fill` from the C++ side. Both methods are
|
|
2596
|
+
/// infallible and operate on every storage byte the container owns or borrows.
|
|
2597
|
+
pub trait Fill<Scalar: StorageElement> {
|
|
2598
|
+
/// Set every storage byte to zero. Works for every `StorageElement` since
|
|
2599
|
+
/// the binary-zero of all NumKong scalar dtypes is the value-zero.
|
|
2600
|
+
fn fill_zeros(&mut self);
|
|
2601
|
+
|
|
2602
|
+
/// Set every storage element to `value`. For 1-byte storage (including
|
|
2603
|
+
/// sub-byte packings) this becomes a single byte-pattern memset; for
|
|
2604
|
+
/// multi-byte storage it falls through to a typed broadcast loop on the
|
|
2605
|
+
/// pre-zeroed buffer (so float NaN/inf patterns from uninitialised memory
|
|
2606
|
+
/// cannot leak through).
|
|
2607
|
+
fn fill(&mut self, value: Scalar);
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
/// Copy storage from a `Source` into this container in place.
|
|
2611
|
+
///
|
|
2612
|
+
/// `Source` is the natural read-side counterpart for the implementor:
|
|
2613
|
+
/// `&[Scalar]` (raw storage slice) for owning `Tensor` / `Vector`, or the
|
|
2614
|
+
/// matching `View` type for span implementors. Mirrors `tensor_span::copy_from`
|
|
2615
|
+
/// and `vector_span::copy_from` on the C++ side.
|
|
2616
|
+
pub trait CopyFrom<Source> {
|
|
2617
|
+
/// Copy from `source` into self. Returns an error on a shape or storage
|
|
2618
|
+
/// mismatch and does not modify the destination on error.
|
|
2619
|
+
fn copy_from(&mut self, source: Source) -> Result<(), TensorError>;
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2573
2622
|
impl<Scalar: StorageElement, Alloc: Allocator, const R: usize> TensorRef<Scalar, R>
|
|
2574
2623
|
for Tensor<Scalar, Alloc, R>
|
|
2575
2624
|
{
|
|
@@ -2660,6 +2709,191 @@ impl<'a, Scalar: StorageElement, const R: usize> TensorMut<Scalar, R>
|
|
|
2660
2709
|
|
|
2661
2710
|
// endregion: TensorRef / TensorMut Traits
|
|
2662
2711
|
|
|
2712
|
+
// region: Fill / CopyFrom Implementations
|
|
2713
|
+
|
|
2714
|
+
/// Compares the bit pattern of a fresh `Scalar::default()` to `value` to decide
|
|
2715
|
+
/// whether the post-`fill_zeros` overlay can be skipped. Treats `+0.0` and the
|
|
2716
|
+
/// default `Scalar::default()` representation as identical so a `fill(0.0)` on a
|
|
2717
|
+
/// freshly-defaulted buffer is a single memset.
|
|
2718
|
+
#[inline]
|
|
2719
|
+
fn value_is_default_bit_pattern<Scalar: StorageElement>(value: Scalar) -> bool {
|
|
2720
|
+
let default_value = Scalar::default();
|
|
2721
|
+
let value_bytes = unsafe {
|
|
2722
|
+
core::slice::from_raw_parts(
|
|
2723
|
+
(&value as *const Scalar) as *const u8,
|
|
2724
|
+
core::mem::size_of::<Scalar>(),
|
|
2725
|
+
)
|
|
2726
|
+
};
|
|
2727
|
+
let default_bytes = unsafe {
|
|
2728
|
+
core::slice::from_raw_parts(
|
|
2729
|
+
(&default_value as *const Scalar) as *const u8,
|
|
2730
|
+
core::mem::size_of::<Scalar>(),
|
|
2731
|
+
)
|
|
2732
|
+
};
|
|
2733
|
+
value_bytes == default_bytes
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
/// Body shared by every `Fill::fill` impl: zero the buffer, then byte-pattern
|
|
2737
|
+
/// memset (1-byte storage) or typed broadcast (multi-byte storage).
|
|
2738
|
+
#[inline]
|
|
2739
|
+
unsafe fn overlay_value_into_storage<Scalar: StorageElement>(
|
|
2740
|
+
storage_ptr: *mut Scalar,
|
|
2741
|
+
storage_count: usize,
|
|
2742
|
+
value: Scalar,
|
|
2743
|
+
) {
|
|
2744
|
+
if storage_count == 0 {
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
if value_is_default_bit_pattern(value) {
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
if core::mem::size_of::<Scalar>() == 1 {
|
|
2751
|
+
let value_byte = *((&value as *const Scalar) as *const u8);
|
|
2752
|
+
core::ptr::write_bytes(storage_ptr as *mut u8, value_byte, storage_count);
|
|
2753
|
+
} else {
|
|
2754
|
+
for storage_index in 0..storage_count {
|
|
2755
|
+
core::ptr::write(storage_ptr.add(storage_index), value);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> Fill<Scalar>
|
|
2761
|
+
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
2762
|
+
{
|
|
2763
|
+
fn fill_zeros(&mut self) {
|
|
2764
|
+
let storage_count = self.numel() / Scalar::dimensions_per_value();
|
|
2765
|
+
if storage_count == 0 {
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
unsafe {
|
|
2769
|
+
core::ptr::write_bytes(self.data.as_ptr(), 0, storage_count);
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
fn fill(&mut self, value: Scalar) {
|
|
2774
|
+
self.fill_zeros();
|
|
2775
|
+
let storage_count = self.numel() / Scalar::dimensions_per_value();
|
|
2776
|
+
unsafe {
|
|
2777
|
+
overlay_value_into_storage(self.data.as_ptr(), storage_count, value);
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> CopyFrom<&[Scalar]>
|
|
2783
|
+
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
2784
|
+
{
|
|
2785
|
+
fn copy_from(&mut self, source: &[Scalar]) -> Result<(), TensorError> {
|
|
2786
|
+
let storage_count = self.storage_len();
|
|
2787
|
+
if source.len() != storage_count {
|
|
2788
|
+
return Err(TensorError::ShapeMismatch {
|
|
2789
|
+
axis: 0,
|
|
2790
|
+
expected: storage_count,
|
|
2791
|
+
got: source.len(),
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
if storage_count == 0 {
|
|
2795
|
+
return Ok(());
|
|
2796
|
+
}
|
|
2797
|
+
unsafe {
|
|
2798
|
+
core::ptr::copy_nonoverlapping(source.as_ptr(), self.data.as_ptr(), storage_count);
|
|
2799
|
+
}
|
|
2800
|
+
Ok(())
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
impl<'a, Scalar: StorageElement, const MAX_RANK: usize> Fill<Scalar>
|
|
2805
|
+
for TensorSpan<'a, Scalar, MAX_RANK>
|
|
2806
|
+
{
|
|
2807
|
+
fn fill_zeros(&mut self) {
|
|
2808
|
+
// Contiguous fast path: one write_bytes for the whole span.
|
|
2809
|
+
if self.is_contiguous() {
|
|
2810
|
+
let storage_count = self.numel().div_ceil(Scalar::dimensions_per_value());
|
|
2811
|
+
if storage_count == 0 {
|
|
2812
|
+
return;
|
|
2813
|
+
}
|
|
2814
|
+
unsafe {
|
|
2815
|
+
core::ptr::write_bytes(self.data, 0, storage_count);
|
|
2816
|
+
}
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
// Strided path: walk the leading axis and recurse.
|
|
2820
|
+
if self.ndim == 0 {
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
if let Ok(axis_iter) = self.axis_spans(0usize) {
|
|
2824
|
+
for mut sub_span in axis_iter {
|
|
2825
|
+
sub_span.fill_zeros();
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
fn fill(&mut self, value: Scalar) {
|
|
2831
|
+
if self.is_contiguous() {
|
|
2832
|
+
let storage_count = self.numel().div_ceil(Scalar::dimensions_per_value());
|
|
2833
|
+
if storage_count == 0 {
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2836
|
+
unsafe {
|
|
2837
|
+
core::ptr::write_bytes(self.data, 0, storage_count);
|
|
2838
|
+
overlay_value_into_storage(self.data, storage_count, value);
|
|
2839
|
+
}
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
if self.ndim == 0 {
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
if let Ok(axis_iter) = self.axis_spans(0usize) {
|
|
2846
|
+
for mut sub_span in axis_iter {
|
|
2847
|
+
sub_span.fill(value);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
impl<'a, 'b, Scalar: StorageElement, const MAX_RANK: usize>
|
|
2854
|
+
CopyFrom<&'b TensorView<'_, Scalar, MAX_RANK>> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
2855
|
+
{
|
|
2856
|
+
fn copy_from(
|
|
2857
|
+
&mut self,
|
|
2858
|
+
source: &'b TensorView<'_, Scalar, MAX_RANK>,
|
|
2859
|
+
) -> Result<(), TensorError> {
|
|
2860
|
+
if self.shape() != source.shape() {
|
|
2861
|
+
return Err(TensorError::ShapeMismatch {
|
|
2862
|
+
axis: 0,
|
|
2863
|
+
expected: source.numel(),
|
|
2864
|
+
got: self.numel(),
|
|
2865
|
+
});
|
|
2866
|
+
}
|
|
2867
|
+
if self.is_empty() {
|
|
2868
|
+
return Ok(());
|
|
2869
|
+
}
|
|
2870
|
+
// Contiguous fast path: memcpy the whole storage at once.
|
|
2871
|
+
if self.is_contiguous() && source.is_contiguous() {
|
|
2872
|
+
let storage_count = self.numel().div_ceil(Scalar::dimensions_per_value());
|
|
2873
|
+
unsafe {
|
|
2874
|
+
core::ptr::copy_nonoverlapping(source.as_ptr(), self.data, storage_count);
|
|
2875
|
+
}
|
|
2876
|
+
return Ok(());
|
|
2877
|
+
}
|
|
2878
|
+
// Strided: walk the leading axis on both source and destination, recurse.
|
|
2879
|
+
if self.ndim == 0 {
|
|
2880
|
+
return Ok(());
|
|
2881
|
+
}
|
|
2882
|
+
let source_axis = source.axis_views(0usize)?;
|
|
2883
|
+
let mut destination_axis = self.axis_spans(0usize)?;
|
|
2884
|
+
for source_sub in source_axis {
|
|
2885
|
+
let mut destination_sub = match destination_axis.next() {
|
|
2886
|
+
Some(span) => span,
|
|
2887
|
+
None => break,
|
|
2888
|
+
};
|
|
2889
|
+
destination_sub.copy_from(&source_sub)?;
|
|
2890
|
+
}
|
|
2891
|
+
Ok(())
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
// endregion: Fill / CopyFrom Implementations
|
|
2896
|
+
|
|
2663
2897
|
// region: AxisIterator
|
|
2664
2898
|
|
|
2665
2899
|
/// Iterator over sub-tensor views along a given axis.
|
|
@@ -2993,7 +3227,7 @@ impl<'a, Scalar, const MAX_RANK: usize> TensorSpan<'a, Scalar, MAX_RANK> {
|
|
|
2993
3227
|
}
|
|
2994
3228
|
}
|
|
2995
3229
|
|
|
2996
|
-
impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_RANK> {
|
|
3230
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_RANK> {
|
|
2997
3231
|
/// Iterate along the given axis, yielding sub-tensor views with rank-1.
|
|
2998
3232
|
pub fn axis_views<AnyIndex: VectorIndex>(
|
|
2999
3233
|
&self,
|
|
@@ -3032,7 +3266,7 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_
|
|
|
3032
3266
|
|
|
3033
3267
|
// region: Debug
|
|
3034
3268
|
|
|
3035
|
-
impl<Scalar: core::fmt::Debug, Alloc: Allocator, const MAX_RANK: usize> core::fmt::Debug
|
|
3269
|
+
impl<Scalar: StorageElement + core::fmt::Debug, Alloc: Allocator, const MAX_RANK: usize> core::fmt::Debug
|
|
3036
3270
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
3037
3271
|
{
|
|
3038
3272
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
@@ -3469,7 +3703,7 @@ impl<'a, Scalar: FloatConvertible, const MAX_RANK: usize> IntoIterator
|
|
|
3469
3703
|
|
|
3470
3704
|
// region: PartialEq
|
|
3471
3705
|
|
|
3472
|
-
impl<Scalar: PartialEq, Alloc: Allocator, const MAX_RANK: usize> PartialEq
|
|
3706
|
+
impl<Scalar: StorageElement + PartialEq, Alloc: Allocator, const MAX_RANK: usize> PartialEq
|
|
3473
3707
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
3474
3708
|
{
|
|
3475
3709
|
fn eq(&self, other: &Self) -> bool {
|
|
@@ -3513,46 +3747,11 @@ where
|
|
|
3513
3747
|
|
|
3514
3748
|
// endregion: PartialEq
|
|
3515
3749
|
|
|
3516
|
-
//
|
|
3517
|
-
|
|
3518
|
-
/// Extension trait: tolerance-based equality for any [`TensorRef`] implementor.
|
|
3519
|
-
///
|
|
3520
|
-
/// Uses the formula `|a - b| <= atol + rtol * |b|` per element.
|
|
3521
|
-
/// Returns `false` if shapes differ.
|
|
3522
|
-
pub trait AllCloseOps<Scalar: FloatConvertible, const MAX_RANK: usize>:
|
|
3523
|
-
TensorRef<Scalar, MAX_RANK>
|
|
3524
|
-
where
|
|
3525
|
-
Scalar::DimScalar: NumberLike,
|
|
3526
|
-
{
|
|
3527
|
-
fn allclose(
|
|
3528
|
-
&self,
|
|
3529
|
-
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
3530
|
-
atol: f64,
|
|
3531
|
-
rtol: f64,
|
|
3532
|
-
) -> bool {
|
|
3533
|
-
let a = self.view();
|
|
3534
|
-
let b = other.view();
|
|
3535
|
-
a.ndim() == b.ndim()
|
|
3536
|
-
&& a.shape() == b.shape()
|
|
3537
|
-
&& a.iter()
|
|
3538
|
-
.dims()
|
|
3539
|
-
.zip(b.iter().dims())
|
|
3540
|
-
.all(|(x, y)| crate::types::is_close((*x).to_f64(), (*y).to_f64(), atol, rtol))
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
|
-
impl<C, Scalar: FloatConvertible, const R: usize> AllCloseOps<Scalar, R> for C
|
|
3545
|
-
where
|
|
3546
|
-
C: TensorRef<Scalar, R>,
|
|
3547
|
-
Scalar::DimScalar: NumberLike,
|
|
3548
|
-
{
|
|
3549
|
-
}
|
|
3550
|
-
|
|
3551
|
-
// endregion: Tolerance Equality
|
|
3750
|
+
// endregion: Tolerance Equality (AllCloseOps moved to crate::each)
|
|
3552
3751
|
|
|
3553
3752
|
// region: AsRef
|
|
3554
3753
|
|
|
3555
|
-
impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> AsRef<[Scalar]>
|
|
3754
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> AsRef<[Scalar]>
|
|
3556
3755
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
3557
3756
|
{
|
|
3558
3757
|
fn as_ref(&self) -> &[Scalar] {
|
|
@@ -3564,7 +3763,7 @@ impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> AsRef<[Scalar]>
|
|
|
3564
3763
|
|
|
3565
3764
|
// region: Tensor View and Slice Methods
|
|
3566
3765
|
|
|
3567
|
-
impl<Scalar, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_RANK> {
|
|
3766
|
+
impl<Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> Tensor<Scalar, Alloc, MAX_RANK> {
|
|
3568
3767
|
/// Create a view of the entire array.
|
|
3569
3768
|
pub fn view(&self) -> TensorView<'_, Scalar, MAX_RANK> {
|
|
3570
3769
|
TensorView {
|
|
@@ -3819,7 +4018,7 @@ impl<
|
|
|
3819
4018
|
I1: VectorIndex,
|
|
3820
4019
|
I2: VectorIndex,
|
|
3821
4020
|
I3: VectorIndex,
|
|
3822
|
-
Scalar,
|
|
4021
|
+
Scalar: StorageElement,
|
|
3823
4022
|
const MAX_RANK: usize,
|
|
3824
4023
|
> Index<(I0, I1, I2, I3)> for TensorView<'a, Scalar, MAX_RANK>
|
|
3825
4024
|
{
|
|
@@ -3838,7 +4037,7 @@ impl<
|
|
|
3838
4037
|
I2: VectorIndex,
|
|
3839
4038
|
I3: VectorIndex,
|
|
3840
4039
|
I4: VectorIndex,
|
|
3841
|
-
Scalar,
|
|
4040
|
+
Scalar: StorageElement,
|
|
3842
4041
|
const MAX_RANK: usize,
|
|
3843
4042
|
> Index<(I0, I1, I2, I3, I4)> for TensorView<'a, Scalar, MAX_RANK>
|
|
3844
4043
|
{
|
|
@@ -3858,7 +4057,7 @@ impl<
|
|
|
3858
4057
|
I3: VectorIndex,
|
|
3859
4058
|
I4: VectorIndex,
|
|
3860
4059
|
I5: VectorIndex,
|
|
3861
|
-
Scalar,
|
|
4060
|
+
Scalar: StorageElement,
|
|
3862
4061
|
const MAX_RANK: usize,
|
|
3863
4062
|
> Index<(I0, I1, I2, I3, I4, I5)> for TensorView<'a, Scalar, MAX_RANK>
|
|
3864
4063
|
{
|
|
@@ -3879,7 +4078,7 @@ impl<
|
|
|
3879
4078
|
I4: VectorIndex,
|
|
3880
4079
|
I5: VectorIndex,
|
|
3881
4080
|
I6: VectorIndex,
|
|
3882
|
-
Scalar,
|
|
4081
|
+
Scalar: StorageElement,
|
|
3883
4082
|
const MAX_RANK: usize,
|
|
3884
4083
|
> Index<(I0, I1, I2, I3, I4, I5, I6)> for TensorView<'a, Scalar, MAX_RANK>
|
|
3885
4084
|
{
|
|
@@ -3901,7 +4100,7 @@ impl<
|
|
|
3901
4100
|
I5: VectorIndex,
|
|
3902
4101
|
I6: VectorIndex,
|
|
3903
4102
|
I7: VectorIndex,
|
|
3904
|
-
Scalar,
|
|
4103
|
+
Scalar: StorageElement,
|
|
3905
4104
|
const MAX_RANK: usize,
|
|
3906
4105
|
> Index<(I0, I1, I2, I3, I4, I5, I6, I7)> for TensorView<'a, Scalar, MAX_RANK>
|
|
3907
4106
|
{
|
|
@@ -3977,7 +4176,7 @@ impl<
|
|
|
3977
4176
|
I1: VectorIndex,
|
|
3978
4177
|
I2: VectorIndex,
|
|
3979
4178
|
I3: VectorIndex,
|
|
3980
|
-
Scalar,
|
|
4179
|
+
Scalar: StorageElement,
|
|
3981
4180
|
const MAX_RANK: usize,
|
|
3982
4181
|
> Index<(I0, I1, I2, I3)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
3983
4182
|
{
|
|
@@ -3995,7 +4194,7 @@ impl<
|
|
|
3995
4194
|
I1: VectorIndex,
|
|
3996
4195
|
I2: VectorIndex,
|
|
3997
4196
|
I3: VectorIndex,
|
|
3998
|
-
Scalar,
|
|
4197
|
+
Scalar: StorageElement,
|
|
3999
4198
|
const MAX_RANK: usize,
|
|
4000
4199
|
> IndexMut<(I0, I1, I2, I3)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4001
4200
|
{
|
|
@@ -4012,7 +4211,7 @@ impl<
|
|
|
4012
4211
|
I2: VectorIndex,
|
|
4013
4212
|
I3: VectorIndex,
|
|
4014
4213
|
I4: VectorIndex,
|
|
4015
|
-
Scalar,
|
|
4214
|
+
Scalar: StorageElement,
|
|
4016
4215
|
const MAX_RANK: usize,
|
|
4017
4216
|
> Index<(I0, I1, I2, I3, I4)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4018
4217
|
{
|
|
@@ -4031,7 +4230,7 @@ impl<
|
|
|
4031
4230
|
I2: VectorIndex,
|
|
4032
4231
|
I3: VectorIndex,
|
|
4033
4232
|
I4: VectorIndex,
|
|
4034
|
-
Scalar,
|
|
4233
|
+
Scalar: StorageElement,
|
|
4035
4234
|
const MAX_RANK: usize,
|
|
4036
4235
|
> IndexMut<(I0, I1, I2, I3, I4)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4037
4236
|
{
|
|
@@ -4049,7 +4248,7 @@ impl<
|
|
|
4049
4248
|
I3: VectorIndex,
|
|
4050
4249
|
I4: VectorIndex,
|
|
4051
4250
|
I5: VectorIndex,
|
|
4052
|
-
Scalar,
|
|
4251
|
+
Scalar: StorageElement,
|
|
4053
4252
|
const MAX_RANK: usize,
|
|
4054
4253
|
> Index<(I0, I1, I2, I3, I4, I5)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4055
4254
|
{
|
|
@@ -4069,7 +4268,7 @@ impl<
|
|
|
4069
4268
|
I3: VectorIndex,
|
|
4070
4269
|
I4: VectorIndex,
|
|
4071
4270
|
I5: VectorIndex,
|
|
4072
|
-
Scalar,
|
|
4271
|
+
Scalar: StorageElement,
|
|
4073
4272
|
const MAX_RANK: usize,
|
|
4074
4273
|
> IndexMut<(I0, I1, I2, I3, I4, I5)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4075
4274
|
{
|
|
@@ -4088,7 +4287,7 @@ impl<
|
|
|
4088
4287
|
I4: VectorIndex,
|
|
4089
4288
|
I5: VectorIndex,
|
|
4090
4289
|
I6: VectorIndex,
|
|
4091
|
-
Scalar,
|
|
4290
|
+
Scalar: StorageElement,
|
|
4092
4291
|
const MAX_RANK: usize,
|
|
4093
4292
|
> Index<(I0, I1, I2, I3, I4, I5, I6)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4094
4293
|
{
|
|
@@ -4109,7 +4308,7 @@ impl<
|
|
|
4109
4308
|
I4: VectorIndex,
|
|
4110
4309
|
I5: VectorIndex,
|
|
4111
4310
|
I6: VectorIndex,
|
|
4112
|
-
Scalar,
|
|
4311
|
+
Scalar: StorageElement,
|
|
4113
4312
|
const MAX_RANK: usize,
|
|
4114
4313
|
> IndexMut<(I0, I1, I2, I3, I4, I5, I6)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4115
4314
|
{
|
|
@@ -4129,7 +4328,7 @@ impl<
|
|
|
4129
4328
|
I5: VectorIndex,
|
|
4130
4329
|
I6: VectorIndex,
|
|
4131
4330
|
I7: VectorIndex,
|
|
4132
|
-
Scalar,
|
|
4331
|
+
Scalar: StorageElement,
|
|
4133
4332
|
const MAX_RANK: usize,
|
|
4134
4333
|
> Index<(I0, I1, I2, I3, I4, I5, I6, I7)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4135
4334
|
{
|
|
@@ -4151,7 +4350,7 @@ impl<
|
|
|
4151
4350
|
I5: VectorIndex,
|
|
4152
4351
|
I6: VectorIndex,
|
|
4153
4352
|
I7: VectorIndex,
|
|
4154
|
-
Scalar,
|
|
4353
|
+
Scalar: StorageElement,
|
|
4155
4354
|
const MAX_RANK: usize,
|
|
4156
4355
|
> IndexMut<(I0, I1, I2, I3, I4, I5, I6, I7)> for TensorSpan<'a, Scalar, MAX_RANK>
|
|
4157
4356
|
{
|
|
@@ -4161,7 +4360,7 @@ impl<
|
|
|
4161
4360
|
}
|
|
4162
4361
|
}
|
|
4163
4362
|
|
|
4164
|
-
impl<AnyIndex: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize> Index<AnyIndex>
|
|
4363
|
+
impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> Index<AnyIndex>
|
|
4165
4364
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
4166
4365
|
{
|
|
4167
4366
|
type Output = Scalar;
|
|
@@ -4171,7 +4370,7 @@ impl<AnyIndex: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize> Ind
|
|
|
4171
4370
|
}
|
|
4172
4371
|
}
|
|
4173
4372
|
|
|
4174
|
-
impl<AnyIndex: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize> IndexMut<AnyIndex>
|
|
4373
|
+
impl<AnyIndex: VectorIndex, Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize> IndexMut<AnyIndex>
|
|
4175
4374
|
for Tensor<Scalar, Alloc, MAX_RANK>
|
|
4176
4375
|
{
|
|
4177
4376
|
fn index_mut(&mut self, index: AnyIndex) -> &mut Self::Output {
|
|
@@ -4180,7 +4379,7 @@ impl<AnyIndex: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize> Ind
|
|
|
4180
4379
|
}
|
|
4181
4380
|
}
|
|
4182
4381
|
|
|
4183
|
-
impl<I0: VectorIndex, I1: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize>
|
|
4382
|
+
impl<I0: VectorIndex, I1: VectorIndex, Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
4184
4383
|
Index<(I0, I1)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
4185
4384
|
{
|
|
4186
4385
|
type Output = Scalar;
|
|
@@ -4191,7 +4390,7 @@ impl<I0: VectorIndex, I1: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK:
|
|
|
4191
4390
|
}
|
|
4192
4391
|
}
|
|
4193
4392
|
|
|
4194
|
-
impl<I0: VectorIndex, I1: VectorIndex, Scalar, Alloc: Allocator, const MAX_RANK: usize>
|
|
4393
|
+
impl<I0: VectorIndex, I1: VectorIndex, Scalar: StorageElement, Alloc: Allocator, const MAX_RANK: usize>
|
|
4195
4394
|
IndexMut<(I0, I1)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
4196
4395
|
{
|
|
4197
4396
|
fn index_mut(&mut self, index: (I0, I1)) -> &mut Self::Output {
|
|
@@ -4204,7 +4403,7 @@ impl<
|
|
|
4204
4403
|
I0: VectorIndex,
|
|
4205
4404
|
I1: VectorIndex,
|
|
4206
4405
|
I2: VectorIndex,
|
|
4207
|
-
Scalar,
|
|
4406
|
+
Scalar: StorageElement,
|
|
4208
4407
|
Alloc: Allocator,
|
|
4209
4408
|
const MAX_RANK: usize,
|
|
4210
4409
|
> Index<(I0, I1, I2)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4221,7 +4420,7 @@ impl<
|
|
|
4221
4420
|
I0: VectorIndex,
|
|
4222
4421
|
I1: VectorIndex,
|
|
4223
4422
|
I2: VectorIndex,
|
|
4224
|
-
Scalar,
|
|
4423
|
+
Scalar: StorageElement,
|
|
4225
4424
|
Alloc: Allocator,
|
|
4226
4425
|
const MAX_RANK: usize,
|
|
4227
4426
|
> IndexMut<(I0, I1, I2)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4237,7 +4436,7 @@ impl<
|
|
|
4237
4436
|
I1: VectorIndex,
|
|
4238
4437
|
I2: VectorIndex,
|
|
4239
4438
|
I3: VectorIndex,
|
|
4240
|
-
Scalar,
|
|
4439
|
+
Scalar: StorageElement,
|
|
4241
4440
|
Alloc: Allocator,
|
|
4242
4441
|
const MAX_RANK: usize,
|
|
4243
4442
|
> Index<(I0, I1, I2, I3)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4255,7 +4454,7 @@ impl<
|
|
|
4255
4454
|
I1: VectorIndex,
|
|
4256
4455
|
I2: VectorIndex,
|
|
4257
4456
|
I3: VectorIndex,
|
|
4258
|
-
Scalar,
|
|
4457
|
+
Scalar: StorageElement,
|
|
4259
4458
|
Alloc: Allocator,
|
|
4260
4459
|
const MAX_RANK: usize,
|
|
4261
4460
|
> IndexMut<(I0, I1, I2, I3)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4272,7 +4471,7 @@ impl<
|
|
|
4272
4471
|
I2: VectorIndex,
|
|
4273
4472
|
I3: VectorIndex,
|
|
4274
4473
|
I4: VectorIndex,
|
|
4275
|
-
Scalar,
|
|
4474
|
+
Scalar: StorageElement,
|
|
4276
4475
|
Alloc: Allocator,
|
|
4277
4476
|
const MAX_RANK: usize,
|
|
4278
4477
|
> Index<(I0, I1, I2, I3, I4)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4291,7 +4490,7 @@ impl<
|
|
|
4291
4490
|
I2: VectorIndex,
|
|
4292
4491
|
I3: VectorIndex,
|
|
4293
4492
|
I4: VectorIndex,
|
|
4294
|
-
Scalar,
|
|
4493
|
+
Scalar: StorageElement,
|
|
4295
4494
|
Alloc: Allocator,
|
|
4296
4495
|
const MAX_RANK: usize,
|
|
4297
4496
|
> IndexMut<(I0, I1, I2, I3, I4)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4309,7 +4508,7 @@ impl<
|
|
|
4309
4508
|
I3: VectorIndex,
|
|
4310
4509
|
I4: VectorIndex,
|
|
4311
4510
|
I5: VectorIndex,
|
|
4312
|
-
Scalar,
|
|
4511
|
+
Scalar: StorageElement,
|
|
4313
4512
|
Alloc: Allocator,
|
|
4314
4513
|
const MAX_RANK: usize,
|
|
4315
4514
|
> Index<(I0, I1, I2, I3, I4, I5)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4329,7 +4528,7 @@ impl<
|
|
|
4329
4528
|
I3: VectorIndex,
|
|
4330
4529
|
I4: VectorIndex,
|
|
4331
4530
|
I5: VectorIndex,
|
|
4332
|
-
Scalar,
|
|
4531
|
+
Scalar: StorageElement,
|
|
4333
4532
|
Alloc: Allocator,
|
|
4334
4533
|
const MAX_RANK: usize,
|
|
4335
4534
|
> IndexMut<(I0, I1, I2, I3, I4, I5)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4348,7 +4547,7 @@ impl<
|
|
|
4348
4547
|
I4: VectorIndex,
|
|
4349
4548
|
I5: VectorIndex,
|
|
4350
4549
|
I6: VectorIndex,
|
|
4351
|
-
Scalar,
|
|
4550
|
+
Scalar: StorageElement,
|
|
4352
4551
|
Alloc: Allocator,
|
|
4353
4552
|
const MAX_RANK: usize,
|
|
4354
4553
|
> Index<(I0, I1, I2, I3, I4, I5, I6)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4369,7 +4568,7 @@ impl<
|
|
|
4369
4568
|
I4: VectorIndex,
|
|
4370
4569
|
I5: VectorIndex,
|
|
4371
4570
|
I6: VectorIndex,
|
|
4372
|
-
Scalar,
|
|
4571
|
+
Scalar: StorageElement,
|
|
4373
4572
|
Alloc: Allocator,
|
|
4374
4573
|
const MAX_RANK: usize,
|
|
4375
4574
|
> IndexMut<(I0, I1, I2, I3, I4, I5, I6)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4389,7 +4588,7 @@ impl<
|
|
|
4389
4588
|
I5: VectorIndex,
|
|
4390
4589
|
I6: VectorIndex,
|
|
4391
4590
|
I7: VectorIndex,
|
|
4392
|
-
Scalar,
|
|
4591
|
+
Scalar: StorageElement,
|
|
4393
4592
|
Alloc: Allocator,
|
|
4394
4593
|
const MAX_RANK: usize,
|
|
4395
4594
|
> Index<(I0, I1, I2, I3, I4, I5, I6, I7)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -4411,7 +4610,7 @@ impl<
|
|
|
4411
4610
|
I5: VectorIndex,
|
|
4412
4611
|
I6: VectorIndex,
|
|
4413
4612
|
I7: VectorIndex,
|
|
4414
|
-
Scalar,
|
|
4613
|
+
Scalar: StorageElement,
|
|
4415
4614
|
Alloc: Allocator,
|
|
4416
4615
|
const MAX_RANK: usize,
|
|
4417
4616
|
> IndexMut<(I0, I1, I2, I3, I4, I5, I6, I7)> for Tensor<Scalar, Alloc, MAX_RANK>
|
|
@@ -5400,31 +5599,7 @@ where
|
|
|
5400
5599
|
}
|
|
5401
5600
|
}
|
|
5402
5601
|
|
|
5403
|
-
|
|
5404
|
-
pub trait SumSqToF64 {
|
|
5405
|
-
fn to_f64(self) -> f64;
|
|
5406
|
-
}
|
|
5407
|
-
|
|
5408
|
-
impl SumSqToF64 for f32 {
|
|
5409
|
-
fn to_f64(self) -> f64 {
|
|
5410
|
-
self as f64
|
|
5411
|
-
}
|
|
5412
|
-
}
|
|
5413
|
-
impl SumSqToF64 for f64 {
|
|
5414
|
-
fn to_f64(self) -> f64 {
|
|
5415
|
-
self
|
|
5416
|
-
}
|
|
5417
|
-
}
|
|
5418
|
-
impl SumSqToF64 for u64 {
|
|
5419
|
-
fn to_f64(self) -> f64 {
|
|
5420
|
-
self as f64
|
|
5421
|
-
}
|
|
5422
|
-
}
|
|
5423
|
-
impl SumSqToF64 for i64 {
|
|
5424
|
-
fn to_f64(self) -> f64 {
|
|
5425
|
-
self as f64
|
|
5426
|
-
}
|
|
5427
|
-
}
|
|
5602
|
+
// SumSqToF64 trait moved to crate::reduce
|
|
5428
5603
|
|
|
5429
5604
|
fn try_alloc_output_like<Destination: Clone + StorageElement, Kernel, const MAX_RANK: usize>(
|
|
5430
5605
|
shape: &[usize],
|
|
@@ -5441,7 +5616,12 @@ where
|
|
|
5441
5616
|
Ok(result)
|
|
5442
5617
|
}
|
|
5443
5618
|
|
|
5444
|
-
fn try_reborrow_tensor_into<
|
|
5619
|
+
pub(crate) fn try_reborrow_tensor_into<
|
|
5620
|
+
Scalar: StorageElement + Clone,
|
|
5621
|
+
Destination: StorageElement + Clone,
|
|
5622
|
+
Kernel,
|
|
5623
|
+
const MAX_RANK: usize,
|
|
5624
|
+
>(
|
|
5445
5625
|
source: &Tensor<Scalar, Global, MAX_RANK>,
|
|
5446
5626
|
out: &mut Tensor<Destination, Global, MAX_RANK>,
|
|
5447
5627
|
apply: Kernel,
|
|
@@ -5457,7 +5637,11 @@ where
|
|
|
5457
5637
|
apply(&view, &mut span)
|
|
5458
5638
|
}
|
|
5459
5639
|
|
|
5460
|
-
fn try_reborrow_tensor_inplace<
|
|
5640
|
+
pub(crate) fn try_reborrow_tensor_inplace<
|
|
5641
|
+
Scalar: StorageElement + Clone,
|
|
5642
|
+
Kernel,
|
|
5643
|
+
const MAX_RANK: usize,
|
|
5644
|
+
>(
|
|
5461
5645
|
tensor: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
5462
5646
|
apply: Kernel,
|
|
5463
5647
|
) -> Result<(), TensorError>
|
|
@@ -5910,470 +6094,106 @@ impl<'a, Source: Clone + CastDtype, const MAX_RANK: usize> TensorView<'a, Source
|
|
|
5910
6094
|
}
|
|
5911
6095
|
}
|
|
5912
6096
|
|
|
5913
|
-
|
|
5914
|
-
pub trait ScaleOps<Scalar: Clone + EachScale, const MAX_RANK: usize>:
|
|
5915
|
-
TensorRef<Scalar, MAX_RANK>
|
|
5916
|
-
where
|
|
5917
|
-
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
5918
|
-
{
|
|
5919
|
-
fn try_add_scalar(
|
|
5920
|
-
&self,
|
|
5921
|
-
scalar: Scalar::Scalar,
|
|
5922
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
5923
|
-
self.view().try_add_scalar(scalar)
|
|
5924
|
-
}
|
|
6097
|
+
// endregion: Tensor Explicit Elementwise + Cast (ScaleOps/SumOps/BlendOps/FmaOps/CastOps moved to crate::each / crate::cast)
|
|
5925
6098
|
|
|
5926
|
-
|
|
5927
|
-
&self,
|
|
5928
|
-
scalar: Scalar::Scalar,
|
|
5929
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
5930
|
-
self.view().try_sub_scalar(scalar)
|
|
5931
|
-
}
|
|
6099
|
+
// region: Tensor Trigonometry
|
|
5932
6100
|
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
5937
|
-
self.view().try_mul_scalar(scalar)
|
|
6101
|
+
impl<'a, Scalar: Clone + EachSin, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6102
|
+
pub fn try_sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6103
|
+
try_alloc_output_like(self.shape(), |span| self.try_sin_into(span))
|
|
5938
6104
|
}
|
|
5939
|
-
}
|
|
5940
|
-
|
|
5941
|
-
impl<Scalar: Clone + EachScale, const R: usize, C: TensorRef<Scalar, R>> ScaleOps<Scalar, R> for C where
|
|
5942
|
-
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy
|
|
5943
|
-
{
|
|
5944
|
-
}
|
|
5945
6105
|
|
|
5946
|
-
|
|
5947
|
-
where
|
|
5948
|
-
Scalar::Scalar: From<f32> + core::ops::Mul<Output = Scalar::Scalar> + Copy,
|
|
5949
|
-
{
|
|
5950
|
-
pub fn try_add_scalar_into(
|
|
6106
|
+
pub fn try_sin_into(
|
|
5951
6107
|
&self,
|
|
5952
|
-
|
|
5953
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
6108
|
+
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
5954
6109
|
) -> Result<(), TensorError> {
|
|
5955
|
-
|
|
5956
|
-
|
|
6110
|
+
try_unary_kernel_into(self, out, |source, target| {
|
|
6111
|
+
Scalar::sin(source, target);
|
|
5957
6112
|
})
|
|
5958
6113
|
}
|
|
6114
|
+
}
|
|
5959
6115
|
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
5964
|
-
) -> Result<(), TensorError> {
|
|
5965
|
-
try_reborrow_tensor_into(self, out, |view, span| {
|
|
5966
|
-
view.try_sub_scalar_into(scalar, span)
|
|
5967
|
-
})
|
|
6116
|
+
impl<'a, Scalar: Clone + EachCos, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6117
|
+
pub fn try_cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6118
|
+
try_alloc_output_like(self.shape(), |span| self.try_cos_into(span))
|
|
5968
6119
|
}
|
|
5969
6120
|
|
|
5970
|
-
pub fn
|
|
6121
|
+
pub fn try_cos_into(
|
|
5971
6122
|
&self,
|
|
5972
|
-
|
|
5973
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
6123
|
+
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
5974
6124
|
) -> Result<(), TensorError> {
|
|
5975
|
-
|
|
5976
|
-
|
|
6125
|
+
try_unary_kernel_into(self, out, |source, target| {
|
|
6126
|
+
Scalar::cos(source, target);
|
|
5977
6127
|
})
|
|
5978
6128
|
}
|
|
6129
|
+
}
|
|
5979
6130
|
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
}
|
|
5984
|
-
|
|
5985
|
-
pub fn try_sub_scalar_inplace(&mut self, scalar: Scalar::Scalar) -> Result<(), TensorError> {
|
|
5986
|
-
self.sub_scalar_inplace(scalar);
|
|
5987
|
-
Ok(())
|
|
5988
|
-
}
|
|
5989
|
-
|
|
5990
|
-
pub fn try_mul_scalar_inplace(&mut self, scalar: Scalar::Scalar) -> Result<(), TensorError> {
|
|
5991
|
-
self.mul_scalar_inplace(scalar);
|
|
5992
|
-
Ok(())
|
|
6131
|
+
impl<'a, Scalar: Clone + EachATan, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6132
|
+
pub fn try_atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6133
|
+
try_alloc_output_like(self.shape(), |span| self.try_atan_into(span))
|
|
5993
6134
|
}
|
|
5994
6135
|
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
6136
|
+
pub fn try_atan_into(
|
|
6137
|
+
&self,
|
|
6138
|
+
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
6139
|
+
) -> Result<(), TensorError> {
|
|
6140
|
+
try_unary_kernel_into(self, out, |source, target| {
|
|
6141
|
+
Scalar::atan(source, target);
|
|
6142
|
+
})
|
|
5999
6143
|
}
|
|
6144
|
+
}
|
|
6000
6145
|
|
|
6001
|
-
|
|
6002
|
-
pub fn sub_scalar_inplace(&mut self, scalar: Scalar::Scalar) {
|
|
6003
|
-
try_reborrow_tensor_inplace(self, |view, span| view.try_sub_scalar_into(scalar, span))
|
|
6004
|
-
.expect("inplace scalar op on self cannot fail")
|
|
6005
|
-
}
|
|
6146
|
+
// endregion: Tensor Trigonometry (moved to crate::each)
|
|
6006
6147
|
|
|
6007
|
-
|
|
6008
|
-
pub fn mul_scalar_inplace(&mut self, scalar: Scalar::Scalar) {
|
|
6009
|
-
try_reborrow_tensor_inplace(self, |view, span| view.try_mul_scalar_into(scalar, span))
|
|
6010
|
-
.expect("inplace scalar op on self cannot fail")
|
|
6011
|
-
}
|
|
6012
|
-
}
|
|
6148
|
+
// region: Tensor Reductions
|
|
6013
6149
|
|
|
6014
|
-
impl<Scalar: Clone +
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6150
|
+
impl<Scalar: Clone + Dot, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6151
|
+
/// Compute the dot product of this array with another.
|
|
6152
|
+
///
|
|
6153
|
+
/// Both arrays must be 1D with the same length.
|
|
6154
|
+
pub fn dot_product<const OTHER_MAX_RANK: usize>(
|
|
6155
|
+
&self,
|
|
6156
|
+
other: &Tensor<Scalar, Global, OTHER_MAX_RANK>,
|
|
6157
|
+
) -> Result<Scalar::Output, TensorError> {
|
|
6158
|
+
if self.ndim != 1 || other.ndim != 1 {
|
|
6159
|
+
return Err(TensorError::DimensionMismatch {
|
|
6160
|
+
expected: 1,
|
|
6161
|
+
got: if self.ndim != 1 {
|
|
6162
|
+
self.ndim
|
|
6163
|
+
} else {
|
|
6164
|
+
other.ndim
|
|
6165
|
+
},
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
if self.numel() != other.numel() {
|
|
6169
|
+
return Err(TensorError::ShapeMismatch {
|
|
6170
|
+
axis: 0,
|
|
6171
|
+
expected: self.numel(),
|
|
6172
|
+
got: other.numel(),
|
|
6173
|
+
});
|
|
6174
|
+
}
|
|
6175
|
+
// Dot::dot returns Option, unwrap since we verified lengths match
|
|
6176
|
+
Ok(Scalar::dot(self.as_slice(), other.as_slice()).expect("dot product failed"))
|
|
6021
6177
|
}
|
|
6022
6178
|
}
|
|
6023
6179
|
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
}
|
|
6032
|
-
}
|
|
6180
|
+
pub(crate) type MomentsAxisResult<Scalar, const MAX_RANK: usize> = Result<
|
|
6181
|
+
(
|
|
6182
|
+
Tensor<<Scalar as ReduceMoments>::SumOutput, Global, MAX_RANK>,
|
|
6183
|
+
Tensor<<Scalar as ReduceMoments>::SumSqOutput, Global, MAX_RANK>,
|
|
6184
|
+
),
|
|
6185
|
+
TensorError,
|
|
6186
|
+
>;
|
|
6033
6187
|
|
|
6034
|
-
impl<Scalar: Clone +
|
|
6035
|
-
for Tensor<Scalar, Global, MAX_RANK>
|
|
6188
|
+
impl<'a, Scalar: Clone + ReduceMoments, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK>
|
|
6036
6189
|
where
|
|
6037
|
-
Scalar::
|
|
6190
|
+
Scalar::SumOutput: Clone + Default + core::ops::AddAssign,
|
|
6191
|
+
Scalar::SumSqOutput: Clone + Default + core::ops::AddAssign + SumSqToF64,
|
|
6038
6192
|
{
|
|
6039
|
-
fn
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
}
|
|
6043
|
-
|
|
6044
|
-
/// Extension trait: element-wise addition for any [`TensorRef`] implementor.
|
|
6045
|
-
pub trait SumOps<Scalar: Clone + EachSum, const MAX_RANK: usize>:
|
|
6046
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6047
|
-
{
|
|
6048
|
-
fn try_add_tensor(
|
|
6049
|
-
&self,
|
|
6050
|
-
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
6051
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6052
|
-
self.view().try_add_tensor(&other.view())
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
|
|
6056
|
-
impl<Scalar: Clone + EachSum, const R: usize, C: TensorRef<Scalar, R>> SumOps<Scalar, R> for C {}
|
|
6057
|
-
|
|
6058
|
-
impl<Scalar: Clone + EachSum, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6059
|
-
pub fn try_add_tensor_into(
|
|
6060
|
-
&self,
|
|
6061
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6062
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
6063
|
-
) -> Result<(), TensorError> {
|
|
6064
|
-
try_reborrow_tensor_into(self, out, |view, span| {
|
|
6065
|
-
view.try_add_tensor_into(&other.view(), span)
|
|
6066
|
-
})
|
|
6067
|
-
}
|
|
6068
|
-
|
|
6069
|
-
pub fn try_add_tensor_inplace(
|
|
6070
|
-
&mut self,
|
|
6071
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6072
|
-
) -> Result<(), TensorError> {
|
|
6073
|
-
try_reborrow_tensor_inplace(self, |view, span| {
|
|
6074
|
-
view.try_add_tensor_into(&other.view(), span)
|
|
6075
|
-
})
|
|
6076
|
-
}
|
|
6077
|
-
}
|
|
6078
|
-
|
|
6079
|
-
/// Extension trait: element-wise subtraction for any [`TensorRef`] implementor.
|
|
6080
|
-
pub trait BlendOps<Scalar: Clone + EachBlend, const MAX_RANK: usize>:
|
|
6081
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6082
|
-
where
|
|
6083
|
-
Scalar::Scalar: From<f32> + Copy,
|
|
6084
|
-
{
|
|
6085
|
-
fn try_sub_tensor(
|
|
6086
|
-
&self,
|
|
6087
|
-
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
6088
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6089
|
-
self.view().try_sub_tensor(&other.view())
|
|
6090
|
-
}
|
|
6091
|
-
}
|
|
6092
|
-
|
|
6093
|
-
impl<Scalar: Clone + EachBlend, const R: usize, C: TensorRef<Scalar, R>> BlendOps<Scalar, R> for C where
|
|
6094
|
-
Scalar::Scalar: From<f32> + Copy
|
|
6095
|
-
{
|
|
6096
|
-
}
|
|
6097
|
-
|
|
6098
|
-
impl<Scalar: Clone + EachBlend, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK>
|
|
6099
|
-
where
|
|
6100
|
-
Scalar::Scalar: From<f32> + Copy,
|
|
6101
|
-
{
|
|
6102
|
-
pub fn try_sub_tensor_into(
|
|
6103
|
-
&self,
|
|
6104
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6105
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
6106
|
-
) -> Result<(), TensorError> {
|
|
6107
|
-
try_reborrow_tensor_into(self, out, |view, span| {
|
|
6108
|
-
view.try_sub_tensor_into(&other.view(), span)
|
|
6109
|
-
})
|
|
6110
|
-
}
|
|
6111
|
-
|
|
6112
|
-
pub fn try_sub_tensor_inplace(
|
|
6113
|
-
&mut self,
|
|
6114
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6115
|
-
) -> Result<(), TensorError> {
|
|
6116
|
-
try_reborrow_tensor_inplace(self, |view, span| {
|
|
6117
|
-
view.try_sub_tensor_into(&other.view(), span)
|
|
6118
|
-
})
|
|
6119
|
-
}
|
|
6120
|
-
}
|
|
6121
|
-
|
|
6122
|
-
/// Extension trait: element-wise multiplication for any [`TensorRef`] implementor.
|
|
6123
|
-
pub trait FmaOps<Scalar: Clone + EachFMA, const MAX_RANK: usize>:
|
|
6124
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6125
|
-
where
|
|
6126
|
-
Scalar::Scalar: From<f32> + Copy,
|
|
6127
|
-
{
|
|
6128
|
-
fn try_mul_tensor(
|
|
6129
|
-
&self,
|
|
6130
|
-
other: &(impl TensorRef<Scalar, MAX_RANK> + ?Sized),
|
|
6131
|
-
) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6132
|
-
self.view().try_mul_tensor(&other.view())
|
|
6133
|
-
}
|
|
6134
|
-
}
|
|
6135
|
-
|
|
6136
|
-
impl<Scalar: Clone + EachFMA, const R: usize, C: TensorRef<Scalar, R>> FmaOps<Scalar, R> for C where
|
|
6137
|
-
Scalar::Scalar: From<f32> + Copy
|
|
6138
|
-
{
|
|
6139
|
-
}
|
|
6140
|
-
|
|
6141
|
-
impl<Scalar: Clone + EachFMA, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK>
|
|
6142
|
-
where
|
|
6143
|
-
Scalar::Scalar: From<f32> + Copy,
|
|
6144
|
-
{
|
|
6145
|
-
pub fn try_mul_tensor_into(
|
|
6146
|
-
&self,
|
|
6147
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6148
|
-
out: &mut Tensor<Scalar, Global, MAX_RANK>,
|
|
6149
|
-
) -> Result<(), TensorError> {
|
|
6150
|
-
try_reborrow_tensor_into(self, out, |view, span| {
|
|
6151
|
-
view.try_mul_tensor_into(&other.view(), span)
|
|
6152
|
-
})
|
|
6153
|
-
}
|
|
6154
|
-
|
|
6155
|
-
pub fn try_mul_tensor_inplace(
|
|
6156
|
-
&mut self,
|
|
6157
|
-
other: &Tensor<Scalar, Global, MAX_RANK>,
|
|
6158
|
-
) -> Result<(), TensorError> {
|
|
6159
|
-
try_reborrow_tensor_inplace(self, |view, span| {
|
|
6160
|
-
view.try_mul_tensor_into(&other.view(), span)
|
|
6161
|
-
})
|
|
6162
|
-
}
|
|
6163
|
-
}
|
|
6164
|
-
|
|
6165
|
-
/// Extension trait: type casting for any [`TensorRef`] implementor.
|
|
6166
|
-
pub trait CastOps<Source: Clone + CastDtype, const MAX_RANK: usize>:
|
|
6167
|
-
TensorRef<Source, MAX_RANK>
|
|
6168
|
-
{
|
|
6169
|
-
fn try_cast_dtype<Destination: Clone + CastDtype>(
|
|
6170
|
-
&self,
|
|
6171
|
-
) -> Result<Tensor<Destination, Global, MAX_RANK>, TensorError> {
|
|
6172
|
-
self.view().try_cast_dtype()
|
|
6173
|
-
}
|
|
6174
|
-
}
|
|
6175
|
-
|
|
6176
|
-
impl<Source: Clone + CastDtype, const R: usize, C: TensorRef<Source, R>> CastOps<Source, R> for C {}
|
|
6177
|
-
|
|
6178
|
-
impl<Source: Clone + CastDtype, const MAX_RANK: usize> Tensor<Source, Global, MAX_RANK> {
|
|
6179
|
-
pub fn try_cast_dtype_into<Destination: Clone + CastDtype>(
|
|
6180
|
-
&self,
|
|
6181
|
-
out: &mut Tensor<Destination, Global, MAX_RANK>,
|
|
6182
|
-
) -> Result<(), TensorError> {
|
|
6183
|
-
try_reborrow_tensor_into(self, out, |view, span| view.try_cast_dtype_into(span))
|
|
6184
|
-
}
|
|
6185
|
-
}
|
|
6186
|
-
|
|
6187
|
-
// endregion: Tensor Explicit Elementwise + Cast
|
|
6188
|
-
|
|
6189
|
-
// region: Tensor Trigonometry
|
|
6190
|
-
|
|
6191
|
-
impl<'a, Scalar: Clone + EachSin, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6192
|
-
pub fn try_sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6193
|
-
try_alloc_output_like(self.shape(), |span| self.try_sin_into(span))
|
|
6194
|
-
}
|
|
6195
|
-
|
|
6196
|
-
pub fn try_sin_into(
|
|
6197
|
-
&self,
|
|
6198
|
-
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
6199
|
-
) -> Result<(), TensorError> {
|
|
6200
|
-
try_unary_kernel_into(self, out, |source, target| {
|
|
6201
|
-
Scalar::sin(source, target);
|
|
6202
|
-
})
|
|
6203
|
-
}
|
|
6204
|
-
}
|
|
6205
|
-
|
|
6206
|
-
impl<'a, Scalar: Clone + EachCos, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6207
|
-
pub fn try_cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6208
|
-
try_alloc_output_like(self.shape(), |span| self.try_cos_into(span))
|
|
6209
|
-
}
|
|
6210
|
-
|
|
6211
|
-
pub fn try_cos_into(
|
|
6212
|
-
&self,
|
|
6213
|
-
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
6214
|
-
) -> Result<(), TensorError> {
|
|
6215
|
-
try_unary_kernel_into(self, out, |source, target| {
|
|
6216
|
-
Scalar::cos(source, target);
|
|
6217
|
-
})
|
|
6218
|
-
}
|
|
6219
|
-
}
|
|
6220
|
-
|
|
6221
|
-
impl<'a, Scalar: Clone + EachATan, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK> {
|
|
6222
|
-
pub fn try_atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6223
|
-
try_alloc_output_like(self.shape(), |span| self.try_atan_into(span))
|
|
6224
|
-
}
|
|
6225
|
-
|
|
6226
|
-
pub fn try_atan_into(
|
|
6227
|
-
&self,
|
|
6228
|
-
out: &mut TensorSpan<'_, Scalar, MAX_RANK>,
|
|
6229
|
-
) -> Result<(), TensorError> {
|
|
6230
|
-
try_unary_kernel_into(self, out, |source, target| {
|
|
6231
|
-
Scalar::atan(source, target);
|
|
6232
|
-
})
|
|
6233
|
-
}
|
|
6234
|
-
}
|
|
6235
|
-
|
|
6236
|
-
/// Extension trait: element-wise sine for any [`TensorRef`] implementor.
|
|
6237
|
-
pub trait TrigSinOps<Scalar: Clone + EachSin, const MAX_RANK: usize>:
|
|
6238
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6239
|
-
{
|
|
6240
|
-
fn try_sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6241
|
-
self.view().try_sin()
|
|
6242
|
-
}
|
|
6243
|
-
}
|
|
6244
|
-
|
|
6245
|
-
impl<Scalar: Clone + EachSin, const R: usize, C: TensorRef<Scalar, R>> TrigSinOps<Scalar, R> for C {}
|
|
6246
|
-
|
|
6247
|
-
/// Extension trait: element-wise cosine for any [`TensorRef`] implementor.
|
|
6248
|
-
pub trait TrigCosOps<Scalar: Clone + EachCos, const MAX_RANK: usize>:
|
|
6249
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6250
|
-
{
|
|
6251
|
-
fn try_cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6252
|
-
self.view().try_cos()
|
|
6253
|
-
}
|
|
6254
|
-
}
|
|
6255
|
-
|
|
6256
|
-
impl<Scalar: Clone + EachCos, const R: usize, C: TensorRef<Scalar, R>> TrigCosOps<Scalar, R> for C {}
|
|
6257
|
-
|
|
6258
|
-
/// Extension trait: element-wise arctangent for any [`TensorRef`] implementor.
|
|
6259
|
-
pub trait TrigAtanOps<Scalar: Clone + EachATan, const MAX_RANK: usize>:
|
|
6260
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6261
|
-
{
|
|
6262
|
-
fn try_atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6263
|
-
self.view().try_atan()
|
|
6264
|
-
}
|
|
6265
|
-
}
|
|
6266
|
-
|
|
6267
|
-
impl<Scalar: Clone + EachATan, const R: usize, C: TensorRef<Scalar, R>> TrigAtanOps<Scalar, R>
|
|
6268
|
-
for C
|
|
6269
|
-
{
|
|
6270
|
-
}
|
|
6271
|
-
|
|
6272
|
-
impl<Scalar: Clone + EachSin, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6273
|
-
/// Element-wise sine: result\[i\] = sin(self\[i\])
|
|
6274
|
-
pub fn sin(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6275
|
-
self.view().try_sin()
|
|
6276
|
-
}
|
|
6277
|
-
|
|
6278
|
-
/// Element-wise sine in-place (infallible — self vs self always matches).
|
|
6279
|
-
pub fn sin_inplace(&mut self) {
|
|
6280
|
-
try_reborrow_tensor_inplace(self, |view, span| view.try_sin_into(span))
|
|
6281
|
-
.expect("inplace trig op on self cannot fail")
|
|
6282
|
-
}
|
|
6283
|
-
|
|
6284
|
-
pub fn try_sin_inplace(&mut self) -> Result<(), TensorError> {
|
|
6285
|
-
self.sin_inplace();
|
|
6286
|
-
Ok(())
|
|
6287
|
-
}
|
|
6288
|
-
}
|
|
6289
|
-
|
|
6290
|
-
impl<Scalar: Clone + EachCos, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6291
|
-
/// Element-wise cosine: result\[i\] = cos(self\[i\])
|
|
6292
|
-
pub fn cos(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6293
|
-
self.view().try_cos()
|
|
6294
|
-
}
|
|
6295
|
-
|
|
6296
|
-
/// Element-wise cosine in-place (infallible — self vs self always matches).
|
|
6297
|
-
pub fn cos_inplace(&mut self) {
|
|
6298
|
-
try_reborrow_tensor_inplace(self, |view, span| view.try_cos_into(span))
|
|
6299
|
-
.expect("inplace trig op on self cannot fail")
|
|
6300
|
-
}
|
|
6301
|
-
|
|
6302
|
-
pub fn try_cos_inplace(&mut self) -> Result<(), TensorError> {
|
|
6303
|
-
self.cos_inplace();
|
|
6304
|
-
Ok(())
|
|
6305
|
-
}
|
|
6306
|
-
}
|
|
6307
|
-
|
|
6308
|
-
impl<Scalar: Clone + EachATan, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6309
|
-
/// Element-wise arctangent: result\[i\] = atan(self\[i\])
|
|
6310
|
-
pub fn atan(&self) -> Result<Tensor<Scalar, Global, MAX_RANK>, TensorError> {
|
|
6311
|
-
self.view().try_atan()
|
|
6312
|
-
}
|
|
6313
|
-
|
|
6314
|
-
/// Element-wise arctangent in-place (infallible — self vs self always matches).
|
|
6315
|
-
pub fn atan_inplace(&mut self) {
|
|
6316
|
-
try_reborrow_tensor_inplace(self, |view, span| view.try_atan_into(span))
|
|
6317
|
-
.expect("inplace trig op on self cannot fail")
|
|
6318
|
-
}
|
|
6319
|
-
|
|
6320
|
-
pub fn try_atan_inplace(&mut self) -> Result<(), TensorError> {
|
|
6321
|
-
self.atan_inplace();
|
|
6322
|
-
Ok(())
|
|
6323
|
-
}
|
|
6324
|
-
}
|
|
6325
|
-
|
|
6326
|
-
// endregion: Tensor Trigonometry
|
|
6327
|
-
|
|
6328
|
-
// region: Tensor Reductions
|
|
6329
|
-
|
|
6330
|
-
impl<Scalar: Clone + Dot, const MAX_RANK: usize> Tensor<Scalar, Global, MAX_RANK> {
|
|
6331
|
-
/// Compute the dot product of this array with another.
|
|
6332
|
-
///
|
|
6333
|
-
/// Both arrays must be 1D with the same length.
|
|
6334
|
-
pub fn dot_product<const OTHER_MAX_RANK: usize>(
|
|
6335
|
-
&self,
|
|
6336
|
-
other: &Tensor<Scalar, Global, OTHER_MAX_RANK>,
|
|
6337
|
-
) -> Result<Scalar::Output, TensorError> {
|
|
6338
|
-
if self.ndim != 1 || other.ndim != 1 {
|
|
6339
|
-
return Err(TensorError::DimensionMismatch {
|
|
6340
|
-
expected: 1,
|
|
6341
|
-
got: if self.ndim != 1 {
|
|
6342
|
-
self.ndim
|
|
6343
|
-
} else {
|
|
6344
|
-
other.ndim
|
|
6345
|
-
},
|
|
6346
|
-
});
|
|
6347
|
-
}
|
|
6348
|
-
if self.numel() != other.numel() {
|
|
6349
|
-
return Err(TensorError::ShapeMismatch {
|
|
6350
|
-
axis: 0,
|
|
6351
|
-
expected: self.numel(),
|
|
6352
|
-
got: other.numel(),
|
|
6353
|
-
});
|
|
6354
|
-
}
|
|
6355
|
-
// Dot::dot returns Option, unwrap since we verified lengths match
|
|
6356
|
-
Ok(Scalar::dot(self.as_slice(), other.as_slice()).expect("dot product failed"))
|
|
6357
|
-
}
|
|
6358
|
-
}
|
|
6359
|
-
|
|
6360
|
-
type MomentsAxisResult<Scalar, const MAX_RANK: usize> = Result<
|
|
6361
|
-
(
|
|
6362
|
-
Tensor<<Scalar as ReduceMoments>::SumOutput, Global, MAX_RANK>,
|
|
6363
|
-
Tensor<<Scalar as ReduceMoments>::SumSqOutput, Global, MAX_RANK>,
|
|
6364
|
-
),
|
|
6365
|
-
TensorError,
|
|
6366
|
-
>;
|
|
6367
|
-
|
|
6368
|
-
impl<'a, Scalar: Clone + ReduceMoments, const MAX_RANK: usize> TensorView<'a, Scalar, MAX_RANK>
|
|
6369
|
-
where
|
|
6370
|
-
Scalar::SumOutput: Clone + Default + core::ops::AddAssign,
|
|
6371
|
-
Scalar::SumSqOutput: Clone + Default + core::ops::AddAssign + SumSqToF64,
|
|
6372
|
-
{
|
|
6373
|
-
pub fn try_moments_all(&self) -> Result<(Scalar::SumOutput, Scalar::SumSqOutput), TensorError> {
|
|
6374
|
-
Ok(unsafe {
|
|
6375
|
-
reduce_moments_recursive::<Scalar>(self.data, self.shape(), &self.strides[..self.ndim])
|
|
6376
|
-
})
|
|
6193
|
+
pub fn try_moments_all(&self) -> Result<(Scalar::SumOutput, Scalar::SumSqOutput), TensorError> {
|
|
6194
|
+
Ok(unsafe {
|
|
6195
|
+
reduce_moments_recursive::<Scalar>(self.data, self.shape(), &self.strides[..self.ndim])
|
|
6196
|
+
})
|
|
6377
6197
|
}
|
|
6378
6198
|
|
|
6379
6199
|
pub fn try_moments_axis<AnyIndex: VectorIndex>(
|
|
@@ -6502,7 +6322,7 @@ where
|
|
|
6502
6322
|
}
|
|
6503
6323
|
}
|
|
6504
6324
|
|
|
6505
|
-
type MinMaxAxisResult<Scalar, const MAX_RANK: usize> = Result<
|
|
6325
|
+
pub(crate) type MinMaxAxisResult<Scalar, const MAX_RANK: usize> = Result<
|
|
6506
6326
|
MinMaxResult<
|
|
6507
6327
|
Tensor<<Scalar as ReduceMinMax>::Output, Global, MAX_RANK>,
|
|
6508
6328
|
Tensor<usize, Global, MAX_RANK>,
|
|
@@ -6669,173 +6489,7 @@ where
|
|
|
6669
6489
|
}
|
|
6670
6490
|
}
|
|
6671
6491
|
|
|
6672
|
-
|
|
6673
|
-
pub trait MomentsOps<Scalar: Clone + ReduceMoments, const MAX_RANK: usize>:
|
|
6674
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6675
|
-
where
|
|
6676
|
-
Scalar::SumOutput: Clone + Default + core::ops::AddAssign,
|
|
6677
|
-
Scalar::SumSqOutput: Clone + Default + core::ops::AddAssign + SumSqToF64,
|
|
6678
|
-
{
|
|
6679
|
-
fn try_moments_all(&self) -> Result<(Scalar::SumOutput, Scalar::SumSqOutput), TensorError> {
|
|
6680
|
-
self.view().try_moments_all()
|
|
6681
|
-
}
|
|
6682
|
-
|
|
6683
|
-
fn try_moments_axis<AnyIndex: VectorIndex>(
|
|
6684
|
-
&self,
|
|
6685
|
-
axis: AnyIndex,
|
|
6686
|
-
keep_dims: bool,
|
|
6687
|
-
) -> MomentsAxisResult<Scalar, MAX_RANK> {
|
|
6688
|
-
self.view().try_moments_axis(axis, keep_dims)
|
|
6689
|
-
}
|
|
6690
|
-
|
|
6691
|
-
fn try_moments_axis_into<AnyIndex: VectorIndex>(
|
|
6692
|
-
&self,
|
|
6693
|
-
axis: AnyIndex,
|
|
6694
|
-
keep_dims: bool,
|
|
6695
|
-
sum_out: &mut Tensor<Scalar::SumOutput, Global, MAX_RANK>,
|
|
6696
|
-
sumsq_out: &mut Tensor<Scalar::SumSqOutput, Global, MAX_RANK>,
|
|
6697
|
-
) -> Result<(), TensorError> {
|
|
6698
|
-
self.view()
|
|
6699
|
-
.try_moments_axis_into(axis, keep_dims, sum_out, sumsq_out)
|
|
6700
|
-
}
|
|
6701
|
-
|
|
6702
|
-
fn try_sum_all(&self) -> Result<Scalar::SumOutput, TensorError> {
|
|
6703
|
-
self.view().try_sum_all()
|
|
6704
|
-
}
|
|
6705
|
-
|
|
6706
|
-
fn try_sum_axis<AnyIndex: VectorIndex>(
|
|
6707
|
-
&self,
|
|
6708
|
-
axis: AnyIndex,
|
|
6709
|
-
keep_dims: bool,
|
|
6710
|
-
) -> Result<Tensor<Scalar::SumOutput, Global, MAX_RANK>, TensorError> {
|
|
6711
|
-
self.view().try_sum_axis(axis, keep_dims)
|
|
6712
|
-
}
|
|
6713
|
-
|
|
6714
|
-
fn try_sum_axis_into<AnyIndex: VectorIndex>(
|
|
6715
|
-
&self,
|
|
6716
|
-
axis: AnyIndex,
|
|
6717
|
-
keep_dims: bool,
|
|
6718
|
-
out: &mut Tensor<Scalar::SumOutput, Global, MAX_RANK>,
|
|
6719
|
-
) -> Result<(), TensorError> {
|
|
6720
|
-
self.view().try_sum_axis_into(axis, keep_dims, out)
|
|
6721
|
-
}
|
|
6722
|
-
|
|
6723
|
-
fn try_norm_all(&self) -> Result<f64, TensorError> {
|
|
6724
|
-
self.view().try_norm_all()
|
|
6725
|
-
}
|
|
6726
|
-
|
|
6727
|
-
fn try_norm_axis<AnyIndex: VectorIndex>(
|
|
6728
|
-
&self,
|
|
6729
|
-
axis: AnyIndex,
|
|
6730
|
-
keep_dims: bool,
|
|
6731
|
-
) -> Result<Tensor<f64, Global, MAX_RANK>, TensorError> {
|
|
6732
|
-
self.view().try_norm_axis(axis, keep_dims)
|
|
6733
|
-
}
|
|
6734
|
-
|
|
6735
|
-
fn try_norm_axis_into<AnyIndex: VectorIndex>(
|
|
6736
|
-
&self,
|
|
6737
|
-
axis: AnyIndex,
|
|
6738
|
-
keep_dims: bool,
|
|
6739
|
-
out: &mut Tensor<f64, Global, MAX_RANK>,
|
|
6740
|
-
) -> Result<(), TensorError> {
|
|
6741
|
-
self.view().try_norm_axis_into(axis, keep_dims, out)
|
|
6742
|
-
}
|
|
6743
|
-
}
|
|
6744
|
-
|
|
6745
|
-
impl<Scalar: Clone + ReduceMoments, const R: usize, C: TensorRef<Scalar, R>> MomentsOps<Scalar, R>
|
|
6746
|
-
for C
|
|
6747
|
-
where
|
|
6748
|
-
Scalar::SumOutput: Clone + Default + core::ops::AddAssign,
|
|
6749
|
-
Scalar::SumSqOutput: Clone + Default + core::ops::AddAssign + SumSqToF64,
|
|
6750
|
-
{
|
|
6751
|
-
}
|
|
6752
|
-
|
|
6753
|
-
/// Extension trait: min/max reductions for any [`TensorRef`] implementor.
|
|
6754
|
-
pub trait MinMaxOps<Scalar: Clone + ReduceMinMax, const MAX_RANK: usize>:
|
|
6755
|
-
TensorRef<Scalar, MAX_RANK>
|
|
6756
|
-
where
|
|
6757
|
-
Scalar::Output: Clone + Default + PartialOrd,
|
|
6758
|
-
{
|
|
6759
|
-
fn try_minmax_all(&self) -> Result<MinMaxResult<Scalar::Output>, TensorError> {
|
|
6760
|
-
self.view().try_minmax_all()
|
|
6761
|
-
}
|
|
6762
|
-
|
|
6763
|
-
fn try_minmax_axis<AnyIndex: VectorIndex>(
|
|
6764
|
-
&self,
|
|
6765
|
-
axis: AnyIndex,
|
|
6766
|
-
keep_dims: bool,
|
|
6767
|
-
) -> MinMaxAxisResult<Scalar, MAX_RANK> {
|
|
6768
|
-
self.view().try_minmax_axis(axis, keep_dims)
|
|
6769
|
-
}
|
|
6770
|
-
|
|
6771
|
-
fn try_minmax_axis_into<AnyIndex: VectorIndex>(
|
|
6772
|
-
&self,
|
|
6773
|
-
axis: AnyIndex,
|
|
6774
|
-
keep_dims: bool,
|
|
6775
|
-
min_out: &mut Tensor<Scalar::Output, Global, MAX_RANK>,
|
|
6776
|
-
argmin_out: &mut Tensor<usize, Global, MAX_RANK>,
|
|
6777
|
-
max_out: &mut Tensor<Scalar::Output, Global, MAX_RANK>,
|
|
6778
|
-
argmax_out: &mut Tensor<usize, Global, MAX_RANK>,
|
|
6779
|
-
) -> Result<(), TensorError> {
|
|
6780
|
-
self.view()
|
|
6781
|
-
.try_minmax_axis_into(axis, keep_dims, min_out, argmin_out, max_out, argmax_out)
|
|
6782
|
-
}
|
|
6783
|
-
|
|
6784
|
-
fn try_min_all(&self) -> Result<Scalar::Output, TensorError> {
|
|
6785
|
-
self.view().try_min_all()
|
|
6786
|
-
}
|
|
6787
|
-
|
|
6788
|
-
fn try_argmin_all(&self) -> Result<usize, TensorError> {
|
|
6789
|
-
self.view().try_argmin_all()
|
|
6790
|
-
}
|
|
6791
|
-
|
|
6792
|
-
fn try_max_all(&self) -> Result<Scalar::Output, TensorError> {
|
|
6793
|
-
self.view().try_max_all()
|
|
6794
|
-
}
|
|
6795
|
-
|
|
6796
|
-
fn try_argmax_all(&self) -> Result<usize, TensorError> {
|
|
6797
|
-
self.view().try_argmax_all()
|
|
6798
|
-
}
|
|
6799
|
-
|
|
6800
|
-
fn try_min_axis<AnyIndex: VectorIndex>(
|
|
6801
|
-
&self,
|
|
6802
|
-
axis: AnyIndex,
|
|
6803
|
-
keep_dims: bool,
|
|
6804
|
-
) -> Result<Tensor<Scalar::Output, Global, MAX_RANK>, TensorError> {
|
|
6805
|
-
self.view().try_min_axis(axis, keep_dims)
|
|
6806
|
-
}
|
|
6807
|
-
|
|
6808
|
-
fn try_argmin_axis<AnyIndex: VectorIndex>(
|
|
6809
|
-
&self,
|
|
6810
|
-
axis: AnyIndex,
|
|
6811
|
-
keep_dims: bool,
|
|
6812
|
-
) -> Result<Tensor<usize, Global, MAX_RANK>, TensorError> {
|
|
6813
|
-
self.view().try_argmin_axis(axis, keep_dims)
|
|
6814
|
-
}
|
|
6815
|
-
|
|
6816
|
-
fn try_max_axis<AnyIndex: VectorIndex>(
|
|
6817
|
-
&self,
|
|
6818
|
-
axis: AnyIndex,
|
|
6819
|
-
keep_dims: bool,
|
|
6820
|
-
) -> Result<Tensor<Scalar::Output, Global, MAX_RANK>, TensorError> {
|
|
6821
|
-
self.view().try_max_axis(axis, keep_dims)
|
|
6822
|
-
}
|
|
6823
|
-
|
|
6824
|
-
fn try_argmax_axis<AnyIndex: VectorIndex>(
|
|
6825
|
-
&self,
|
|
6826
|
-
axis: AnyIndex,
|
|
6827
|
-
keep_dims: bool,
|
|
6828
|
-
) -> Result<Tensor<usize, Global, MAX_RANK>, TensorError> {
|
|
6829
|
-
self.view().try_argmax_axis(axis, keep_dims)
|
|
6830
|
-
}
|
|
6831
|
-
}
|
|
6832
|
-
|
|
6833
|
-
impl<Scalar: Clone + ReduceMinMax, const R: usize, C: TensorRef<Scalar, R>> MinMaxOps<Scalar, R>
|
|
6834
|
-
for C
|
|
6835
|
-
where
|
|
6836
|
-
Scalar::Output: Clone + Default + PartialOrd,
|
|
6837
|
-
{
|
|
6838
|
-
}
|
|
6492
|
+
// MomentsOps and MinMaxOps moved to crate::reduce
|
|
6839
6493
|
|
|
6840
6494
|
impl<const MAX_RANK: usize> Tensor<f32, Global, MAX_RANK> {
|
|
6841
6495
|
/// Sum all elements of the tensor.
|
|
@@ -6858,30 +6512,38 @@ impl<const MAX_RANK: usize> Tensor<f64, Global, MAX_RANK> {
|
|
|
6858
6512
|
#[cfg(test)]
|
|
6859
6513
|
mod tests {
|
|
6860
6514
|
use super::*;
|
|
6515
|
+
use crate::cast::CastOps;
|
|
6516
|
+
use crate::each::{AllCloseOps, ScaleOps, SumOps, TrigSinOps};
|
|
6517
|
+
use crate::reduce::MomentsOps;
|
|
6861
6518
|
use crate::types::{bf16c, f16, f16c, f32c};
|
|
6862
6519
|
|
|
6863
6520
|
#[test]
|
|
6864
|
-
fn
|
|
6865
|
-
// Creation
|
|
6521
|
+
fn tensor_creation_from_factories() {
|
|
6866
6522
|
let arr = Tensor::<f32>::try_full(&[3, 4], 1.0f32).unwrap();
|
|
6867
6523
|
assert_eq!(arr.shape(), &[3, 4]);
|
|
6868
6524
|
assert_eq!(arr.ndim(), 2);
|
|
6869
6525
|
assert_eq!(arr.numel(), 12);
|
|
6870
6526
|
assert!(!arr.is_empty());
|
|
6527
|
+
}
|
|
6871
6528
|
|
|
6872
|
-
|
|
6529
|
+
#[test]
|
|
6530
|
+
fn tensor_from_slice() {
|
|
6873
6531
|
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
6874
6532
|
let arr = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
6875
6533
|
assert_eq!(arr.shape(), &[3, 4]);
|
|
6876
6534
|
assert_eq!(arr.as_slice(), &data[..]);
|
|
6535
|
+
}
|
|
6877
6536
|
|
|
6878
|
-
|
|
6537
|
+
#[test]
|
|
6538
|
+
fn tensor_clone() {
|
|
6879
6539
|
let arr = Tensor::<f32>::try_full(&[3, 4], 2.5f32).unwrap();
|
|
6880
6540
|
let cloned = arr.clone();
|
|
6881
6541
|
assert_eq!(cloned.shape(), arr.shape());
|
|
6882
6542
|
assert_eq!(cloned.as_slice(), arr.as_slice());
|
|
6543
|
+
}
|
|
6883
6544
|
|
|
6884
|
-
|
|
6545
|
+
#[test]
|
|
6546
|
+
fn tensor_error_display() {
|
|
6885
6547
|
let err = TensorError::AllocationFailed;
|
|
6886
6548
|
assert_eq!(format!("{}", err), "memory allocation failed");
|
|
6887
6549
|
let err = TensorError::TooManyRanks { got: 10 };
|
|
@@ -6889,16 +6551,17 @@ mod tests {
|
|
|
6889
6551
|
}
|
|
6890
6552
|
|
|
6891
6553
|
#[test]
|
|
6892
|
-
fn
|
|
6893
|
-
// Row access
|
|
6554
|
+
fn tensor_row_access() {
|
|
6894
6555
|
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
6895
6556
|
let arr = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
6896
6557
|
assert_eq!(arr.row(0), Some(&[0.0, 1.0, 2.0, 3.0][..]));
|
|
6897
6558
|
assert_eq!(arr.row(1), Some(&[4.0, 5.0, 6.0, 7.0][..]));
|
|
6898
6559
|
assert_eq!(arr.row(2), Some(&[8.0, 9.0, 10.0, 11.0][..]));
|
|
6899
6560
|
assert_eq!(arr.row(3), None);
|
|
6561
|
+
}
|
|
6900
6562
|
|
|
6901
|
-
|
|
6563
|
+
#[test]
|
|
6564
|
+
fn tensor_slicing() {
|
|
6902
6565
|
let arr = Tensor::<f32>::try_full(&[4, 5], 1.0f32).unwrap();
|
|
6903
6566
|
let view = arr
|
|
6904
6567
|
.slice(&[SliceRange::full(), SliceRange::full()])
|
|
@@ -6913,19 +6576,25 @@ mod tests {
|
|
|
6913
6576
|
.unwrap();
|
|
6914
6577
|
assert_eq!(view.shape(), &[5]);
|
|
6915
6578
|
assert_eq!(view.ndim(), 1);
|
|
6579
|
+
}
|
|
6916
6580
|
|
|
6917
|
-
|
|
6581
|
+
#[test]
|
|
6582
|
+
fn tensor_transpose_2d() {
|
|
6918
6583
|
let arr = Tensor::<f32>::try_full(&[3, 4], 1.0f32).unwrap();
|
|
6919
6584
|
let transposed = arr.transpose().unwrap();
|
|
6920
6585
|
assert_eq!(transposed.shape(), &[4, 3]);
|
|
6586
|
+
}
|
|
6921
6587
|
|
|
6922
|
-
|
|
6588
|
+
#[test]
|
|
6589
|
+
fn tensor_is_contiguous() {
|
|
6923
6590
|
let arr = Tensor::<f32>::try_full(&[3, 4], 1.0f32).unwrap();
|
|
6924
6591
|
let view = arr.view();
|
|
6925
6592
|
assert!(view.is_contiguous());
|
|
6926
6593
|
assert!(arr.has_contiguous_rows());
|
|
6594
|
+
}
|
|
6927
6595
|
|
|
6928
|
-
|
|
6596
|
+
#[test]
|
|
6597
|
+
fn matrix_alias_round_trip() {
|
|
6929
6598
|
let mat: Matrix<f32> = Matrix::try_full(&[3, 4], 1.0f32).unwrap();
|
|
6930
6599
|
assert_eq!(mat.shape(), &[3, 4]);
|
|
6931
6600
|
}
|
|
@@ -7002,105 +6671,6 @@ mod tests {
|
|
|
7002
6671
|
assert!((sum - 100.0).abs() < 1e-9);
|
|
7003
6672
|
}
|
|
7004
6673
|
|
|
7005
|
-
#[test]
|
|
7006
|
-
fn elementwise_and_cast() {
|
|
7007
|
-
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
7008
|
-
let left = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
7009
|
-
let right = Tensor::<f32>::try_full(&[3, 4], 2.0).unwrap();
|
|
7010
|
-
|
|
7011
|
-
let left_even = left
|
|
7012
|
-
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
7013
|
-
.unwrap();
|
|
7014
|
-
let right_even = right
|
|
7015
|
-
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
7016
|
-
.unwrap();
|
|
7017
|
-
|
|
7018
|
-
let added = left_even.try_add_tensor(&right_even).unwrap();
|
|
7019
|
-
assert_eq!(added.shape(), &[3, 2]);
|
|
7020
|
-
assert_eq!(added.as_slice(), &[2.0, 4.0, 6.0, 8.0, 10.0, 12.0]);
|
|
7021
|
-
|
|
7022
|
-
let scaled = left_even.try_mul_scalar(0.5).unwrap();
|
|
7023
|
-
assert_eq!(scaled.as_slice(), &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
|
|
7024
|
-
|
|
7025
|
-
let casted = left_even.try_cast_dtype::<f64>().unwrap();
|
|
7026
|
-
assert_eq!(casted.shape(), &[3, 2]);
|
|
7027
|
-
assert_eq!(casted.as_slice(), &[0.0, 2.0, 4.0, 6.0, 8.0, 10.0]);
|
|
7028
|
-
|
|
7029
|
-
let complex = left_even.try_cast_dtype::<f32c>().unwrap();
|
|
7030
|
-
assert_eq!(complex.shape(), &[3, 2]);
|
|
7031
|
-
assert_eq!(complex.as_slice()[0], f32c::from_real_imag(0.0, 0.0));
|
|
7032
|
-
assert_eq!(complex.as_slice()[5], f32c::from_real_imag(10.0, 0.0));
|
|
7033
|
-
|
|
7034
|
-
let mut out = Tensor::<f32>::try_full(&[3, 4], 0.0).unwrap();
|
|
7035
|
-
left.try_add_tensor_into(&right, &mut out).unwrap();
|
|
7036
|
-
assert_eq!(out.as_slice()[0], 2.0);
|
|
7037
|
-
assert_eq!(out.as_slice()[11], 13.0);
|
|
7038
|
-
|
|
7039
|
-
let mut inplace = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
7040
|
-
inplace.try_add_scalar_inplace(1.0).unwrap();
|
|
7041
|
-
assert_eq!(inplace.as_slice()[0], 1.0);
|
|
7042
|
-
assert_eq!(inplace.as_slice()[11], 12.0);
|
|
7043
|
-
|
|
7044
|
-
let mut trig_out = Tensor::<f32>::try_full(&[3, 2], 0.0).unwrap();
|
|
7045
|
-
{
|
|
7046
|
-
let mut span = trig_out.span();
|
|
7047
|
-
left_even.try_sin_into(&mut span).unwrap();
|
|
7048
|
-
}
|
|
7049
|
-
assert_eq!(trig_out.shape(), &[3, 2]);
|
|
7050
|
-
assert!((trig_out.as_slice()[0] - 0.0).abs() < 1e-6);
|
|
7051
|
-
}
|
|
7052
|
-
|
|
7053
|
-
#[test]
|
|
7054
|
-
fn reductions_axis_and_strided_views() {
|
|
7055
|
-
let data: Vec<f32> = (0..12).map(|i| i as f32).collect();
|
|
7056
|
-
let a = Tensor::<f32>::try_from_slice(&data, &[3, 4]).unwrap();
|
|
7057
|
-
let a_even = a
|
|
7058
|
-
.slice(&[SliceRange::full(), SliceRange::range_step(0, 4, 2)])
|
|
7059
|
-
.unwrap();
|
|
7060
|
-
|
|
7061
|
-
let sum_all = a_even.try_sum_all().unwrap();
|
|
7062
|
-
assert!((sum_all - 30.0).abs() < 1e-6);
|
|
7063
|
-
|
|
7064
|
-
let norm_all = a_even.try_norm_all().unwrap();
|
|
7065
|
-
assert!((norm_all - 14.832396974191326).abs() < 1e-9);
|
|
7066
|
-
|
|
7067
|
-
let (sum_axis0, sumsq_axis0) = a_even.try_moments_axis(0, false).unwrap();
|
|
7068
|
-
assert_eq!(sum_axis0.shape(), &[2]);
|
|
7069
|
-
assert!((sum_axis0.as_slice()[0] - 12.0).abs() < 1e-6);
|
|
7070
|
-
assert!((sum_axis0.as_slice()[1] - 18.0).abs() < 1e-6);
|
|
7071
|
-
assert!((sumsq_axis0.as_slice()[0] - 80.0).abs() < 1e-6);
|
|
7072
|
-
assert!((sumsq_axis0.as_slice()[1] - 140.0).abs() < 1e-6);
|
|
7073
|
-
|
|
7074
|
-
let sum_axis1_keep = a_even.try_sum_axis(-1_i32, true).unwrap();
|
|
7075
|
-
assert_eq!(sum_axis1_keep.shape(), &[3, 1]);
|
|
7076
|
-
assert!((sum_axis1_keep.as_slice()[0] - 2.0).abs() < 1e-6);
|
|
7077
|
-
assert!((sum_axis1_keep.as_slice()[1] - 10.0).abs() < 1e-6);
|
|
7078
|
-
assert!((sum_axis1_keep.as_slice()[2] - 18.0).abs() < 1e-6);
|
|
7079
|
-
|
|
7080
|
-
let MinMaxResult {
|
|
7081
|
-
min_value: min_axis0,
|
|
7082
|
-
min_index: argmin_axis0,
|
|
7083
|
-
max_value: max_axis0,
|
|
7084
|
-
max_index: argmax_axis0,
|
|
7085
|
-
} = a_even.try_minmax_axis(0, false).unwrap();
|
|
7086
|
-
assert_eq!(min_axis0.as_slice(), &[0.0, 2.0]);
|
|
7087
|
-
assert_eq!(max_axis0.as_slice(), &[8.0, 10.0]);
|
|
7088
|
-
assert_eq!(argmin_axis0.as_slice(), &[0, 0]);
|
|
7089
|
-
assert_eq!(argmax_axis0.as_slice(), &[2, 2]);
|
|
7090
|
-
|
|
7091
|
-
let reversed = a
|
|
7092
|
-
.slice(&[SliceRange::full(), SliceRange::range_step(3, 0, -1)])
|
|
7093
|
-
.unwrap();
|
|
7094
|
-
let reversed_sum = reversed.try_sum_axis(-1_i32, false).unwrap();
|
|
7095
|
-
assert_eq!(reversed_sum.shape(), &[3]);
|
|
7096
|
-
assert_eq!(reversed_sum.as_slice(), &[6.0, 18.0, 30.0]);
|
|
7097
|
-
|
|
7098
|
-
let reversed_argmin = reversed.try_argmin_axis(-1_i32, false).unwrap();
|
|
7099
|
-
let reversed_argmax = reversed.try_argmax_axis(-1_i32, false).unwrap();
|
|
7100
|
-
assert_eq!(reversed_argmin.as_slice(), &[2, 2, 2]);
|
|
7101
|
-
assert_eq!(reversed_argmax.as_slice(), &[0, 0, 0]);
|
|
7102
|
-
}
|
|
7103
|
-
|
|
7104
6674
|
#[test]
|
|
7105
6675
|
fn nd_contraction_global_reductions() {
|
|
7106
6676
|
// Verify that N-D contiguous tensors produce the same moments/minmax as a flat copy.
|
|
@@ -7560,6 +7130,44 @@ mod tests {
|
|
|
7560
7130
|
assert_eq!(tensor.shape(), &[2, 4]);
|
|
7561
7131
|
assert_eq!(tensor.storage_len(), 4);
|
|
7562
7132
|
}
|
|
7133
|
+
|
|
7134
|
+
#[test]
|
|
7135
|
+
fn fill_zeros_and_fill_on_tensor_and_span() {
|
|
7136
|
+
// Tensor: fill with zero, then fill with 7.0, verify storage matches.
|
|
7137
|
+
let mut tensor = unsafe { Tensor::<f32>::try_empty(&[3, 4]).unwrap() };
|
|
7138
|
+
tensor.fill_zeros();
|
|
7139
|
+
assert!(tensor.as_slice().iter().all(|&value| value == 0.0));
|
|
7140
|
+
tensor.fill(7.0);
|
|
7141
|
+
assert!(tensor.as_slice().iter().all(|&value| value == 7.0));
|
|
7142
|
+
|
|
7143
|
+
// TensorSpan via tensor.span() also exposes Fill methods.
|
|
7144
|
+
let mut other = unsafe { Tensor::<f32>::try_empty(&[3, 4]).unwrap() };
|
|
7145
|
+
{
|
|
7146
|
+
let mut span = other.span();
|
|
7147
|
+
span.fill_zeros();
|
|
7148
|
+
span.fill(2.5);
|
|
7149
|
+
}
|
|
7150
|
+
assert!(other.as_slice().iter().all(|&value| value == 2.5));
|
|
7151
|
+
}
|
|
7152
|
+
|
|
7153
|
+
#[test]
|
|
7154
|
+
fn copy_from_via_trait_round_trips_through_span() {
|
|
7155
|
+
let source_tensor = Tensor::<f32>::try_from_slice(&[1.0, 2.0, 3.0, 4.0], &[2, 2]).unwrap();
|
|
7156
|
+
let mut destination_tensor = unsafe { Tensor::<f32>::try_empty(&[2, 2]).unwrap() };
|
|
7157
|
+
// Tensor: CopyFrom<&[Scalar]>.
|
|
7158
|
+
destination_tensor.copy_from(source_tensor.as_slice()).unwrap();
|
|
7159
|
+
assert_eq!(destination_tensor.as_slice(), source_tensor.as_slice());
|
|
7160
|
+
|
|
7161
|
+
// TensorSpan: CopyFrom<&TensorView>.
|
|
7162
|
+
let mut span_destination = unsafe { Tensor::<f32>::try_empty(&[2, 2]).unwrap() };
|
|
7163
|
+
{
|
|
7164
|
+
let mut span = span_destination.span();
|
|
7165
|
+
let view = source_tensor.view();
|
|
7166
|
+
span.copy_from(&view).unwrap();
|
|
7167
|
+
}
|
|
7168
|
+
assert_eq!(span_destination.as_slice(), source_tensor.as_slice());
|
|
7169
|
+
}
|
|
7170
|
+
|
|
7563
7171
|
}
|
|
7564
7172
|
|
|
7565
7173
|
// endregion: Tests
|