1use crate::TVec;
3use crate::blob::Blob;
4use crate::datum::{ClampCast, Datum, DatumType, QParams, round_ties_to_even, scale_by};
5use crate::dim::TDim;
6use crate::internal::*;
7use half::f16;
8use itertools::{Itertools, izip};
9use ndarray::prelude::*;
10#[cfg(feature = "complex")]
11use num_complex::Complex;
12use num_traits::Float;
13use std::borrow::Cow;
14use std::fmt;
15use std::hash::Hash;
16use std::ops::Range;
17use std::sync::Arc;
18
19pub mod litteral;
20pub mod plain_view;
21pub mod storage;
22pub mod view;
23
24pub use plain_view::{PlainView, PlainViewMut};
25use storage::{PlainStorage, StorageKind, TensorStorage};
26
27#[derive(Copy, Clone, Default, Debug)]
28pub enum Approximation {
29 Exact,
30 #[default]
31 Close,
32 Approximate,
33 VeryApproximate,
34 SuperApproximate,
35 UltraApproximate,
36 Custom(f32, f32, f32),
37 Ulp(u64),
45}
46
47impl PartialEq for Approximation {
48 fn eq(&self, other: &Self) -> bool {
49 use Approximation::*;
50 match (self, other) {
51 (Custom(aa, ar, ao), Custom(ba, br, bo)) => aa == ba && ar == br && bo == ao,
52 (Ulp(a), Ulp(b)) => a == b,
53 _ => std::mem::discriminant(self) == std::mem::discriminant(other),
54 }
55 }
56}
57
58impl Eq for Approximation {}
59
60impl From<bool> for Approximation {
61 fn from(b: bool) -> Self {
62 if b { Self::Approximate } else { Self::Exact }
63 }
64}
65
66impl Approximation {
67 fn atol_rtol_outliers(&self, dt: &DatumType) -> (f64, f64, f64) {
68 use Approximation::*;
69 match (self, dt) {
70 (Exact, _) => (0.0, 0.0, 0.0),
71 (Close, DatumType::F16) => (1e-3, 1e-3, 0.0),
72 (Approximate, DatumType::F16) => (1e-3, 5e-3, 0.0),
73 (Approximate, qp) if qp.is_quantized() => (qp.zp_scale().1 as f64, 0., 0.0),
74 (Close, _) => (1e-7, 1e-7, 0.0),
75 (Approximate, _) => (1e-4, 5e-4, 0.0),
76 (VeryApproximate, _) => (5e-2, 1e-2, 0.0),
77 (SuperApproximate, _) => (0.1, 0.05, 0.0001),
78 (UltraApproximate, _) => (0.2, 0.1, 0.0005),
79 (Custom(atol, rtol, out), _) => (*atol as _, *rtol as _, *out as _),
80 (Ulp(_), _) => (0.0, 0.0, 0.0),
83 }
84 }
85}
86
87pub struct Tensor {
89 dt: DatumType,
90 shape: TVec<usize>,
91 strides: TVec<isize>,
92 len: usize,
93 storage: StorageKind,
94}
95
96unsafe impl Send for Tensor {}
97unsafe impl Sync for Tensor {}
98
99impl Hash for Tensor {
100 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
101 use DatumType::*;
102 self.dt.hash(state);
103 self.shape.hash(state);
104 if let Some(plain) = self.storage.as_plain_ram() {
105 plain.layout().align().hash(state);
106 unsafe {
107 match self.dt {
108 Bool => self.as_slice_unchecked::<bool>().hash(state),
109 I8 => self.as_slice_unchecked::<i8>().hash(state),
110 I16 => self.as_slice_unchecked::<i16>().hash(state),
111 I32 => self.as_slice_unchecked::<i32>().hash(state),
112 I64 => self.as_slice_unchecked::<i64>().hash(state),
113 U8 => self.as_slice_unchecked::<u8>().hash(state),
114 U16 => self.as_slice_unchecked::<u16>().hash(state),
115 U32 => self.as_slice_unchecked::<u32>().hash(state),
116 U64 => self.as_slice_unchecked::<u64>().hash(state),
117 F16 => self.as_slice_unchecked::<i16>().hash(state),
118 F32 => self.as_slice_unchecked::<i32>().hash(state),
119 F64 => self.as_slice_unchecked::<i64>().hash(state),
120 TDim => self.as_slice_unchecked::<crate::dim::TDim>().hash(state),
121 String => self.as_slice_unchecked::<std::string::String>().hash(state),
122 Blob => self.as_slice_unchecked::<crate::blob::Blob>().hash(state),
123 QI8(_) => self.as_slice_unchecked::<i8>().hash(state),
124 QU8(_) => self.as_slice_unchecked::<u8>().hash(state),
125 QI32(_) => self.as_slice_unchecked::<i32>().hash(state),
126 #[cfg(feature = "complex")]
127 ComplexI16 => self.as_slice_unchecked::<Complex<i16>>().hash(state),
128 #[cfg(feature = "complex")]
129 ComplexI32 => self.as_slice_unchecked::<Complex<i32>>().hash(state),
130 #[cfg(feature = "complex")]
131 ComplexI64 => self.as_slice_unchecked::<Complex<i64>>().hash(state),
132 #[cfg(feature = "complex")]
133 ComplexF16 => self.as_slice_unchecked::<Complex<i16>>().hash(state),
134 #[cfg(feature = "complex")]
135 ComplexF32 => self.as_slice_unchecked::<Complex<i32>>().hash(state),
136 #[cfg(feature = "complex")]
137 ComplexF64 => self.as_slice_unchecked::<Complex<i64>>().hash(state),
138 }
139 }
140 } else {
141 self.storage.dyn_hash(state);
142 }
143 }
144}
145
146impl Clone for Tensor {
147 fn clone(&self) -> Tensor {
148 self.deep_clone()
149 }
150}
151
152impl Default for Tensor {
153 fn default() -> Tensor {
154 litteral::tensor0(0f32)
155 }
156}
157
158impl Drop for Tensor {
159 fn drop(&mut self) {
160 if self.as_plain_ram_storage().is_some() {
161 macro_rules! drop_in_place {
162 ($t: ty) => {
163 if self.dt == <$t>::datum_type() {
164 unsafe {
165 let slice = self.as_slice_mut_unchecked::<$t>();
166 std::ptr::drop_in_place(slice as *mut [$t]);
167 }
168 }
169 };
170 }
171 drop_in_place!(Blob);
172 drop_in_place!(String);
173 drop_in_place!(TDim);
174 }
175 }
177}
178
179#[allow(unreachable_code)]
180pub fn vector_size() -> usize {
181 #[cfg(target_arch = "x86_64")]
182 {
183 return if is_x86_feature_detected!("avx512f") { 512 / 8 } else { 256 / 8 };
184 }
185 128 / 8
186}
187
188#[inline]
196unsafe fn copy_blocks<T: Copy>(
197 src: *const u8,
198 dst: *mut u8,
199 outer: usize,
200 block: usize,
201 out_stride: usize,
202) {
203 unsafe {
204 let n = block / std::mem::size_of::<T>();
205 for o in 0..outer {
206 let s = src.add(o * block) as *const T;
207 let d = dst.add(o * out_stride) as *mut T;
208 for i in 0..n {
209 *d.add(i) = *s.add(i);
210 }
211 }
212 }
213}
214
215#[allow(clippy::too_many_arguments)]
219fn transpose_tile_cells<const N: usize>(
220 src: &[u8],
221 dst: &mut [u8],
222 i0: usize,
223 i1: usize,
224 m0: usize,
225 m1: usize,
226 a: usize,
227 mid: usize,
228) {
229 let (src, _) = src.as_chunks::<N>();
230 let (dst, _) = dst.as_chunks_mut::<N>();
231 if (a * N).is_multiple_of(4096) {
232 for (m, row) in dst.chunks_exact_mut(a).enumerate().take(m1).skip(m0) {
233 for (cell, i) in row[i0..i1].iter_mut().zip(i0..i1) {
234 *cell = src[i * mid + m];
235 }
236 }
237 } else {
238 let mut i = i0;
239 while i + 4 <= i1 {
240 let mut m = m0;
241 while m + 4 <= m1 {
242 let r0: &[[u8; N]; 4] = src[i * mid + m..i * mid + m + 4].try_into().unwrap();
243 let r1: &[[u8; N]; 4] =
244 src[(i + 1) * mid + m..(i + 1) * mid + m + 4].try_into().unwrap();
245 let r2: &[[u8; N]; 4] =
246 src[(i + 2) * mid + m..(i + 2) * mid + m + 4].try_into().unwrap();
247 let r3: &[[u8; N]; 4] =
248 src[(i + 3) * mid + m..(i + 3) * mid + m + 4].try_into().unwrap();
249 for k in 0..4 {
250 dst[(m + k) * a + i..(m + k) * a + i + 4]
251 .copy_from_slice(&[r0[k], r1[k], r2[k], r3[k]]);
252 }
253 m += 4;
254 }
255 while m < m1 {
256 for ii in i..i + 4 {
257 dst[m * a + ii] = src[ii * mid + m];
258 }
259 m += 1;
260 }
261 i += 4;
262 }
263 for i in i..i1 {
264 for (cell, m) in src[i * mid + m0..i * mid + m1].iter().zip(m0..m1) {
265 dst[m * a + i] = *cell;
266 }
267 }
268 }
269}
270
271#[allow(clippy::too_many_arguments)]
275fn transpose_tile(
276 src: &[u8],
277 dst: &mut [u8],
278 i0: usize,
279 i1: usize,
280 m0: usize,
281 m1: usize,
282 a: usize,
283 mid: usize,
284 item: usize,
285) {
286 match item {
287 1 => transpose_tile_cells::<1>(src, dst, i0, i1, m0, m1, a, mid),
288 2 => transpose_tile_cells::<2>(src, dst, i0, i1, m0, m1, a, mid),
289 4 => transpose_tile_cells::<4>(src, dst, i0, i1, m0, m1, a, mid),
290 8 => transpose_tile_cells::<8>(src, dst, i0, i1, m0, m1, a, mid),
291 16 => transpose_tile_cells::<16>(src, dst, i0, i1, m0, m1, a, mid),
292 _ => {
293 for i in i0..i1 {
294 for m in m0..m1 {
295 let s = (i * mid + m) * item;
296 let d = (m * a + i) * item;
297 dst[d..d + item].copy_from_slice(&src[s..s + item]);
298 }
299 }
300 }
301 }
302}
303
304fn transpose_cells(src: &[u8], dst: &mut [u8], a: usize, mid: usize, item: usize) {
308 let tile = (64 / item).max(16);
309 let mut i0 = 0;
310 while i0 < a {
311 let i1 = (i0 + tile).min(a);
312 let mut m0 = 0;
313 while m0 < mid {
314 let m1 = (m0 + tile).min(mid);
315 transpose_tile(src, dst, i0, i1, m0, m1, a, mid, item);
316 m0 += tile;
317 }
318 i0 += tile;
319 }
320}
321
322fn move_axis_blocks(
328 src: &[u8],
329 dst: &mut [u8],
330 outer: usize,
331 p: usize,
332 q: usize,
333 inner: usize,
334 elem_size: usize,
335) {
336 let item = inner * elem_size;
337 let block = outer * p * q * item;
338 if p == 1 || q == 1 || inner == 0 {
339 dst[..block].copy_from_slice(&src[..block]);
340 return;
341 }
342 if item >= 64 {
343 for o in 0..outer {
344 for i in 0..p {
345 for m in 0..q {
346 let s = ((o * p + i) * q + m) * item;
347 let d = ((o * q + m) * p + i) * item;
348 dst[d..d + item].copy_from_slice(&src[s..s + item]);
349 }
350 }
351 }
352 } else {
353 for o in 0..outer {
354 transpose_cells(
355 &src[o * p * q * item..(o + 1) * p * q * item],
356 &mut dst[o * p * q * item..(o + 1) * p * q * item],
357 p,
358 q,
359 item,
360 );
361 }
362 }
363}
364
365fn move_axis_pair(axes: &[usize]) -> Option<(usize, usize)> {
368 let lo = (0..axes.len()).find(|&i| axes[i] != i)?;
369 let hi = (0..axes.len()).rev().find(|&i| axes[i] != i).unwrap();
370 if axes[hi] == lo && (lo..hi).all(|i| axes[i] == i + 1) {
371 return Some((lo, hi));
372 }
373 if axes[lo] == hi && (lo + 1..=hi).all(|i| axes[i] == i - 1) {
374 return Some((hi, lo));
375 }
376 None
377}
378
379impl Tensor {
380 #[inline]
387 fn plain_ram_storage(&self) -> &PlainStorage {
388 self.storage.materialize_plain_ram().expect("Non-plain storage")
389 }
390
391 #[inline]
392 fn plain_ram_storage_mut(&mut self) -> &mut PlainStorage {
393 self.storage.as_plain_ram_mut().expect("Non-plain storage")
394 }
395
396 pub fn storage_as<T: TensorStorage>(&self) -> Option<&T> {
397 self.storage.as_storage().downcast_ref::<T>()
398 }
399
400 pub fn try_storage_as<T: TensorStorage>(&self) -> TractResult<&T> {
401 self.storage_as::<T>().context("Unexpected tensor storage type")
402 }
403
404 pub fn from_storage(
405 dt: DatumType,
406 shape: &[usize],
407 storage: impl TensorStorage + 'static,
408 ) -> Tensor {
409 let len = shape.iter().product::<usize>();
410 let strides = Self::natural_strides(shape);
411 Tensor {
412 dt,
413 shape: shape.into(),
414 strides,
415 len,
416 storage: StorageKind::Exotic(Box::new(storage)),
417 }
418 }
419
420 #[inline]
422 pub fn as_plain_ram(&self) -> Option<PlainView<'_>> {
423 let storage = self.storage.as_plain_ram()?;
424 Some(PlainView::new(self, storage))
425 }
426
427 #[inline]
433 pub fn try_as_plain_ram(&self) -> TractResult<PlainView<'_>> {
434 let storage = self.storage.materialize_plain_ram()?;
435 Ok(PlainView::new(self, storage))
436 }
437
438 #[inline]
442 pub fn as_plain_ram_storage(&self) -> Option<&PlainStorage> {
443 self.storage.as_plain_ram()
444 }
445
446 #[inline]
449 pub fn as_plain_ram_storage_mut(&mut self) -> Option<&mut PlainStorage> {
450 self.storage.as_plain_ram_mut()
451 }
452
453 #[inline]
460 pub fn is_plain(&self) -> bool {
461 !self.storage.is_exotic()
462 }
463
464 #[inline]
467 pub fn is_exotic(&self) -> bool {
468 self.storage.is_exotic()
469 }
470
471 #[inline]
475 pub fn is_plain_ram(&self) -> bool {
476 self.as_plain_ram_storage().is_some()
477 }
478
479 #[inline]
488 pub fn in_ram(&self) -> bool {
489 self.storage.in_ram()
490 }
491
492 pub fn exotic_fact(&self) -> TractResult<Option<Box<dyn crate::exotic::ExoticFact>>> {
494 self.storage.as_storage().exotic_fact(&self.shape)
495 }
496
497 #[inline]
499 pub fn as_plain_ram_mut(&mut self) -> Option<PlainViewMut<'_>> {
500 let storage = self.storage.as_plain_ram_mut()?;
501 Some(PlainViewMut::new(self.dt, &self.shape, &self.strides, self.len, storage))
502 }
503
504 #[inline]
506 pub fn try_as_plain_ram_mut(&mut self) -> TractResult<PlainViewMut<'_>> {
507 self.as_plain_ram_mut().context("Tensor storage is not plain")
508 }
509
510 #[inline]
512 pub unsafe fn uninitialized<T: Datum>(shape: &[usize]) -> TractResult<Tensor> {
513 unsafe { Self::uninitialized_dt(T::datum_type(), shape) }
514 }
515
516 #[inline]
518 pub unsafe fn uninitialized_dt(dt: DatumType, shape: &[usize]) -> TractResult<Tensor> {
519 unsafe { Self::uninitialized_aligned_dt(dt, shape, vector_size()) }
520 }
521
522 #[inline]
524 pub unsafe fn uninitialized_aligned<T: Datum>(
525 shape: &[usize],
526 alignment: usize,
527 ) -> TractResult<Tensor> {
528 unsafe { Self::uninitialized_aligned_dt(T::datum_type(), shape, alignment) }
529 }
530
531 pub unsafe fn uninitialized_aligned_dt(
533 dt: DatumType,
534 shape: &[usize],
535 alignment: usize,
536 ) -> TractResult<Tensor> {
537 let bytes = shape
542 .iter()
543 .try_fold(dt.size_of(), |acc, &d| acc.checked_mul(d))
544 .filter(|&b| b <= isize::MAX as usize)
545 .ok_or_else(|| format_err!("tensor shape {shape:?} of {dt:?} is too large"))?;
546 let storage = StorageKind::Plain(PlainStorage::from(unsafe {
547 Blob::new_for_size_and_align(bytes, alignment)
548 }));
549 let mut tensor = Tensor { strides: tvec!(), dt, shape: shape.into(), storage, len: 0 };
550 if tensor.shape.len() == 0 {
551 tensor.len = 1;
552 } else {
553 tensor.update_strides_and_len();
554 }
555 if !tensor.storage.is_empty() {
556 unsafe fn write_defaults<T: Datum + Default>(tensor: &mut Tensor) {
557 unsafe {
558 let len = tensor.len;
559 let dst = tensor.as_slice_mut_unchecked::<T>().as_mut_ptr();
560 for i in 0..len {
561 std::ptr::write(dst.add(i), T::default());
562 }
563 }
564 }
565 if dt == String::datum_type() {
566 unsafe { write_defaults::<String>(&mut tensor) }
567 } else if dt == Blob::datum_type() {
568 unsafe { write_defaults::<Blob>(&mut tensor) }
569 } else if dt == TDim::datum_type() {
570 unsafe { write_defaults::<TDim>(&mut tensor) }
571 } else if cfg!(debug_assertions) {
572 assert!(dt.is_copy());
573 if dt == DatumType::F32 {
574 tensor.fill_t(f32::NAN).unwrap();
575 } else {
576 tensor.as_bytes_mut().iter_mut().for_each(|x| *x = (-1i8) as u8);
578 }
579 }
580 }
581 Ok(tensor)
582 }
583
584 pub fn stack_tensors(
585 axis: usize,
586 tensors: &[impl std::borrow::Borrow<Tensor>],
587 ) -> TractResult<Tensor> {
588 ensure!(tensors.len() > 0);
589 let rank = tensors[0].borrow().rank();
590 ensure!(axis < rank);
591 ensure!(tensors.iter().all(|t| t.borrow().rank() == rank));
592 let dt = tensors[0].borrow().datum_type();
593 ensure!(tensors.iter().all(|t| t.borrow().datum_type() == dt));
594 let mut shape: TVec<usize> = tensors[0].borrow().shape().into();
595 for ax in 0..rank {
596 if ax != axis {
597 ensure!(tensors.iter().all(|t| t.borrow().shape()[ax] == shape[ax]));
598 }
599 }
600 shape[axis] = tensors.iter().map(|v| v.borrow().shape()[axis]).sum();
601 unsafe {
602 let mut result = Tensor::uninitialized_dt(dt, &shape)?;
603 let outer: usize = shape[..axis].iter().product();
606 let out_stride = shape[axis..].iter().product::<usize>() * dt.size_of();
607 const SMALL_BLOCK_BYTES: usize = 64;
615 if dt.is_copy()
616 && outer > 0
617 && tensors.iter().all(|t| t.borrow().storage.as_plain_ram().is_some())
618 {
619 let out = result.plain_ram_storage_mut().as_mut_ptr();
620 let mut offset = 0isize;
621 for v in tensors {
622 let v = v.borrow();
623 let block = v.storage.byte_len() / outer;
624 let src = v.plain_ram_storage().as_ptr();
625 let dst = out.offset(offset);
626 if outer == 1 {
627 std::ptr::copy_nonoverlapping(src, dst, block);
628 } else if block >= SMALL_BLOCK_BYTES {
629 for o in 0..outer {
630 std::ptr::copy_nonoverlapping(
631 src.add(o * block),
632 dst.add(o * out_stride),
633 block,
634 );
635 }
636 } else {
637 match dt.size_of() {
640 1 => copy_blocks::<u8>(src, dst, outer, block, out_stride),
641 2 => copy_blocks::<u16>(src, dst, outer, block, out_stride),
642 4 => copy_blocks::<u32>(src, dst, outer, block, out_stride),
643 8 => copy_blocks::<u64>(src, dst, outer, block, out_stride),
644 16 => copy_blocks::<u128>(src, dst, outer, block, out_stride),
645 _ => {
646 for o in 0..outer {
647 std::ptr::copy_nonoverlapping(
648 src.add(o * block),
649 dst.add(o * out_stride),
650 block,
651 );
652 }
653 }
654 }
655 }
656 offset += block as isize;
657 }
658 } else {
659 let mut offset = 0;
660 for t in tensors {
661 let t = t.borrow();
662 let len = t.shape()[axis];
663 result.assign_slice_from_resolved(
664 &[],
665 offset..offset + len,
666 t,
667 &[],
668 0..len,
669 axis,
670 );
671 offset += len;
672 }
673 }
674
675 Ok(result)
676 }
677 }
678
679 pub fn clear<T: Datum + num_traits::Zero + Clone>(&mut self) -> TractResult<()> {
680 self.fill_t(T::zero())
681 }
682
683 pub fn zero<T: Datum + num_traits::Zero>(shape: &[usize]) -> TractResult<Tensor> {
684 unsafe {
685 let mut t = Tensor::uninitialized::<T>(shape)?;
686 t.clear::<T>()?;
687 Ok(t)
688 }
689 }
690
691 pub fn zero_scalar<T: Datum + num_traits::Zero>() -> TractResult<Tensor> {
692 Tensor::zero::<T>(&[])
693 }
694
695 pub fn zero_scalar_dt(dt: DatumType) -> TractResult<Tensor> {
696 Tensor::zero_dt(dt, &[])
697 }
698
699 pub fn zero_dt(dt: DatumType, shape: &[usize]) -> TractResult<Tensor> {
700 Tensor::zero_aligned_dt(dt, shape, vector_size())
701 }
702
703 pub fn fill_t<T: Datum + Clone>(&mut self, value: T) -> TractResult<()> {
704 self.try_as_plain_ram_mut()?
705 .as_slice_mut::<T>()?
706 .iter_mut()
707 .for_each(|item| *item = value.clone());
708 Ok(())
709 }
710
711 pub fn zero_aligned_dt(
712 dt: DatumType,
713 shape: &[usize],
714 alignment: usize,
715 ) -> TractResult<Tensor> {
716 if shape.iter().product::<usize>() == 0 {
717 unsafe { return Tensor::uninitialized_dt(dt, shape) };
718 }
719 if dt.is_quantized() {
720 unsafe {
721 let mut t = Tensor::uninitialized_dt(dt, shape)?;
722 let zp = dt.zp_scale().0;
723 match dt.unquantized() {
724 DatumType::I8 => t
725 .try_as_plain_ram_mut()?
726 .as_slice_mut::<i8>()?
727 .iter_mut()
728 .for_each(|item| *item = zp as _),
729 DatumType::U8 => t
730 .try_as_plain_ram_mut()?
731 .as_slice_mut::<u8>()?
732 .iter_mut()
733 .for_each(|item| *item = zp as _),
734 DatumType::I32 => t
735 .try_as_plain_ram_mut()?
736 .as_slice_mut::<i32>()?
737 .iter_mut()
738 .for_each(|item| *item = zp as _),
739 _ => unreachable!(),
740 }
741 Ok(t)
742 }
743 } else if dt == DatumType::Bool {
744 let mut t = unsafe { Tensor::uninitialized_dt(dt, shape)? };
745 t.fill_t::<bool>(false)?;
746 Ok(t)
747 } else {
748 dispatch_zerolike!(Self::zero_aligned(dt)(shape, alignment))
749 }
750 }
751
752 pub fn zero_aligned<T: Datum + num_traits::Zero>(
753 shape: &[usize],
754 alignment: usize,
755 ) -> TractResult<Tensor> {
756 unsafe {
757 let mut tensor = Self::uninitialized_aligned::<T>(shape, alignment)?;
758 tensor.clear::<T>()?;
759 Ok(tensor)
760 }
761 }
762
763 pub fn from_shape<T: Datum + Copy>(shape: &[usize], data: &[T]) -> TractResult<Tensor> {
766 Self::from_shape_align(shape, data, vector_size())
767 }
768
769 pub fn from_shape_align<T: Datum + Copy>(
772 shape: &[usize],
773 data: &[T],
774 align: usize,
775 ) -> TractResult<Tensor> {
776 ensure!(
777 data.len() == shape.iter().product::<usize>(),
778 "Shape product must be equal to data length"
779 );
780 unsafe {
781 let bytes = std::slice::from_raw_parts(
782 data.as_ptr() as *const u8,
783 data.len() * T::datum_type().size_of(),
784 );
785 let dt = T::datum_type();
786 Self::from_raw_dt_align(dt, shape, bytes, align)
787 }
788 }
789
790 pub unsafe fn from_raw<T: Datum>(shape: &[usize], content: &[u8]) -> TractResult<Tensor> {
794 unsafe { Tensor::from_raw_dt(T::datum_type(), shape, content) }
795 }
796
797 pub unsafe fn from_raw_aligned<T: Datum>(
798 shape: &[usize],
799 content: &[u8],
800 align: usize,
801 ) -> TractResult<Tensor> {
802 unsafe { Tensor::from_raw_dt_align(T::datum_type(), shape, content, align) }
803 }
804
805 pub unsafe fn from_raw_dt(
806 dt: DatumType,
807 shape: &[usize],
808 content: &[u8],
809 ) -> TractResult<Tensor> {
810 unsafe { Self::from_raw_dt_align(dt, shape, content, vector_size()) }
811 }
812
813 pub unsafe fn from_raw_dt_align(
814 dt: DatumType,
815 shape: &[usize],
816 content: &[u8],
817 align: usize,
818 ) -> TractResult<Tensor> {
819 let len = shape
824 .iter()
825 .try_fold(1usize, |acc, &d| acc.checked_mul(d))
826 .ok_or_else(|| format_err!("tensor shape {shape:?} overflows"))?;
827 let expected = len
828 .checked_mul(dt.size_of())
829 .ok_or_else(|| format_err!("tensor shape {shape:?} of {dt:?} is too large"))?;
830 ensure!(
831 content.len() == expected,
832 "Raw tensor data length ({}) does not match shape {:?} of {:?} ({} bytes)",
833 content.len(),
834 shape,
835 dt,
836 expected
837 );
838 let mut tensor = unsafe { Tensor::uninitialized_aligned_dt(dt, shape, align) }?;
839 tensor.as_bytes_mut().copy_from_slice(content);
840 Ok(tensor)
841 }
842
843 pub unsafe fn from_slice_align<T: Datum>(content: &[T], align: usize) -> TractResult<Tensor> {
844 let bytes = if content.len() == 0 {
845 &[]
846 } else {
847 unsafe {
848 std::slice::from_raw_parts(
849 content.as_ptr() as *const u8,
850 content.len() * T::datum_type().size_of(),
851 )
852 }
853 };
854 unsafe { Self::from_raw_dt_align(T::datum_type(), &[content.len()], bytes, align) }
855 }
856
857 #[inline]
859 pub fn rank(&self) -> usize {
860 self.shape.len()
861 }
862
863 #[inline]
865 pub fn shape(&self) -> &[usize] {
866 &self.shape
867 }
868
869 #[inline]
871 #[allow(clippy::len_without_is_empty)]
872 pub fn len(&self) -> usize {
873 self.len
874 }
875
876 #[inline]
878 #[allow(clippy::len_without_is_empty)]
879 pub fn volume(&self) -> usize {
880 self.len
881 }
882
883 #[inline]
885 pub fn strides(&self) -> &[isize] {
886 &self.strides
887 }
888
889 fn update_strides_and_len(&mut self) {
890 self.strides.clear();
891 if self.shape.len() == 0 {
892 self.len = 1;
893 return;
894 }
895 compute_natural_stride_to(&mut self.strides, &self.shape);
896 self.len = unsafe { *self.strides.get_unchecked(0) as usize * self.shape.get_unchecked(0) };
897 }
898
899 pub unsafe fn set_shape_unchecked(&mut self, shape: &[usize]) {
901 if shape != &*self.shape {
902 self.shape.clear();
903 self.shape.extend_from_slice(shape);
904 self.update_strides_and_len();
905 }
906 }
907
908 pub unsafe fn set_geometry_unchecked(&mut self, shape: &[usize], strides: &[isize]) {
910 self.shape.clear();
911 self.shape.extend_from_slice(shape);
912 self.strides.clear();
913 self.strides.extend_from_slice(strides);
914 }
915
916 pub fn set_shape(&mut self, shape: &[usize]) -> TractResult<()> {
918 if self.len() != shape.iter().product::<usize>() {
919 bail!("Invalid reshape {:?} to {:?}", self.shape, shape);
920 }
921 unsafe { self.set_shape_unchecked(shape) }
922 Ok(())
923 }
924
925 #[inline]
928 fn can_move_axis_blocks(&self) -> bool {
929 self.dt.is_copy()
930 && self.has_plain_ram_storage()
931 && self.strides[..] == natural_strides(&self.shape)[..]
932 }
933
934 pub fn moved_axis(&self, from: usize, to: usize) -> TractResult<Tensor> {
938 ensure!(from < self.rank());
939 ensure!(to < self.rank());
940 let mut new_shape: TVec<usize> = self.shape.clone();
941 let moved = new_shape.remove(from);
942 new_shape.insert(to, moved);
943 if !self.can_move_axis_blocks() {
944 let mut permutation: Vec<usize> = (0..self.rank()).collect();
945 permutation.remove(from);
946 permutation.insert(to, from);
947 return self.clone().permute_axes(&permutation);
948 }
949 let shape = &*self.shape;
950 let (outer, p, q, inner) = if from < to {
951 (
952 shape[..from].iter().product(),
953 shape[from],
954 shape[from + 1..=to].iter().product(),
955 shape[to + 1..].iter().product(),
956 )
957 } else {
958 (
959 shape[..to].iter().product(),
960 shape[to..from].iter().product(),
961 shape[from],
962 shape[from + 1..].iter().product(),
963 )
964 };
965 let mut output = Tensor::zero_dt(self.dt, &new_shape)?;
966 move_axis_blocks(
967 self.as_bytes(),
968 output.as_bytes_mut(),
969 outer,
970 p,
971 q,
972 inner,
973 self.dt.size_of(),
974 );
975 Ok(output)
976 }
977
978 pub fn permute_axes(self, axes: &[usize]) -> TractResult<Tensor> {
979 ensure!(axes.iter().duplicates().next().is_none());
980 ensure!(axes.iter().all(|a| *a < self.rank()));
981 if axes.iter().enumerate().all(|(i, a)| i == *a) {
982 return Ok(self);
983 }
984 if let Some((from, to)) = move_axis_pair(axes)
985 && self.can_move_axis_blocks()
986 {
987 return self.moved_axis(from, to);
988 }
989 unsafe {
990 #[inline]
991 unsafe fn permute<T: Datum>(axes: &[usize], input: Tensor) -> Tensor {
992 unsafe { input.into_array_unchecked::<T>().permuted_axes(axes).into_tensor() }
993 }
994 let dt = self.datum_type();
995 let mut t = dispatch_datum_by_size!(permute(self.datum_type())(axes, self));
996 t.set_datum_type(dt);
997 Ok(t)
998 }
999 }
1000
1001 pub fn move_axis(self, from: usize, to: usize) -> TractResult<Tensor> {
1002 let mut permutation: Vec<usize> = (0..self.rank()).collect();
1003 permutation.remove(from);
1004 permutation.insert(to, from);
1005 self.permute_axes(&permutation)
1006 }
1007
1008 pub fn collapse_axis_with_next(mut self, axis: usize) -> Tensor {
1009 let removed = self.shape.remove(axis + 1);
1010 self.shape[axis] *= removed;
1011 self.update_strides_and_len();
1012 self
1013 }
1014
1015 pub fn split_axis(mut self, axis: usize, outer_dim: usize) -> TractResult<Tensor> {
1016 if !self.shape[axis].is_multiple_of(outer_dim) {
1017 bail!(
1018 "Invalid axis split, shape is {:?}, axis split at {}, outer {}",
1019 self.shape,
1020 axis,
1021 outer_dim
1022 );
1023 }
1024 self.shape.insert(axis + 1, self.shape[axis] / outer_dim);
1025 self.shape[axis] = outer_dim;
1026 self.update_strides_and_len();
1027 Ok(self)
1028 }
1029
1030 pub fn into_shape(mut self, shape: &[usize]) -> TractResult<Tensor> {
1032 self.set_shape(shape)?;
1033 Ok(self)
1034 }
1035
1036 pub fn insert_axis(&mut self, axis: usize) -> TractResult<()> {
1037 self.shape.insert(axis, 1);
1038 self.strides.insert(axis, self.strides.get(axis).copied().unwrap_or(1));
1039 Ok(())
1040 }
1041
1042 pub fn remove_axis(&mut self, axis: usize) -> TractResult<()> {
1043 ensure!(self.shape[axis] == 1, "Remove a non-1 axis: axis {} in {:?}", axis, self);
1044 self.shape.remove(axis);
1045 self.strides.remove(axis);
1046 Ok(())
1047 }
1048
1049 pub fn broadcast_into_rank(mut self, rank: usize) -> TractResult<Tensor> {
1050 self.broadcast_to_rank(rank)?;
1051 self.update_strides_and_len();
1052 Ok(self)
1053 }
1054
1055 pub fn broadcast_to_rank(&mut self, rank: usize) -> TractResult<()> {
1056 if rank < self.rank() {
1057 bail!("Can only broadcast to higher rank")
1058 }
1059 while self.shape.len() < rank {
1060 self.shape.insert(0, 1)
1061 }
1062 self.update_strides_and_len();
1063 Ok(())
1064 }
1065
1066 pub fn broadcast_scalar_to_shape(&self, shape: &[usize]) -> TractResult<Tensor> {
1067 if self.rank() > 0 {
1068 bail!("broadcast_scalar_to_shape called on {:?}, which is not a salar", self);
1069 }
1070 unsafe fn make<T: Datum>(src: &Tensor, dst: &mut Tensor) {
1071 unsafe {
1072 let value: &T = src.to_scalar_unchecked::<T>();
1073 dst.as_slice_mut_unchecked::<T>().iter_mut().for_each(|item| *item = value.clone())
1074 };
1075 }
1076 unsafe {
1077 let mut t = Tensor::uninitialized_dt(self.datum_type(), shape)?;
1078 dispatch_datum_by_size!(make(self.datum_type())(self, &mut t));
1079 Ok(t)
1080 }
1081 }
1082
1083 fn broadcast_to_shape_t<T: Datum>(&self, shape: &[usize]) -> TractResult<Tensor> {
1084 unsafe {
1085 let view = self.to_array_view_unchecked::<T>();
1086 let mut output = view
1087 .broadcast(shape)
1088 .with_context(|| format!("Broadcasting {view:?} to {shape:?}"))?
1089 .into_owned()
1090 .into_tensor();
1091 output.set_datum_type(self.datum_type());
1092 Ok(output)
1093 }
1094 }
1095
1096 pub fn broadcast_to_shape(&self, shape: &[usize]) -> TractResult<Tensor> {
1097 if !self.dt.is_copy() {
1098 return dispatch_datum!(Self::broadcast_to_shape_t(self.dt)(self, shape));
1099 }
1100 ensure!(
1101 self.rank() <= shape.len(),
1102 "Broadcasting {self:?} to {shape:?} would lose {} axes",
1103 self.rank() - shape.len()
1104 );
1105 let offset = shape.len() - self.rank();
1106 let mut src: TVec<usize> = tvec!(1; shape.len());
1107 src[offset..].copy_from_slice(self.shape());
1108 ensure!(
1109 izip!(&src, shape).all(|(s, d)| *s == 1 || s == d),
1110 "Broadcasting {self:?} to {shape:?}"
1111 );
1112 let mut split = shape.len();
1116 while split > 0 && src[split - 1] == shape[split - 1] {
1117 split -= 1;
1118 }
1119 let dt_size = self.dt.size_of();
1120 let run = shape[split..].iter().product::<usize>() * dt_size;
1121 let outer: usize = shape[..split].iter().product();
1122 let mut src_strides: TVec<usize> = tvec!(0; split);
1123 let mut acc = run;
1124 for ax in (0..split).rev() {
1125 src_strides[ax] = if src[ax] == 1 { 0 } else { acc };
1126 acc *= src[ax];
1127 }
1128 let mut output = unsafe { Tensor::uninitialized_dt(self.dt, shape)? };
1129 if run == 0 || outer == 0 {
1130 return Ok(output);
1131 }
1132 let source = self.as_bytes();
1133 let dst = output.as_bytes_mut();
1134 if run == dt_size {
1141 let mut bs = shape.len();
1142 while bs > 0 && src[bs - 1] == 1 {
1143 bs -= 1;
1144 }
1145 let cell: usize = shape[bs..].iter().product();
1146 let cells: usize = shape[..bs].iter().product();
1147 if cell > 0 && cells > 0 {
1148 let mut src_strides: TVec<usize> = tvec!(0; bs);
1149 let mut acc = 1;
1150 for ax in (0..bs).rev() {
1151 src_strides[ax] = if src[ax] == 1 { 0 } else { acc };
1152 acc *= src[ax];
1153 }
1154 let cell_bytes = cell * dt_size;
1155 let mut coords: TVec<usize> = tvec!(0; bs);
1156 for cell_ix in 0..cells {
1157 let from: usize =
1158 izip!(&coords, &src_strides).map(|(c, s)| c * s).sum::<usize>() * dt_size;
1159 let out = &mut dst[cell_ix * cell_bytes..][..cell_bytes];
1160 out[..dt_size].copy_from_slice(&source[from..][..dt_size]);
1161 let mut filled = dt_size;
1162 while filled < cell_bytes {
1163 let n = filled.min(cell_bytes - filled);
1164 out.copy_within(..n, filled);
1165 filled += n;
1166 }
1167 for ax in (0..bs).rev() {
1168 coords[ax] += 1;
1169 if coords[ax] < shape[ax] {
1170 break;
1171 }
1172 coords[ax] = 0;
1173 }
1174 }
1175 return Ok(output);
1176 }
1177 }
1178 let mut coords: TVec<usize> = tvec!(0; split);
1179 for block in 0..outer {
1180 let from: usize = izip!(&coords, &src_strides).map(|(c, s)| c * s).sum();
1181 dst[block * run..][..run].copy_from_slice(&source[from..][..run]);
1182 for ax in (0..split).rev() {
1183 coords[ax] += 1;
1184 if coords[ax] < shape[ax] {
1185 break;
1186 }
1187 coords[ax] = 0;
1188 }
1189 }
1190 Ok(output)
1191 }
1192
1193 pub fn broadcast_vector_to_shape(&self, shape: &[usize], axis: usize) -> TractResult<Tensor> {
1194 ensure!(self.rank() == 1);
1195 ensure!(shape[axis] == self.len());
1196 if !self.datum_type().is_copy() {
1197 let mut vec_shape = vec![1; shape.len()];
1198 vec_shape[axis] = self.len();
1199 return self.clone().into_shape(&vec_shape)?.broadcast_to_shape(shape);
1200 }
1201 unsafe {
1202 let mut output = Tensor::uninitialized_dt(self.datum_type(), shape)?;
1203 if output.len() == 0 {
1204 return Ok(output);
1205 }
1206 let inner_len = shape[axis + 1..].iter().product::<usize>();
1207
1208 unsafe fn splat<T>(input: &Tensor, output: &mut Tensor, inner_len: usize)
1209 where
1210 T: Datum + Copy,
1211 {
1212 unsafe {
1213 for ix in 0..input.len() {
1214 let value: T = input.as_slice_unchecked()[ix];
1215 output.as_slice_mut_unchecked::<T>()[ix * inner_len..(ix + 1) * inner_len]
1216 .iter_mut()
1217 .for_each(|item| *item = value);
1218 }
1219 }
1220 }
1221 dispatch_copy_by_size!(splat(self.datum_type())(&self, &mut output, inner_len));
1222
1223 let outer_len = shape[0..axis].iter().product::<usize>();
1224 let repeat_bytes_len = inner_len * self.as_bytes().len();
1225 let bytes = output.as_bytes_mut();
1226 for ix in 1..outer_len {
1227 bytes.copy_within(0..repeat_bytes_len, ix * repeat_bytes_len);
1228 }
1229
1230 Ok(output)
1231 }
1232 }
1233 pub fn assign_slice(
1234 &mut self,
1235 range: impl std::ops::RangeBounds<usize>,
1236 src: &Tensor,
1237 src_range: impl std::ops::RangeBounds<usize>,
1238 axis: usize,
1239 ) -> TractResult<()> {
1240 self.assign_slice_at_prefix(&[], range, src, &[], src_range, axis)
1241 }
1242
1243 pub fn assign_slice_at_prefix(
1249 &mut self,
1250 prefix: &[usize],
1251 range: impl std::ops::RangeBounds<usize>,
1252 src: &Tensor,
1253 src_prefix: &[usize],
1254 src_range: impl std::ops::RangeBounds<usize>,
1255 axis: usize,
1256 ) -> TractResult<()> {
1257 ensure!(self.rank() == src.rank());
1258 ensure!(axis < self.rank());
1259 let range = clip_range_bounds(self.shape[axis], range);
1260 let src_range = clip_range_bounds(src.shape[axis], src_range);
1261 ensure!(
1262 src.datum_type() == self.datum_type(),
1263 "Attempt to assign into {:?} from {:?}, datum type mismatch",
1264 self.datum_type(),
1265 src.datum_type()
1266 );
1267 ensure!(
1268 src_range.len() == range.len(),
1269 "Attempt to assign a range of {:?} from a range of {:?}",
1270 range,
1271 src_range,
1272 );
1273 ensure!(
1274 prefix.len() == src_prefix.len() && prefix.len() <= axis,
1275 "Attempt to assign axis {axis} at prefixes {prefix:?} and {src_prefix:?}"
1276 );
1277 ensure!(
1278 izip!(prefix, self.shape()).all(|(ix, dim)| ix < dim)
1279 && izip!(src_prefix, src.shape()).all(|(ix, dim)| ix < dim),
1280 "Attempt to assign into {self:?} at {prefix:?} from {src:?} at {src_prefix:?}"
1281 );
1282 ensure!(
1283 izip!(prefix.len().., &self.shape[prefix.len()..], &src.shape[prefix.len()..])
1284 .all(|(ix, dst, src)| ix == axis || src == dst),
1285 "Attempt to assign a {}-axis range of {:?} from a range of {:?}",
1286 axis,
1287 self,
1288 src
1289 );
1290 ensure!(
1291 src_range.end <= src.shape()[axis],
1292 "Assigning from invalid slice (axis {}, {:?}) of {:?}",
1293 axis,
1294 src_range,
1295 src
1296 );
1297 ensure!(
1298 range.end <= self.shape()[axis],
1299 "Assigning to invalid slice (axis {}, {:?}) of {:?}",
1300 axis,
1301 range,
1302 self
1303 );
1304 unsafe { self.assign_slice_from_resolved(prefix, range, src, src_prefix, src_range, axis) };
1305 Ok(())
1306 }
1307
1308 pub unsafe fn assign_slice_unchecked(
1309 &mut self,
1310 range: impl std::ops::RangeBounds<usize>,
1311 src: &Tensor,
1312 src_range: impl std::ops::RangeBounds<usize>,
1313 axis: usize,
1314 ) {
1315 let range = clip_range_bounds(self.shape[axis], range);
1316 let src_range = clip_range_bounds(src.shape[axis], src_range);
1317 unsafe { self.assign_slice_from_resolved(&[], range, src, &[], src_range, axis) };
1318 }
1319
1320 fn prefix_offset(&self, prefix: &[usize]) -> usize {
1323 izip!(prefix, &self.strides).map(|(ix, stride)| ix * *stride as usize).sum::<usize>()
1324 * self.datum_type().size_of()
1325 }
1326
1327 #[allow(clippy::ptr_eq)]
1328 unsafe fn assign_slice_from_resolved(
1329 &mut self,
1330 prefix: &[usize],
1331 range: std::ops::Range<usize>,
1332 src: &Tensor,
1333 src_prefix: &[usize],
1334 src_range: std::ops::Range<usize>,
1335 axis: usize,
1336 ) {
1337 unsafe {
1338 use ndarray::Slice;
1339 unsafe fn assign_slice_t<T: Datum>(
1340 to: &mut Tensor,
1341 to_prefix: &[usize],
1342 to_range: Range<usize>,
1343 from: &Tensor,
1344 from_prefix: &[usize],
1345 from_range: Range<usize>,
1346 axis: usize,
1347 ) {
1348 unsafe {
1349 let mut to_view = to.to_array_view_mut_unchecked::<T>();
1350 let mut from_view = from.to_array_view_unchecked::<T>();
1351 for (ax, (to, from)) in izip!(to_prefix, from_prefix).enumerate() {
1352 to_view.slice_axis_inplace(Axis(ax), Slice::from(*to..*to + 1));
1353 from_view.slice_axis_inplace(Axis(ax), Slice::from(*from..*from + 1));
1354 }
1355 to_view
1356 .slice_axis_mut(Axis(axis), Slice::from(to_range))
1357 .assign(&from_view.slice_axis(Axis(axis), Slice::from(from_range)))
1358 }
1359 }
1360 if self.datum_type().is_copy() {
1361 let post = self.strides[axis] as usize * self.datum_type().size_of();
1366 let len = post * range.len();
1367 if len > 0 {
1368 let outer: usize = self.shape[prefix.len()..axis].iter().product();
1369 let dst_block = post * self.shape[axis];
1370 let src_block = post * src.shape[axis];
1371 let src_ptr = src
1372 .plain_ram_storage()
1373 .as_ptr()
1374 .add(src.prefix_offset(src_prefix) + post * src_range.start);
1375 let aliasing =
1376 self.plain_ram_storage().as_ptr() == src.plain_ram_storage().as_ptr();
1377 let dst_offset = self.prefix_offset(prefix) + post * range.start;
1378 let dst_ptr = self.plain_ram_storage_mut().as_mut_ptr().add(dst_offset);
1379 for run in 0..outer {
1380 let from = src_ptr.add(run * src_block);
1381 let to = dst_ptr.add(run * dst_block);
1382 if aliasing {
1383 std::ptr::copy(from, to, len);
1384 } else {
1385 std::ptr::copy_nonoverlapping(from, to, len);
1386 }
1387 }
1388 }
1389 } else {
1390 dispatch_datum!(assign_slice_t(self.datum_type())(
1391 self, prefix, range, src, src_prefix, src_range, axis
1392 ));
1393 }
1394 }
1395 }
1396 pub fn fill_slice(
1399 &mut self,
1400 range: impl std::ops::RangeBounds<usize>,
1401 value: &Tensor,
1402 axis: usize,
1403 ) -> TractResult<()> {
1404 self.fill_slice_at_prefix(&[], range, value, axis)
1405 }
1406
1407 pub fn fill_slice_at_prefix(
1412 &mut self,
1413 prefix: &[usize],
1414 range: impl std::ops::RangeBounds<usize>,
1415 value: &Tensor,
1416 axis: usize,
1417 ) -> TractResult<()> {
1418 ensure!(axis < self.rank(), "Filling axis {axis} of {self:?}");
1419 ensure!(
1420 prefix.len() <= axis,
1421 "Filling axis {axis} of {self:?} at prefix {prefix:?}, which reaches it"
1422 );
1423 ensure!(
1424 izip!(prefix, self.shape()).all(|(ix, dim)| ix < dim),
1425 "Filling {self:?} at prefix {prefix:?}"
1426 );
1427 ensure!(
1428 value.datum_type() == self.datum_type() && value.len() == 1,
1429 "Filling {:?} with {value:?}",
1430 self.datum_type()
1431 );
1432 let range = clip_range_bounds(self.shape[axis], range);
1433 ensure!(
1434 range.end <= self.shape[axis],
1435 "Filling invalid slice (axis {axis}, {range:?}) of {self:?}"
1436 );
1437 if !self.datum_type().is_copy() {
1438 return dispatch_datum!(Self::fill_slice_t(self.datum_type())(
1439 self, prefix, range, value, axis
1440 ));
1441 }
1442 let dt_size = self.datum_type().size_of();
1446 let post = self.strides[axis] as usize * dt_size;
1447 let len = post * range.len();
1448 if len == 0 {
1449 return Ok(());
1450 }
1451 let block = post * self.shape[axis];
1452 let runs: usize = self.shape[prefix.len()..axis].iter().product();
1453 let start = self.prefix_offset(prefix) + range.start * post;
1454 let value = &value.as_bytes()[..dt_size];
1455 let data = self.as_bytes_mut();
1456 for run in 0..runs {
1457 let run = &mut data[start + run * block..start + run * block + len];
1458 run[..dt_size].copy_from_slice(value);
1459 let mut written = dt_size;
1460 while written < len {
1461 let grow = written.min(len - written);
1462 run.copy_within(0..grow, written);
1463 written += grow;
1464 }
1465 }
1466 Ok(())
1467 }
1468
1469 fn fill_slice_t<T: Datum>(
1470 &mut self,
1471 prefix: &[usize],
1472 range: Range<usize>,
1473 value: &Tensor,
1474 axis: usize,
1475 ) -> TractResult<()> {
1476 let value = value.try_as_plain_ram()?.to_scalar::<T>()?.clone();
1477 let mut view = self.to_plain_array_view_mut::<T>()?;
1478 for (ax, ix) in prefix.iter().enumerate() {
1479 view.slice_axis_inplace(Axis(ax), (*ix..*ix + 1).into());
1480 }
1481 view.slice_axis_mut(Axis(axis), range.into()).fill(value);
1482 Ok(())
1483 }
1484
1485 #[inline]
1487 pub fn datum_type(&self) -> DatumType {
1488 self.dt
1489 }
1490
1491 #[inline]
1493 pub unsafe fn set_datum_type(&mut self, dt: DatumType) {
1494 self.dt = dt
1495 }
1496
1497 pub fn dump(&self, force_full: bool) -> TractResult<String> {
1501 if self.as_plain_ram_storage().is_none() {
1502 return Ok(format!(
1503 "{},{:?} (non-plain storage)",
1504 self.shape.iter().join(","),
1505 self.dt,
1506 ));
1507 }
1508 unsafe fn dump_t<D: Datum>(tensor: &Tensor, n: usize) -> String {
1509 unsafe {
1510 if let Some(qp) = tensor.datum_type().qparams() {
1511 let integers = tensor.cast_to::<i32>().unwrap();
1512 integers.as_slice_unchecked::<i32>()[0..n]
1513 .iter()
1514 .map(|x| format!("[{}]({})", x, qp.dq(*x)))
1515 .join(", ")
1516 } else {
1517 tensor.as_slice_unchecked::<D>()[0..n].iter().join(", ")
1518 }
1519 }
1520 }
1521 unsafe {
1522 let trunc = self.len() > 12 && !force_full;
1523 let data = dispatch_datum!(dump_t(self.datum_type())(
1524 self,
1525 if trunc { 12 } else { self.len() }
1526 ));
1527 Ok(format!(
1528 "{},{:?} {}{}",
1529 self.shape.iter().join(","),
1530 self.dt,
1531 data,
1532 if trunc { "..." } else { "" }
1533 ))
1534 }
1535 }
1536
1537 pub fn close_enough(
1539 &self,
1540 other: &Self,
1541 approx: impl Into<Approximation> + std::fmt::Debug,
1542 ) -> TractResult<()> {
1543 let approx = approx.into();
1544 if self.shape() != other.shape() {
1545 bail!("Shape mismatch {:?} != {:?}", self.shape(), other.shape())
1546 }
1547 if let Approximation::Ulp(max_ulp) = approx {
1548 return self.ulp_close_enough(other, max_ulp);
1549 }
1550 let (atol, rtol, outliers) = approx.atol_rtol_outliers(&self.datum_type());
1551 let ma = self.cast_to::<f32>()?;
1552 let ma = ma.to_plain_array_view::<f32>()?;
1553 let mb = other.cast_to::<f32>()?;
1554 let mb = mb.to_plain_array_view::<f32>()?;
1555 let mut first_outlier = None;
1556 let mut outliers_count = 0;
1557 ndarray::indices_of(&ma).into_iter().for_each(|indices| {
1558 let a = ma[&indices];
1559 let b = mb[&indices];
1560 if !((a.is_nan() && b.is_nan())
1561 || (a.is_infinite() && b.is_infinite() && a.signum() == b.signum())
1562 || (a - b).abs() <= atol as f32 + rtol as f32 * b.abs())
1563 {
1564 if outliers_count == 0 {
1565 first_outlier = Some(indices.as_array_view().to_vec());
1566 }
1567 outliers_count += 1;
1568 }
1569 });
1570 if self.volume() > 0 && outliers_count as f64 / self.volume() as f64 > outliers {
1571 let indices = first_outlier.unwrap();
1572 let a = ma[&*indices];
1573 let b = mb[&*indices];
1574 let ulp = self
1575 .max_ulp_distance(other)
1576 .map(|(d, _)| format!("{d}"))
1577 .unwrap_or_else(|_| "n/a".to_string());
1578 bail!(
1579 "Mismatch. First outlier: {:?} for {:?}) at {:?} {} != {}. Outliers: {} / {} = {:0.5} > {:0.5}. Max ULP ({:?}): {}.",
1580 approx,
1581 self.datum_type(),
1582 indices,
1583 a,
1584 b,
1585 outliers_count,
1586 self.volume(),
1587 outliers_count as f64 / self.volume() as f64,
1588 outliers,
1589 self.ulp_comparison_dt(),
1590 ulp,
1591 );
1592 }
1593 Ok(())
1594 }
1595
1596 pub fn ulp_comparison_dt(&self) -> DatumType {
1602 match self.datum_type() {
1603 dt @ (DatumType::F16 | DatumType::F32 | DatumType::F64) => dt,
1604 _ => DatumType::F32,
1605 }
1606 }
1607
1608 pub fn max_ulp_distance(&self, other: &Self) -> TractResult<(u64, Option<usize>)> {
1614 if self.shape() != other.shape() {
1615 bail!("Shape mismatch {:?} != {:?}", self.shape(), other.shape())
1616 }
1617 let dt = self.ulp_comparison_dt();
1618 let a = self.cast_to_dt(dt)?;
1619 let b = other.cast_to_dt(dt)?;
1620 fn worst<D: Datum + crate::ulp::UlpFloat>(
1621 a: &Tensor,
1622 b: &Tensor,
1623 ) -> TractResult<(u64, Option<usize>)> {
1624 let a = a.to_plain_array_view::<D>()?;
1625 let b = b.to_plain_array_view::<D>()?;
1626 Ok(crate::ulp::max_ulp_distance(a.iter().copied(), b.iter().copied()))
1627 }
1628 match dt {
1629 DatumType::F16 => worst::<f16>(&a, &b),
1630 DatumType::F32 => worst::<f32>(&a, &b),
1631 DatumType::F64 => worst::<f64>(&a, &b),
1632 dt => bail!("No ULP comparison for {dt:?}"),
1633 }
1634 }
1635
1636 fn ulp_close_enough(&self, other: &Self, max_ulp: u64) -> TractResult<()> {
1639 let (worst, at) = self.max_ulp_distance(other)?;
1640 if worst <= max_ulp {
1641 return Ok(());
1642 }
1643 let dt = self.ulp_comparison_dt();
1644 let indices = at
1645 .map(|flat| {
1646 let mut rest = flat;
1647 let mut indices = vec![0; self.rank()];
1648 for (ix, dim) in self.shape().iter().enumerate().rev() {
1649 indices[ix] = rest % dim;
1650 rest /= dim;
1651 }
1652 format!("{indices:?}")
1653 })
1654 .unwrap_or_else(|| "?".to_string());
1655 let a = self.cast_to::<f64>()?;
1656 let b = other.cast_to::<f64>()?;
1657 let (a, b) = (a.to_plain_array_view::<f64>()?, b.to_plain_array_view::<f64>()?);
1658 let flat = at.unwrap_or(0);
1659 bail!(
1660 "Mismatch. Max ULP distance ({dt:?}): {} > {}, at {} ({} != {}).",
1661 worst,
1662 max_ulp,
1663 indices,
1664 a.iter().nth(flat).copied().unwrap_or(f64::NAN),
1665 b.iter().nth(flat).copied().unwrap_or(f64::NAN),
1666 );
1667 }
1668
1669 pub fn into_plain_array<D: Datum>(self) -> TractResult<ArrayD<D>> {
1671 Ok(self.to_plain_array_view::<D>()?.to_owned())
1672 }
1673
1674 pub unsafe fn into_array_unchecked<D: Datum>(self) -> ArrayD<D> {
1676 unsafe { self.to_array_view_unchecked::<D>().to_owned() }
1677 }
1678
1679 #[inline]
1683 pub fn to_plain_array_view<D: Datum>(&self) -> TractResult<ArrayViewD<'_, D>> {
1684 self.try_as_plain_ram()?.to_array_view::<D>()
1685 }
1686
1687 #[inline]
1691 pub fn to_plain_array_view_mut<D: Datum>(&mut self) -> TractResult<ArrayViewMutD<'_, D>> {
1692 self.check_for_access::<D>()?;
1693 ensure!(self.storage.as_plain_ram_mut().is_some(), "Tensor storage is not plain");
1694 unsafe { Ok(self.to_array_view_mut_unchecked()) }
1695 }
1696
1697 fn check_for_access<D: Datum>(&self) -> TractResult<()> {
1698 ensure!(
1699 self.datum_type().unquantized() == D::datum_type().unquantized(),
1700 "Tensor datum type error: tensor is {:?}, accessed as {:?}",
1701 self.datum_type(),
1702 D::datum_type(),
1703 );
1704 Ok(())
1705 }
1706
1707 pub unsafe fn to_array_view_unchecked<D: Datum>(&self) -> ArrayViewD<'_, D> {
1709 if self.len() != 0 {
1710 unsafe {
1711 ArrayViewD::from_shape_ptr(
1712 &*self.shape,
1713 self.plain_ram_storage().as_ptr() as *const D,
1714 )
1715 }
1716 } else {
1717 ArrayViewD::from_shape(&*self.shape, &[]).unwrap()
1718 }
1719 }
1720
1721 pub unsafe fn to_array_view_mut_unchecked<D: Datum>(&mut self) -> ArrayViewMutD<'_, D> {
1723 if self.len() != 0 {
1724 unsafe {
1725 let ptr = self.plain_ram_storage_mut().as_mut_ptr() as *mut D;
1726 ArrayViewMutD::from_shape_ptr(&*self.shape, ptr)
1727 }
1728 } else {
1729 ArrayViewMutD::from_shape(&*self.shape, &mut []).unwrap()
1730 }
1731 }
1732
1733 pub fn as_ptr<D: Datum>(&self) -> TractResult<*const D> {
1735 self.check_for_access::<D>()?;
1736 Ok(self.plain_ram_storage().as_ptr() as *const D)
1737 }
1738
1739 pub unsafe fn as_ptr_unchecked<D: Datum>(&self) -> *const D {
1741 self.plain_ram_storage().as_ptr() as *const D
1742 }
1743
1744 pub unsafe fn as_ptr_mut_unchecked<D: Datum>(&mut self) -> *mut D {
1746 self.plain_ram_storage_mut().as_mut_ptr() as *mut D
1747 }
1748
1749 pub fn as_ptr_mut<D: Datum>(&mut self) -> TractResult<*mut D> {
1751 self.as_ptr::<D>().map(|p| p as *mut D)
1752 }
1753
1754 pub unsafe fn as_slice_unchecked<D: Datum>(&self) -> &[D] {
1756 if self.storage.byte_len() == 0 {
1757 &[]
1758 } else {
1759 unsafe { std::slice::from_raw_parts::<D>(self.as_ptr_unchecked(), self.len()) }
1760 }
1761 }
1762
1763 pub unsafe fn as_slice_mut_unchecked<D: Datum>(&mut self) -> &mut [D] {
1765 if self.storage.byte_len() == 0 {
1766 &mut []
1767 } else {
1768 unsafe { std::slice::from_raw_parts_mut::<D>(self.as_ptr_mut_unchecked(), self.len()) }
1769 }
1770 }
1771
1772 pub fn to_scalar_tensor(&self) -> TractResult<Tensor> {
1774 fn to_scalar_tensor_t<D: Datum>(t: &Tensor) -> TractResult<Tensor> {
1775 Ok(litteral::tensor0(t.try_as_plain_ram()?.to_scalar::<D>()?.clone()))
1776 }
1777 dispatch_datum!(to_scalar_tensor_t(self.datum_type())(self))
1778 }
1779
1780 pub unsafe fn to_scalar_unchecked<D: Datum>(&self) -> &D {
1782 unsafe { &*(self.plain_ram_storage().as_ptr() as *const D) }
1783 }
1784
1785 pub fn to_scalar_mut<D: Datum>(&mut self) -> TractResult<&mut D> {
1787 self.check_for_access::<D>()?;
1788 if self.len() == 0 {
1789 bail!("to_scalar_mut called on empty tensor ({:?})", self)
1790 }
1791 if self.len() > 1 {
1792 bail!("to_scalar called on a tensor with multiple values ({:?})", self)
1793 }
1794 unsafe { Ok(self.to_scalar_mut_unchecked()) }
1795 }
1796
1797 pub unsafe fn to_scalar_mut_unchecked<D: Datum>(&mut self) -> &mut D {
1799 unsafe { &mut *(self.plain_ram_storage_mut().as_mut_ptr() as *mut D) }
1800 }
1801
1802 pub fn as_bytes(&self) -> &[u8] {
1803 self.plain_ram_storage().as_bytes()
1804 }
1805
1806 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
1807 self.plain_ram_storage_mut().as_bytes_mut()
1808 }
1809
1810 unsafe fn is_uniform_t<T: Datum>(&self) -> bool {
1811 let slice = unsafe { self.as_slice_unchecked::<T>() };
1812 slice[1..].iter().all(|x| x == &slice[0])
1813 }
1814
1815 pub fn is_uniform(&self) -> bool {
1816 if self.as_plain_ram_storage().is_none() {
1817 return false;
1818 }
1819 if self.len() <= 1 {
1820 return true;
1821 }
1822 unsafe { dispatch_datum!(Tensor::is_uniform_t(self.datum_type())(self)) }
1823 }
1824
1825 unsafe fn as_uniform_t<T: Datum>(&self) -> Tensor {
1826 let v: T = unsafe { self.as_slice_unchecked::<T>() }[0].clone();
1827 litteral::tensor0(v)
1828 }
1829
1830 pub fn as_uniform(&self) -> Option<Tensor> {
1831 if self.len() >= 1 && self.is_uniform() {
1832 unsafe {
1833 let mut t = dispatch_datum!(Tensor::as_uniform_t(self.datum_type())(self));
1834 t.set_datum_type(self.datum_type());
1835 Some(t)
1836 }
1837 } else {
1838 None
1839 }
1840 }
1841
1842 pub fn is_all_zero(&self) -> TractResult<bool> {
1843 Ok(self.len() == 0 || self.as_uniform().map(|t| t.is_zero().unwrap()).unwrap_or(false))
1844 }
1845
1846 pub fn is_zero(&self) -> TractResult<bool> {
1847 Ok(self == &Tensor::zero_scalar_dt(self.dt)?)
1848 }
1849
1850 unsafe fn natural_cast<
1851 Source: Datum + num_traits::AsPrimitive<Target>,
1852 Target: Datum + Copy,
1853 >(
1854 &self,
1855 other: &mut Tensor,
1856 ) {
1857 unsafe {
1858 self.as_slice_unchecked::<Source>()
1859 .iter()
1860 .zip(other.as_slice_mut_unchecked::<Target>().iter_mut())
1861 .for_each(|(s, d)| *d = s.as_())
1862 };
1863 }
1864
1865 unsafe fn cast_number_to_bool<Source: Datum + num_traits::Zero>(&self, other: &mut Tensor) {
1866 unsafe {
1867 self.as_slice_unchecked::<Source>()
1868 .iter()
1869 .zip(other.as_slice_mut_unchecked::<bool>().iter_mut())
1870 .for_each(|(s, d)| *d = !s.is_zero());
1871 }
1872 }
1873
1874 unsafe fn cast_from_string<Target: Datum + core::str::FromStr>(
1875 &self,
1876 other: &mut Tensor,
1877 ) -> TractResult<()> {
1878 unsafe {
1879 for (s, d) in self
1880 .as_slice_unchecked::<String>()
1881 .iter()
1882 .zip(other.as_slice_mut_unchecked::<Target>().iter_mut())
1883 {
1884 *d = s
1885 .parse()
1886 .map_err(|_| format_err!("Can not parse as {:?}", Target::datum_type()))?;
1887 }
1888 Ok(())
1889 }
1890 }
1891
1892 unsafe fn cast_to_string<Source: Datum>(&self, other: &mut Tensor) {
1893 unsafe {
1894 for (s, d) in self
1895 .as_slice_unchecked::<Source>()
1896 .iter()
1897 .zip(other.as_slice_mut_unchecked::<String>().iter_mut())
1898 {
1899 *d = s.to_string()
1900 }
1901 }
1902 }
1903
1904 pub fn cast_to<D: Datum>(&self) -> TractResult<Cow<'_, Tensor>> {
1906 self.cast_to_dt(D::datum_type())
1907 }
1908
1909 #[allow(clippy::redundant_closure_call)]
1911 pub fn cast_to_dt(&self, dst_dt: DatumType) -> TractResult<Cow<'_, Tensor>> {
1912 unsafe {
1913 if self.dt == dst_dt {
1914 return Ok(Cow::Borrowed(self));
1915 }
1916 if self.dt == TDim::datum_type() && (dst_dt.is_integer() || dst_dt.is_float()) {
1917 let slice = self.as_slice_unchecked::<TDim>();
1918 let mut ints = Self::uninitialized::<i64>(&self.shape)?;
1919 let ints_slice = ints.as_slice_mut_unchecked::<i64>();
1920 for i in 0..self.len() {
1921 ints_slice[i] = slice[i].to_i64()?;
1922 }
1923 return Ok(Cow::Owned(ints.cast_to_dt(dst_dt)?.into_owned()));
1924 }
1925 if self.dt == bool::datum_type()
1926 && (dst_dt.is_integer() || dst_dt.is_float() || dst_dt == TDim::datum_type())
1927 {
1928 let slice = self.as_slice_unchecked::<bool>();
1929 let mut ints = Self::uninitialized::<i8>(&self.shape)?;
1930 let ints_slice = ints.as_slice_mut_unchecked::<i8>();
1931 for i in 0..self.len() {
1932 ints_slice[i] = slice[i] as usize as i8;
1933 }
1934 return Ok(Cow::Owned(ints.cast_to_dt(dst_dt)?.into_owned()));
1935 }
1936 let mut result = Self::uninitialized_dt(dst_dt, &self.shape)?;
1937 if self.dt == DatumType::String {
1938 dispatch_numbers!(Self::cast_from_string(dst_dt)(self, &mut result))?;
1939 return Ok(Cow::Owned(result));
1940 }
1941 if dst_dt == DatumType::String {
1942 dispatch_datum!(Self::cast_to_string(self.dt)(self, &mut result));
1943 return Ok(Cow::Owned(result));
1944 }
1945 macro_rules! n {
1946 ($source:ty) => {
1947 if <$source>::datum_type() == self.datum_type() {
1948 match dst_dt {
1949 DatumType::I8 => self.natural_cast::<$source, i8>(&mut result),
1950 DatumType::I16 => self.natural_cast::<$source, i16>(&mut result),
1951 DatumType::I32 => self.natural_cast::<$source, i32>(&mut result),
1952 DatumType::I64 => self.natural_cast::<$source, i64>(&mut result),
1953 DatumType::U8 => self.natural_cast::<$source, u8>(&mut result),
1954 DatumType::U16 => self.natural_cast::<$source, u16>(&mut result),
1955 DatumType::U32 => self.natural_cast::<$source, u32>(&mut result),
1956 DatumType::U64 => self.natural_cast::<$source, u64>(&mut result),
1957 DatumType::F16 => self.natural_cast::<$source, f16>(&mut result),
1958 DatumType::F32 => self.natural_cast::<$source, f32>(&mut result),
1959 DatumType::F64 => self.natural_cast::<$source, f64>(&mut result),
1960 DatumType::TDim => {
1961 let ints = self.cast_to::<i32>()?;
1962 let slice = ints.as_slice_unchecked::<i32>();
1963 let result = result.as_slice_mut_unchecked::<TDim>();
1964 for i in 0..self.len() {
1965 result[i] = slice[i].into();
1966 }
1967 }
1968 DatumType::Bool => self.cast_number_to_bool::<$source>(&mut result),
1969 _ => todo!(),
1970 }
1971 return Ok(Cow::Owned(result));
1972 };
1973 };
1974 }
1975 if !dst_dt.is_quantized() && !self.datum_type().is_quantized() {
1977 n!(u8);
1978 n!(u16);
1979 n!(u32);
1980 n!(u64);
1981 n!(i8);
1982 n!(i16);
1983 n!(i32);
1984 n!(i64);
1985 n!(f16);
1986 n!(f32);
1987 n!(f64);
1988 } else {
1989 let (s_zp, s_scale) = self.datum_type().zp_scale();
1990 let (d_zp, d_scale) = dst_dt.zp_scale();
1991 if self.datum_type().is_quantized() && dst_dt.is_float() {
1992 macro_rules! q_to_fp {
1993 ($source:ty, $dest:ty) => {
1994 if <$source>::datum_type().unquantized()
1995 == self.datum_type().unquantized()
1996 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
1997 {
1998 self.as_slice_unchecked::<$source>()
1999 .iter()
2000 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
2001 .for_each(|(&s, d)| {
2002 *d = (s as $dest - s_zp as $dest) * s_scale as $dest;
2003 });
2004 return Ok(Cow::Owned(result));
2005 }
2006 };
2007 }
2008 q_to_fp!(i8, f64);
2009 q_to_fp!(i8, f32);
2010 q_to_fp!(u8, f64);
2011 q_to_fp!(u8, f32);
2012 }
2013 macro_rules! q8_to_q8 {
2015 ($typ:ty) => {
2016 if dst_dt.unquantized() == <$typ>::datum_type() {
2017 self.as_slice_unchecked::<$typ>()
2018 .iter()
2019 .zip(result.as_slice_mut_unchecked::<$typ>().iter_mut())
2020 .for_each(|(&s, d)| {
2021 *d = (d_zp as i32
2022 + scale_by(s as i32 - s_zp as i32, s_scale / d_scale))
2023 .clamp_cast()
2024 });
2025 return Ok(Cow::Owned(result));
2026 }
2027 };
2028 }
2029
2030 macro_rules! q_via_f32 {
2031 ($source:ty, $dest:ty, $round:expr) => {
2032 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
2033 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
2034 {
2035 self.as_slice_unchecked::<$source>()
2036 .iter()
2037 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
2038 .for_each(|(&s, d)| {
2039 let s_float = (s as f32 - s_zp as f32) * s_scale as f32;
2040 let d_float = s_float as f32 / d_scale as f32 + d_zp as f32;
2041 *d = $round(d_float);
2042 });
2043 return Ok(Cow::Owned(result));
2044 }
2045 };
2046 }
2047
2048 macro_rules! q_n {
2049 (clamp $source:ty, $dest:ty) => {{
2050 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
2051 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
2052 {
2053 self.as_slice_unchecked::<$source>()
2054 .iter()
2055 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
2056 .for_each(|(&s, d)| {
2057 *d = s.clamp_cast();
2058 });
2059 return Ok(Cow::Owned(result));
2060 }
2061 }};
2062 ($source:ty, $dest:ty) => {{
2063 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
2064 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
2065 {
2066 self.as_slice_unchecked::<$source>()
2067 .iter()
2068 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
2069 .for_each(|(&s, d)| {
2070 *d = s as $dest;
2071 });
2072 return Ok(Cow::Owned(result));
2073 }
2074 }};
2075 }
2076
2077 if dst_dt.unquantized() == self.datum_type().unquantized()
2078 && dst_dt.is_quantized()
2079 && self.datum_type().is_quantized()
2080 {
2081 q8_to_q8!(i8);
2082 q8_to_q8!(u8);
2083 }
2084
2085 q_via_f32!(f32, i8, |f| round_ties_to_even(f).clamp_cast());
2086 q_via_f32!(f32, u8, |f| round_ties_to_even(f).clamp_cast());
2087 q_via_f32!(f32, i32, |f| round_ties_to_even(f).clamp_cast());
2088 q_via_f32!(i8, f32, |f| f);
2089 q_via_f32!(u8, f32, |f| f);
2090 q_via_f32!(i32, f32, |f| f);
2091
2092 if dst_dt.is_quantized() && self.datum_type().is_quantized() {
2093 q_via_f32!(u8, i8, |f| round_ties_to_even(f).clamp_cast());
2094 q_via_f32!(i8, u8, |f| round_ties_to_even(f).clamp_cast());
2095 q_via_f32!(i32, u8, |f| round_ties_to_even(f).clamp_cast());
2096 q_via_f32!(i32, i8, |f| round_ties_to_even(f).clamp_cast());
2097 q_via_f32!(u8, i32, |f| round_ties_to_even(f).clamp_cast());
2098 q_via_f32!(i8, i32, |f| round_ties_to_even(f).clamp_cast());
2099
2100 q_via_f32!(i8, i8, |f| round_ties_to_even(f).clamp_cast());
2102 q_via_f32!(u8, u8, |f| round_ties_to_even(f).clamp_cast());
2103 }
2104
2105 q_n!(i8, i32);
2106 q_n!(i8, u32);
2107 q_n!(u8, i32);
2108 q_n!(u8, u32);
2109 q_n!(clamp i32, i8);
2110 q_n!(clamp i32, u8);
2111 q_n!(clamp u32, i8);
2112 q_n!(clamp u32, u8);
2113 q_n!(i8, i8);
2114 q_n!(u8, u8);
2115 q_n!(i32, i32);
2116 q_n!(u32, u32);
2117 }
2118
2119 bail!("Unsupported cast from {:?} to {:?}", self.dt, dst_dt)
2120 }
2121 }
2122
2123 pub fn cast_to_scalar<D: Datum + Copy>(&self) -> TractResult<D> {
2125 let casted = self.cast_to::<D>()?;
2126 casted.try_as_plain_ram()?.to_scalar::<D>().copied()
2127 }
2128
2129 pub fn nth(&self, nth: usize) -> TractResult<Tensor> {
2131 if nth >= self.len() {
2132 bail!(
2133 "nth called with {}th element on a tensor of len {} ({:?}",
2134 nth,
2135 self.len(),
2136 self
2137 );
2138 }
2139 unsafe fn nth_t<T: Datum>(me: &Tensor, nth: usize, output: &mut Tensor) {
2140 unsafe {
2141 let value = me.as_slice_unchecked::<T>()[nth].clone();
2142 std::ptr::write(output.as_slice_mut_unchecked::<T>().as_mut_ptr(), value);
2143 }
2144 }
2145 unsafe {
2146 let mut output = Tensor::uninitialized_dt(self.datum_type(), &[])?;
2147 dispatch_datum_by_size!(nth_t(self.datum_type())(self, nth, &mut output));
2148 Ok(output)
2149 }
2150 }
2151
2152 fn eq_dt(&self, other: &Tensor) -> TractResult<bool> {
2154 unsafe fn eq_t<D: Datum>(me: &Tensor, other: &Tensor) -> TractResult<bool> {
2155 unsafe {
2156 if D::datum_type().is_float() {
2157 return dispatch_floatlike!(float_eq_t(D::datum_type())(me, other));
2158 }
2159 Ok(izip!(me.as_slice_unchecked::<D>(), other.as_slice_unchecked::<D>())
2160 .all(|(a, b)| a == b))
2161 }
2162 }
2163
2164 unsafe fn float_eq_t<D: Datum + Float>(me: &Tensor, other: &Tensor) -> TractResult<bool> {
2165 unsafe {
2166 Ok(izip!(me.as_slice_unchecked::<D>(), other.as_slice_unchecked::<D>())
2167 .all(|(a, b)| (a.is_nan() && b.is_nan()) || a == b))
2168 }
2169 }
2170
2171 unsafe {
2172 Ok(self.datum_type() == other.datum_type()
2173 && self.shape() == other.shape()
2174 && dispatch_datum!(eq_t(self.dt)(self, other))?)
2175 }
2176 }
2177
2178 fn from_datum<T: Datum>(mut it: ArrayD<T>) -> Tensor {
2179 unsafe {
2180 let mut t = Self::uninitialized::<T>(it.shape()).unwrap();
2181 if let Some(slice) = it.as_slice_mut() {
2182 if t.datum_type().is_copy() {
2183 std::ptr::copy_nonoverlapping(
2184 slice.as_ptr() as *const i8,
2185 t.as_ptr_mut_unchecked(),
2186 t.plain_ram_storage().layout().size(),
2187 );
2188 } else {
2189 t.as_slice_mut_unchecked::<T>()
2190 .iter_mut()
2191 .zip(slice.iter_mut())
2192 .for_each(|(t, s)| *t = std::mem::take(s));
2193 }
2194 return t;
2195 }
2196 if it.strides().iter().all(|&s| s > 0) && it.as_slice_memory_order().is_some() {
2197 let mut len_and_strides: TVec<(usize, usize)> = tvec!();
2198 for (len, stride) in itertools::izip!(it.shape(), it.strides(), t.strides())
2199 .sorted_by_key(|(_, src, _)| *src)
2200 .map(|(l, _, dst)| (*l as isize, *dst))
2201 {
2202 if !len_and_strides.is_empty()
2203 && len_and_strides.last().unwrap().1 * len_and_strides.last().unwrap().0
2204 == stride as usize
2205 {
2206 len_and_strides.last_mut().unwrap().0 *= len as usize;
2207 } else {
2208 len_and_strides.push((len as usize, stride as usize));
2209 }
2210 }
2211 len_and_strides.reverse();
2212 crate::scatter::scatter_contig_data(
2213 it.as_ptr(),
2214 t.as_ptr_mut_unchecked(),
2215 &len_and_strides,
2216 );
2217 return t;
2218 }
2219 t.as_slice_mut_unchecked().iter_mut().zip(it).for_each(|(t, a)| *t = a);
2221 t
2222 }
2223 }
2224
2225 pub fn deep_clone(&self) -> Tensor {
2226 if self.as_plain_ram_storage().is_none() {
2227 return Tensor {
2228 dt: self.dt,
2229 shape: self.shape.clone(),
2230 strides: self.strides.clone(),
2231 len: self.len,
2232 storage: self.storage.deep_clone(),
2233 };
2234 }
2235 unsafe {
2236 let mut tensor = Tensor::uninitialized_dt(self.datum_type(), self.shape()).unwrap();
2237 if self.len() > 0 {
2238 if self.dt.is_copy() {
2239 self.plain_ram_storage().as_ptr().copy_to_nonoverlapping(
2240 tensor.as_bytes_mut().as_mut_ptr(),
2241 self.plain_ram_storage().layout().size(),
2242 )
2243 } else if self.dt == DatumType::String {
2244 tensor
2245 .as_slice_mut_unchecked::<String>()
2246 .clone_from_slice(self.as_slice_unchecked());
2247 } else if self.dt == DatumType::Blob {
2248 tensor
2249 .as_slice_mut_unchecked::<Blob>()
2250 .clone_from_slice(self.as_slice_unchecked());
2251 } else if self.dt == DatumType::TDim {
2252 tensor
2253 .as_slice_mut_unchecked::<TDim>()
2254 .clone_from_slice(self.as_slice_unchecked());
2255 }
2256 }
2257 tensor
2258 }
2259 }
2260
2261 pub fn slice(&self, axis: usize, start: usize, end: usize) -> TractResult<Tensor> {
2262 if axis >= self.rank() {
2263 bail!("Can not slice at axis {} tensor {:?}", axis, self);
2264 }
2265 if start > self.shape[axis] || end > self.shape[axis] || start >= end {
2266 bail!("Invalid slicing range {start}..{end} on axis {axis} for {self:?}");
2267 }
2268 if let Some(sliced) =
2271 self.storage.as_storage().slice(self.dt, self.shape(), axis, start, end)?
2272 {
2273 return Ok(sliced);
2274 }
2275 let mut shape: TVec<usize> = self.shape().into();
2276 shape[axis] = end - start;
2277 unsafe {
2278 let mut tensor = Tensor::uninitialized_dt(self.datum_type(), &shape)?;
2279 tensor.assign_slice_from_resolved(&[], 0..end - start, self, &[], start..end, axis);
2280 Ok(tensor)
2281 }
2282 }
2283
2284 #[inline]
2285 pub fn view(&self) -> view::TensorView<'_> {
2286 unsafe { view::TensorView::view(self) }
2287 }
2288
2289 #[inline]
2290 pub fn view_at_prefix(&self, prefix: &[usize]) -> TractResult<view::TensorView<'_>> {
2291 view::TensorView::at_prefix(self, prefix)
2292 }
2293
2294 #[inline]
2295 pub fn view_offsetting(&self, coords: &[usize]) -> TractResult<view::TensorView<'_>> {
2296 view::TensorView::offsetting(self, coords)
2297 }
2298
2299 #[inline]
2300 pub unsafe fn view_offsetting_unchecked(&self, coords: &[usize]) -> view::TensorView<'_> {
2301 unsafe { view::TensorView::offsetting_unchecked(self, coords) }
2302 }
2303
2304 #[inline]
2305 pub fn view_mut(&mut self) -> view::TensorView<'_> {
2306 unsafe { view::TensorView::view(self) }
2307 }
2308
2309 #[inline]
2310 pub fn view_at_prefix_mut(&mut self, prefix: &[usize]) -> TractResult<view::TensorView<'_>> {
2311 view::TensorView::at_prefix(self, prefix)
2312 }
2313
2314 #[inline]
2315 pub fn view_offsetting_mut(&mut self, coords: &[usize]) -> TractResult<view::TensorView<'_>> {
2316 view::TensorView::offsetting(self, coords)
2317 }
2318
2319 pub fn offset_u8_as_i8(self: &Arc<Self>) -> Arc<Self> {
2321 let mut t = if let DatumType::U8 = self.dt.unquantized() {
2322 self.try_as_plain_ram()
2323 .unwrap()
2324 .to_array_view::<u8>()
2325 .unwrap()
2326 .mapv(|v| v.wrapping_sub(128) as i8)
2327 .into_tensor()
2328 } else {
2329 return self.clone();
2330 };
2331
2332 if let DatumType::QU8(qp) = self.dt {
2333 if let QParams::ZpScale { zero_point, scale } = qp {
2334 t.dt = DatumType::QI8(QParams::ZpScale { zero_point: zero_point - 128, scale });
2335 } else {
2336 t.dt = DatumType::QI8(qp);
2337 }
2338 }
2339
2340 t.into_arc_tensor()
2341 }
2342
2343 pub fn offset_i8_as_u8(self: &Arc<Self>) -> Arc<Self> {
2345 let mut t = if let DatumType::I8 = self.dt.unquantized() {
2346 self.try_as_plain_ram()
2347 .unwrap()
2348 .to_array_view::<i8>()
2349 .unwrap()
2350 .mapv(|v| (v as u8).wrapping_add(128))
2351 .into_tensor()
2352 } else {
2353 return self.clone();
2354 };
2355
2356 if let DatumType::QI8(qp) = self.dt {
2357 if let QParams::ZpScale { zero_point, scale } = qp {
2358 t.dt = DatumType::QU8(QParams::ZpScale { zero_point: zero_point + 128, scale });
2359 } else {
2360 t.dt = DatumType::QU8(qp);
2361 }
2362 }
2363 t.into_arc_tensor()
2364 }
2365
2366 pub fn to_aligned_default(&self) -> TractResult<Self> {
2367 if self.dt.is_copy() {
2368 unsafe {
2369 let mut t = Self::uninitialized_dt(self.dt, &self.shape)?;
2370 t.as_bytes_mut().copy_from_slice(self.as_bytes());
2371 Ok(t)
2372 }
2373 } else {
2374 let mut t = Self::zero_dt(self.dt, &self.shape)?;
2375 if self.dt == String::datum_type() {
2376 t.try_as_plain_ram_mut()?
2377 .as_slice_mut::<String>()?
2378 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2379 } else if self.dt == Blob::datum_type() {
2380 t.try_as_plain_ram_mut()?
2381 .as_slice_mut::<Blob>()?
2382 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2383 } else if self.dt == TDim::datum_type() {
2384 t.try_as_plain_ram_mut()?
2385 .as_slice_mut::<TDim>()?
2386 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2387 }
2388 Ok(t)
2389 }
2390 }
2391
2392 pub fn natural_strides(shape: &[usize]) -> TVec<isize> {
2393 let mut strides = tvec!();
2394 compute_natural_stride_to(&mut strides, shape);
2395 strides
2396 }
2397
2398 #[inline]
2402 pub fn has_plain_ram_storage(&self) -> bool {
2403 matches!(self.storage, StorageKind::Plain(_))
2404 }
2405
2406 pub fn into_plain_ram(mut self) -> TractResult<Tensor> {
2410 if self.has_plain_ram_storage() {
2411 return Ok(self);
2412 }
2413 ensure!(self.dt.is_copy());
2414 let storage =
2415 std::mem::replace(&mut self.storage, StorageKind::Plain(PlainStorage::default()));
2416 let storage = storage.into_plain_ram().context("Storage can not produce plain bytes")?;
2417 Ok(Tensor {
2418 dt: self.dt,
2419 shape: self.shape.clone(),
2420 strides: self.strides.clone(),
2421 len: self.len,
2422 storage: StorageKind::Plain(storage),
2423 })
2424 }
2425
2426 pub fn into_blob(mut self) -> TractResult<Blob> {
2427 ensure!(self.dt.is_copy());
2428 let storage =
2429 std::mem::replace(&mut self.storage, StorageKind::Plain(PlainStorage::default()));
2430 Ok(storage.into_plain_ram().context("Storage is not plain")?.into_blob())
2431 }
2432}
2433
2434impl PartialEq for Tensor {
2435 fn eq(&self, other: &Tensor) -> bool {
2436 if self.dt != other.dt || self.shape != other.shape {
2437 return false;
2438 }
2439 match (self.storage.as_plain_ram(), other.storage.as_plain_ram()) {
2440 (Some(_), Some(_)) => self.eq_dt(other).unwrap_or(false),
2441 (None, None) => self.storage == other.storage,
2442 _ => false,
2443 }
2444 }
2445}
2446
2447impl Eq for Tensor {}
2448
2449impl fmt::Debug for Tensor {
2450 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2451 let content = self.dump(false).unwrap_or_else(|e| format!("Error : {e:?}"));
2452 write!(formatter, "{content}")
2453 }
2454}
2455
2456#[cfg(feature = "complex")]
2457pub fn reinterpret_inner_dim_as_complex(mut t: Tensor) -> TractResult<Tensor> {
2458 ensure!(
2459 t.shape().last() == Some(&2),
2460 "The last dimension in the tensor shape {:?} must be 2",
2461 t.shape()
2462 );
2463 unsafe {
2464 t.shape.pop();
2465 t.set_datum_type(t.datum_type().complexify()?);
2466 t.update_strides_and_len();
2467 Ok(t)
2468 }
2469}
2470
2471#[cfg(feature = "complex")]
2472pub fn reinterpret_complex_as_inner_dim(mut t: Tensor) -> TractResult<Tensor> {
2473 unsafe {
2474 t.shape.push(2);
2475 t.set_datum_type(t.datum_type().decomplexify()?);
2476 t.update_strides_and_len();
2477 Ok(t)
2478 }
2479}
2480
2481pub fn clip_range_bounds(len: usize, range: impl std::ops::RangeBounds<usize>) -> Range<usize> {
2482 use std::ops::Bound;
2483 let start = match range.start_bound() {
2484 Bound::Included(ix) => *ix,
2485 Bound::Excluded(ix) => ix + 1,
2486 Bound::Unbounded => 0,
2487 };
2488 let end = match range.end_bound() {
2489 Bound::Included(ix) => *ix + 1,
2490 Bound::Excluded(ix) => *ix,
2491 Bound::Unbounded => len,
2492 };
2493 start..end
2494}
2495
2496pub fn natural_strides(shape: &[usize]) -> TVec<isize> {
2497 let mut strides = tvec!();
2498 compute_natural_stride_to(&mut strides, shape);
2499 strides
2500}
2501
2502fn compute_natural_stride_to(strides: &mut TVec<isize>, shape: &[usize]) {
2503 match shape.len() {
2504 0 => (),
2505 1 => strides.push(1),
2506 2 => strides.extend_from_slice(&[shape[1] as isize, 1]),
2507 3 => strides.extend_from_slice(&[(shape[1] * shape[2]) as isize, shape[2] as _, 1]),
2508 4 => strides.extend_from_slice(&[
2509 (shape[1] * shape[2] * shape[3]) as isize,
2510 (shape[2] * shape[3]) as _,
2511 shape[3] as _,
2512 1,
2513 ]),
2514 _ => {
2515 strides.push(1);
2516 for dim in shape.as_ref().iter().skip(1).rev() {
2517 let previous = *strides.last().unwrap();
2518 strides.push(previous * *dim as isize)
2519 }
2520 strides.reverse();
2521 }
2522 }
2523}
2524
2525impl<D: ::ndarray::Dimension, T: Datum> From<Array<T, D>> for Tensor {
2526 fn from(it: Array<T, D>) -> Tensor {
2527 Tensor::from_datum(it.into_dyn())
2528 }
2529}
2530
2531pub trait IntoTensor: Sized {
2533 fn into_tensor(self) -> Tensor;
2537}
2538
2539pub trait IntoArcTensor: Sized {
2541 fn into_arc_tensor(self) -> Arc<Tensor>;
2545}
2546
2547impl<D: ::ndarray::Dimension, T: Datum> IntoTensor for Array<T, D> {
2548 fn into_tensor(self) -> Tensor {
2549 Tensor::from(self)
2550 }
2551}
2552
2553impl<D: ::ndarray::Dimension, T: Datum> IntoArcTensor for Array<T, D> {
2554 fn into_arc_tensor(self) -> Arc<Tensor> {
2555 Arc::new(Tensor::from(self))
2556 }
2557}
2558
2559impl IntoTensor for Tensor {
2560 fn into_tensor(self) -> Tensor {
2561 self
2562 }
2563}
2564
2565impl IntoTensor for Arc<Tensor> {
2566 fn into_tensor(self) -> Tensor {
2567 Arc::try_unwrap(self).unwrap_or_else(|t| (*t).clone())
2568 }
2569}
2570
2571impl IntoArcTensor for Tensor {
2572 fn into_arc_tensor(self) -> Arc<Tensor> {
2573 Arc::new(self)
2574 }
2575}
2576
2577impl IntoArcTensor for Arc<Tensor> {
2578 fn into_arc_tensor(self) -> Arc<Tensor> {
2579 self
2580 }
2581}
2582
2583#[cfg(test)]
2584mod tests {
2585 use crate::dim::SymbolScope;
2586 use crate::prelude::tensor1;
2587
2588 use super::*;
2589 use litteral::tensor0;
2590 use proptest::collection::vec;
2591 use proptest::prelude::*;
2592
2593 #[test]
2596 fn from_raw_rejects_length_mismatch() {
2597 let err = unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 12]) }
2599 .expect_err("from_raw must reject a short content buffer, not panic");
2600 assert!(err.to_string().contains("does not match shape"), "unexpected error: {err}");
2601 assert!(unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 32]) }.is_err());
2603 assert!(unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 24]) }.is_ok());
2605 }
2606
2607 #[derive(Debug)]
2608 struct PermuteAxisProblem {
2609 shape: Vec<usize>,
2610 permutation: Vec<usize>,
2611 }
2612
2613 impl Arbitrary for PermuteAxisProblem {
2614 type Strategy = BoxedStrategy<PermuteAxisProblem>;
2615 type Parameters = ();
2616
2617 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
2618 (0..8usize)
2619 .prop_flat_map(|rank| {
2620 let permute: Vec<usize> = (0..rank).collect();
2621 (proptest::collection::vec(1..5usize, rank), Just(permute).prop_shuffle())
2622 })
2623 .prop_map(|(shape, permutation)| PermuteAxisProblem { shape, permutation })
2624 .boxed()
2625 }
2626 }
2627
2628 impl PermuteAxisProblem {
2629 fn input(&self) -> ArrayD<i32> {
2630 let mut i = 0;
2631 ArrayD::from_shape_simple_fn(&*self.shape, || {
2632 i += 1;
2633 i
2634 })
2635 .permuted_axes(&*self.permutation)
2636 }
2637
2638 fn reference(&self) -> Tensor {
2639 let values: Vec<i32> = self.input().iter().copied().collect();
2640 let shape = self.permutation.iter().map(|ix| self.shape[*ix]).collect::<TVec<usize>>();
2641 super::litteral::tensor1(&values).into_shape(&shape).unwrap()
2642 }
2643
2644 fn tract(&self) -> Tensor {
2645 Tensor::from(self.input())
2646 }
2647
2648 fn check(&self) -> proptest::test_runner::TestCaseResult {
2649 prop_assert_eq!(self.tract(), self.reference());
2650 Ok(())
2651 }
2652 }
2653
2654 proptest::proptest! {
2655 #[test]
2656 fn prop(pb: PermuteAxisProblem) {
2657 pb.check().unwrap();
2658 }
2659 }
2660
2661 #[test]
2662 fn t_1_2() {
2663 PermuteAxisProblem { shape: vec![2, 1], permutation: vec![1, 0] }.check().unwrap();
2664 }
2665
2666 #[test]
2667 fn t_2_2() {
2668 PermuteAxisProblem { shape: vec![2, 2], permutation: vec![1, 0] }.check().unwrap();
2669 }
2670
2671 fn for_each_shape(rank: usize, max_dim: usize, mut f: impl FnMut(&[usize])) {
2672 let mut shape = vec![1usize; rank];
2673 loop {
2674 f(&shape);
2675 let mut i = rank;
2676 loop {
2677 if i == 0 {
2678 return;
2679 }
2680 i -= 1;
2681 shape[i] += 1;
2682 if shape[i] <= max_dim {
2683 break;
2684 }
2685 shape[i] = 1;
2686 }
2687 }
2688 }
2689
2690 fn check_moved_axis<T: Datum + num_traits::NumCast + Copy>(
2691 shape: &[usize],
2692 from: usize,
2693 to: usize,
2694 ) {
2695 let n: usize = shape.iter().product();
2696 let data: Vec<T> = (0..n).map(|i| T::from(i % 251).unwrap()).collect();
2697 let t = Tensor::from_shape::<T>(shape, &data).unwrap();
2698 let mut permutation: Vec<usize> = (0..shape.len()).collect();
2699 permutation.remove(from);
2700 permutation.insert(to, from);
2701 let reference = unsafe {
2702 t.clone().into_array_unchecked::<T>().permuted_axes(&*permutation).into_tensor()
2703 };
2704 let got = t.moved_axis(from, to).unwrap();
2705 assert_eq!(got.shape(), reference.shape(), "{shape:?} {from}->{to}");
2706 assert_eq!(got.as_bytes(), reference.as_bytes(), "{shape:?} {from}->{to}");
2707 }
2708
2709 #[test]
2710 fn moved_axis_matches_permuted_axes() {
2711 for (rank, max_dim) in [(2, 7), (3, 7), (4, 6), (5, 4)] {
2712 for_each_shape(rank, max_dim, |shape| {
2713 for from in 0..rank {
2714 for to in 0..rank {
2715 check_moved_axis::<u8>(shape, from, to);
2716 check_moved_axis::<f16>(shape, from, to);
2717 check_moved_axis::<f32>(shape, from, to);
2718 check_moved_axis::<f64>(shape, from, to);
2719 }
2720 }
2721 });
2722 }
2723 }
2724
2725 #[test]
2726 fn moved_axis_matches_permuted_axes_across_tiles() {
2727 for shape in [
2729 vec![1030usize, 17],
2730 vec![1024, 5],
2731 vec![4096, 3],
2732 vec![2048, 64],
2733 vec![3, 1024, 7],
2734 vec![2, 70, 33],
2735 vec![1, 64, 100, 96],
2736 ] {
2737 for from in 0..shape.len() {
2738 for to in 0..shape.len() {
2739 check_moved_axis::<u8>(&shape, from, to);
2740 check_moved_axis::<f16>(&shape, from, to);
2741 check_moved_axis::<f32>(&shape, from, to);
2742 check_moved_axis::<f64>(&shape, from, to);
2743 }
2744 }
2745 }
2746 }
2747
2748 #[test]
2749 fn moved_axis_falls_back_on_non_contiguous() {
2750 let mut t =
2751 Tensor::from_shape::<f32>(&[4, 3], &(0..12).map(|i| i as f32).collect_vec()).unwrap();
2752 unsafe { t.set_geometry_unchecked(&[3, 4], &[1, 4]) };
2753 let got = t.moved_axis(0, 1).unwrap();
2754 let reference = unsafe {
2755 t.clone().into_array_unchecked::<f32>().permuted_axes(&[1, 0][..]).into_tensor()
2756 };
2757 assert_eq!(got.shape(), reference.shape());
2758 assert_eq!(got.as_bytes(), reference.as_bytes());
2759 }
2760
2761 #[test]
2762 fn permute_axes_scalar() {
2763 let t = tensor0(1f32);
2764 assert_eq!(t.clone().permute_axes(&[]).unwrap(), t);
2765 }
2766
2767 #[derive(Debug)]
2768 struct BroadcastVecToShape {
2769 vec: Vec<f32>,
2770 axis: usize,
2771 shape: TVec<usize>,
2772 }
2773
2774 impl BroadcastVecToShape {
2775 fn check(&self) -> proptest::test_runner::TestCaseResult {
2776 let input = tensor1(&self.vec);
2777 let mut intermediate = tvec![1usize; self.shape.len()];
2778 intermediate[self.axis] = self.vec.len();
2779 let reference = input
2780 .clone()
2781 .into_shape(&intermediate)
2782 .unwrap()
2783 .broadcast_to_shape(&self.shape)
2784 .unwrap();
2785 prop_assert_eq!(
2786 reference,
2787 input.broadcast_vector_to_shape(&self.shape, self.axis).unwrap()
2788 );
2789 Ok(())
2790 }
2791 }
2792
2793 impl Arbitrary for BroadcastVecToShape {
2794 type Strategy = BoxedStrategy<BroadcastVecToShape>;
2795 type Parameters = ();
2796
2797 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
2798 vec(0usize..5, 0usize..4)
2799 .prop_flat_map(|shape| {
2800 (vec(-10f32..10f32, 0usize..5), Just(shape.clone()), 0..shape.len() + 1)
2801 })
2802 .prop_map(|(vec, mut shape, axis)| {
2803 shape.insert(axis, vec.len());
2804 BroadcastVecToShape { vec, shape: shape.into(), axis }
2805 })
2806 .boxed()
2807 }
2808 }
2809
2810 proptest::proptest! {
2811 #[test]
2812 fn broadcast_vector_to_shape_prop(pb: BroadcastVecToShape) {
2813 pb.check().unwrap()
2814 }
2815 }
2816
2817 #[test]
2818 #[cfg(feature = "complex")]
2819 fn test_reinterpret_inner_dim_as_complex() -> TractResult<()> {
2820 let input = crate::internal::tensor2(&[[1.0f32, 2.0], [3.0, 4.0], [5.0, 6.0]]);
2821 let cplx_input = reinterpret_inner_dim_as_complex(input)?;
2822 let expected = crate::internal::tensor1(&[
2823 Complex::new(1.0f32, 2.0),
2824 Complex::new(3.0, 4.0),
2825 Complex::new(5.0, 6.0),
2826 ]);
2827 assert_eq!(expected, cplx_input);
2828 Ok(())
2829 }
2830
2831 #[test]
2832 #[cfg(feature = "complex")]
2833 fn test_reinterpret_inner_dim_as_complex_2() -> TractResult<()> {
2834 let input =
2835 crate::internal::tensor3(&[[[1i32, 2], [1, 2]], [[3, 4], [3, 4]], [[5, 6], [5, 6]]]);
2836 let cplx_input = reinterpret_inner_dim_as_complex(input)?;
2837 let expected = crate::internal::tensor2(&[
2838 [Complex::new(1i32, 2), Complex::new(1, 2)],
2839 [Complex::new(3, 4), Complex::new(3, 4)],
2840 [Complex::new(5, 6), Complex::new(5, 6)],
2841 ]);
2842 assert_eq!(expected, cplx_input);
2843 Ok(())
2844 }
2845
2846 #[test]
2847 fn clone_tdim_tensor() {
2848 let symbols = SymbolScope::default();
2849 let a = symbols.sym("a");
2850 let t = tensor0(TDim::from(a));
2851 let _ = t.clone();
2852 }
2853
2854 #[test]
2855 fn ulp_approximation_accepts_within_bound() -> TractResult<()> {
2856 let a = tensor1(&[1.0f32, 2.0, 3.0]);
2857 let b = tensor1(&[
2858 f32::from_bits(1.0f32.to_bits() + 1),
2859 2.0,
2860 f32::from_bits(3.0f32.to_bits() + 2),
2861 ]);
2862 a.close_enough(&b, Approximation::Ulp(2))?;
2863 assert!(a.close_enough(&b, Approximation::Ulp(1)).is_err());
2864 assert_eq!(a.max_ulp_distance(&b)?, (2, Some(2)));
2865 Ok(())
2866 }
2867
2868 #[test]
2869 fn ulp_approximation_uses_the_tensor_own_float_type() -> TractResult<()> {
2870 let one = f16::from_f32(1.0);
2873 let a = tensor1(&[one]);
2874 let b = tensor1(&[f16::from_bits(one.to_bits() + 1)]);
2875 assert_eq!(a.ulp_comparison_dt(), DatumType::F16);
2876 assert_eq!(a.max_ulp_distance(&b)?, (1, Some(0)));
2877 a.close_enough(&b, Approximation::Ulp(1))?;
2878 Ok(())
2879 }
2880
2881 #[test]
2882 fn ulp_approximation_is_scale_free() -> TractResult<()> {
2883 let a = tensor1(&[1e-30f32, 1e30]);
2886 let b = tensor1(&[
2887 f32::from_bits(1e-30f32.to_bits() + 1),
2888 f32::from_bits(1e30f32.to_bits() + 1),
2889 ]);
2890 a.close_enough(&b, Approximation::Ulp(1))?;
2891 Ok(())
2892 }
2893
2894 #[test]
2895 fn ulp_approximation_rejects_shape_mismatch() {
2896 let a = tensor1(&[1.0f32, 2.0]);
2897 let b = tensor1(&[1.0f32]);
2898 assert!(a.close_enough(&b, Approximation::Ulp(1000)).is_err());
2899 }
2900
2901 fn stack_reference<T: Datum + Copy + num_traits::Zero>(
2906 axis: usize,
2907 tensors: &[Tensor],
2908 ) -> Tensor {
2909 let mut shape: TVec<usize> = tensors[0].shape().into();
2910 shape[axis] = tensors.iter().map(|t| t.shape()[axis]).sum();
2911 let mut out = Tensor::zero::<T>(&shape).unwrap();
2912 let outer: usize = shape[..axis].iter().product();
2913 let inner: usize = shape[axis + 1..].iter().product();
2914 let mid = shape[axis];
2915 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
2916 let mut base = 0;
2917 for t in tensors {
2918 let m = t.shape()[axis];
2919 let tv = unsafe { t.as_slice_unchecked::<T>() };
2920 for o in 0..outer {
2921 for j in 0..m {
2922 for i in 0..inner {
2923 ov[(o * mid + base + j) * inner + i] = tv[(o * m + j) * inner + i];
2924 }
2925 }
2926 }
2927 base += m;
2928 }
2929 out
2930 }
2931
2932 fn ramp<T: Datum + Copy + From<u8>>(shape: &[usize], seed: u8) -> Tensor {
2933 let n: usize = shape.iter().product();
2934 let v: Vec<T> = (0..n).map(|i| T::from(seed.wrapping_add(i as u8))).collect();
2935 Tensor::from_shape(shape, &v).unwrap()
2936 }
2937
2938 macro_rules! stack_agrees_for {
2939 ($name:ident, $t:ty) => {
2940 #[test]
2941 fn $name() {
2942 for shape in [
2943 tvec!(1usize, 256, 1, 1),
2944 tvec!(1usize, 2, 128, 1),
2945 tvec!(1usize, 35, 35, 8),
2946 tvec!(4usize, 3),
2947 tvec!(7usize),
2948 ] {
2949 for axis in 0..shape.len() {
2950 let a: Tensor = ramp::<$t>(&shape, 1);
2951 let b: Tensor = ramp::<$t>(&shape, 100);
2952 let c: Tensor = ramp::<$t>(&shape, 200);
2953 for n in 1..=3 {
2954 let ins = [a.clone(), b.clone(), c.clone()][..n].to_vec();
2955 let got = Tensor::stack_tensors(axis, &ins).unwrap();
2956 let want = stack_reference::<$t>(axis, &ins);
2957 assert_eq!(got, want, "shape {shape:?} axis {axis} n {n}");
2958 }
2959 }
2960 }
2961 }
2962 };
2963 }
2964
2965 stack_agrees_for!(stack_tensors_agrees_u8, u8);
2966 stack_agrees_for!(stack_tensors_agrees_u16, u16);
2967 stack_agrees_for!(stack_tensors_agrees_u32, u32);
2968 stack_agrees_for!(stack_tensors_agrees_u64, u64);
2969
2970 #[test]
2973 fn stack_tensors_tolerates_a_zero_outer_extent() {
2974 let a = Tensor::zero::<f32>(&[0, 2, 3]).unwrap();
2975 let stacked = Tensor::stack_tensors(2, &[a.clone(), a.clone()]).unwrap();
2976 assert_eq!(stacked.shape(), &[0, 2, 6]);
2977 }
2978
2979 fn assign_slice_reference<T: Datum + Copy>(
2983 dst: &Tensor,
2984 dst_range: Range<usize>,
2985 src: &Tensor,
2986 src_range: Range<usize>,
2987 axis: usize,
2988 ) -> Tensor {
2989 let mut out = dst.clone();
2990 let outer: usize = dst.shape()[..axis].iter().product();
2991 let inner: usize = dst.shape()[axis + 1..].iter().product();
2992 let dst_mid = dst.shape()[axis];
2993 let src_mid = src.shape()[axis];
2994 let sv = unsafe { src.as_slice_unchecked::<T>() };
2995 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
2996 for o in 0..outer {
2997 for j in 0..dst_range.len() {
2998 for i in 0..inner {
2999 ov[(o * dst_mid + dst_range.start + j) * inner + i] =
3000 sv[(o * src_mid + src_range.start + j) * inner + i];
3001 }
3002 }
3003 }
3004 out
3005 }
3006
3007 macro_rules! assign_slice_agrees_for {
3008 ($name:ident, $t:ty) => {
3009 #[test]
3010 fn $name() {
3011 for (shape, axis, dst_mid, src_mid, dst_start, len, src_start) in [
3012 (tvec!(1usize, 56, 24), 2, 24, 8, 16, 8, 0),
3013 (tvec!(1usize, 56, 24), 2, 24, 24, 0, 16, 8),
3014 (tvec!(1usize, 32, 4, 128), 3, 128, 128, 0, 64, 64),
3015 (tvec!(1usize, 8, 16, 64), 2, 16, 1, 3, 1, 0),
3016 (tvec!(3usize, 5), 0, 3, 7, 1, 2, 4),
3017 (tvec!(4usize, 3), 1, 3, 3, 0, 3, 0),
3018 (tvec!(7usize), 0, 7, 7, 2, 0, 5),
3019 ] {
3020 let mut dst_shape = shape.clone();
3021 dst_shape[axis] = dst_mid;
3022 let mut src_shape = shape.clone();
3023 src_shape[axis] = src_mid;
3024 let mut got: Tensor = ramp::<$t>(&dst_shape, 1);
3025 let src: Tensor = ramp::<$t>(&src_shape, 100);
3026 let want = assign_slice_reference::<$t>(
3027 &got,
3028 dst_start..dst_start + len,
3029 &src,
3030 src_start..src_start + len,
3031 axis,
3032 );
3033 got.assign_slice(
3034 dst_start..dst_start + len,
3035 &src,
3036 src_start..src_start + len,
3037 axis,
3038 )
3039 .unwrap();
3040 assert_eq!(got, want, "shape {dst_shape:?} axis {axis}");
3041 }
3042 }
3043 };
3044 }
3045
3046 assign_slice_agrees_for!(assign_slice_agrees_u8, u8);
3047 assign_slice_agrees_for!(assign_slice_agrees_u16, u16);
3048 assign_slice_agrees_for!(assign_slice_agrees_u32, u32);
3049 assign_slice_agrees_for!(assign_slice_agrees_u64, u64);
3050
3051 macro_rules! assign_slice_at_prefix_agrees_for {
3054 ($name:ident, $t:ty) => {
3055 #[test]
3056 fn $name() {
3057 for (shape, prefix, src_lead, src_prefix, axis, start, len, src_start) in [
3058 (tvec!(3usize, 5, 7), tvec!(2usize), 4, tvec!(3usize), 2, 3, 4, 0),
3059 (tvec!(3usize, 5, 7), tvec!(0usize), 1, tvec!(0usize), 1, 1, 3, 2),
3060 (tvec!(2usize, 4, 8, 3), tvec!(1usize, 2), 2, tvec!(0usize, 1), 3, 0, 3, 0),
3061 (tvec!(4usize, 6), tvec!(), 4, tvec!(), 1, 2, 4, 2),
3062 ] {
3063 let mut src_shape = shape.clone();
3064 src_shape[0] = src_lead;
3065 let mut got: Tensor = ramp::<$t>(&shape, 1);
3066 let src: Tensor = ramp::<$t>(&src_shape, 100);
3067 let mut want_sub = sub_tensor(&got, &prefix);
3070 want_sub
3071 .assign_slice(
3072 start..start + len,
3073 &sub_tensor(&src, &src_prefix),
3074 src_start..src_start + len,
3075 axis - prefix.len(),
3076 )
3077 .unwrap();
3078 got.assign_slice_at_prefix(
3079 &prefix,
3080 start..start + len,
3081 &src,
3082 &src_prefix,
3083 src_start..src_start + len,
3084 axis,
3085 )
3086 .unwrap();
3087 assert_eq!(
3088 sub_tensor(&got, &prefix),
3089 want_sub,
3090 "shape {shape:?} prefix {prefix:?} axis {axis}"
3091 );
3092 }
3093 }
3094 };
3095 }
3096
3097 fn sub_tensor(t: &Tensor, prefix: &[usize]) -> Tensor {
3098 let mut sub = t.clone();
3099 for ix in prefix {
3100 sub = sub.slice(0, *ix, ix + 1).unwrap();
3101 sub.remove_axis(0).unwrap();
3102 }
3103 sub
3104 }
3105
3106 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u8, u8);
3107 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u16, u16);
3108 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u32, u32);
3109 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u64, u64);
3110
3111 fn fill_slice_reference<T: Datum + Copy>(
3115 data: &Tensor,
3116 prefix: &[usize],
3117 range: Range<usize>,
3118 value: T,
3119 axis: usize,
3120 ) -> Tensor {
3121 let mut out = data.clone();
3122 let shape = data.shape().to_vec();
3123 let inner: usize = shape[axis + 1..].iter().product();
3124 let mid = shape[axis];
3125 let outer: usize = shape[prefix.len()..axis].iter().product();
3126 let at: usize = izip!(prefix, data.strides()).map(|(ix, s)| ix * *s as usize).sum();
3127 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
3128 for o in 0..outer {
3129 for j in range.clone() {
3130 for i in 0..inner {
3131 ov[at + (o * mid + j) * inner + i] = value;
3132 }
3133 }
3134 }
3135 out
3136 }
3137
3138 macro_rules! fill_slice_agrees_for {
3139 ($name:ident, $t:ty) => {
3140 #[test]
3141 fn $name() {
3142 for (shape, prefix, axis, start, len) in [
3143 (tvec!(1usize, 56, 24), tvec!(), 2, 16, 8),
3144 (tvec!(3usize, 5, 7), tvec!(), 1, 1, 3),
3145 (tvec!(3usize, 5, 7), tvec!(2usize), 2, 3, 4),
3146 (tvec!(3usize, 5, 7), tvec!(1usize, 4), 2, 0, 7),
3147 (tvec!(4usize, 3), tvec!(), 0, 1, 2),
3148 (tvec!(2usize, 8, 16, 64), tvec!(1usize), 2, 3, 1),
3149 (tvec!(7usize), tvec!(), 0, 2, 0),
3150 ] {
3151 let value: $t = 42 as $t;
3152 let mut got: Tensor = ramp::<$t>(&shape, 1);
3153 let want =
3154 fill_slice_reference::<$t>(&got, &prefix, start..start + len, value, axis);
3155 got.fill_slice_at_prefix(&prefix, start..start + len, &tensor0(value), axis)
3156 .unwrap();
3157 assert_eq!(got, want, "shape {shape:?} prefix {prefix:?} axis {axis}");
3158 }
3159 }
3160 };
3161 }
3162
3163 fill_slice_agrees_for!(fill_slice_agrees_u8, u8);
3164 fill_slice_agrees_for!(fill_slice_agrees_u16, u16);
3165 fill_slice_agrees_for!(fill_slice_agrees_u32, u32);
3166 fill_slice_agrees_for!(fill_slice_agrees_u64, u64);
3167
3168 #[test]
3169 fn fill_slice_carries_non_copy_data() {
3170 let strings = |v: [&str; 6]| {
3171 ndarray::Array2::from_shape_vec((2, 3), v.iter().map(|s| s.to_string()).collect())
3172 .unwrap()
3173 .into_tensor()
3174 };
3175 let mut data = strings(["a", "b", "c", "d", "e", "f"]);
3176 data.fill_slice(1..3, &tensor0("x".to_string()), 1).unwrap();
3177 assert_eq!(data, strings(["a", "x", "x", "d", "x", "x"]));
3178 }
3179
3180 #[test]
3181 fn assign_slice_carries_non_copy_data() {
3182 let strings = |v: [&str; 6]| {
3183 ndarray::Array2::from_shape_vec((2, 3), v.iter().map(|s| s.to_string()).collect())
3184 .unwrap()
3185 .into_tensor()
3186 };
3187 let mut dst = strings(["a", "b", "c", "d", "e", "f"]);
3188 let src = ndarray::Array2::from_shape_vec((2, 1), vec!["x".to_string(), "y".to_string()])
3189 .unwrap()
3190 .into_tensor();
3191 dst.assign_slice(1..2, &src, 0..1, 1).unwrap();
3192 assert_eq!(dst, strings(["a", "x", "c", "d", "y", "f"]));
3193 }
3194
3195 #[test]
3198 fn broadcast_to_shape_agrees_with_the_view() {
3199 for (src, dst) in [
3200 (tvec!(1usize, 8, 1, 7, 4), tvec!(1usize, 8, 4, 7, 4)),
3201 (tvec!(1usize, 1, 1, 7), tvec!(2usize, 3, 5, 7)),
3202 (tvec!(4usize), tvec!(2usize, 3, 5, 4)),
3203 (tvec!(1usize, 5, 3), tvec!(6usize, 5, 3)),
3204 (tvec!(2usize, 3), tvec!(2usize, 3)),
3205 (tvec!(1usize), tvec!(3usize, 1, 2)),
3206 (tvec!(3usize, 1), tvec!(3usize, 0)),
3207 (tvec!(3usize, 1), tvec!(3usize, 5)),
3210 (tvec!(2usize, 1, 1), tvec!(2usize, 4, 3)),
3211 (tvec!(1usize, 16, 1, 1), tvec!(1usize, 16, 4, 4)),
3212 (tvec!(1usize, 1), tvec!(2usize, 3, 4)),
3213 (tvec!(1024usize, 1, 1), tvec!(1024usize, 4, 1)),
3214 ] {
3215 for dt in [f32::datum_type(), u8::datum_type(), i32::datum_type()] {
3216 let t = Tensor::zero_dt(dt, &src).unwrap().cast_to_dt(dt).unwrap().into_owned();
3217 let got = t.broadcast_to_shape(&dst).unwrap();
3218 let want = dispatch_datum!(Tensor::broadcast_to_shape_t(dt)(&t, &dst)).unwrap();
3219 assert_eq!(got.shape(), &*dst, "{src:?} -> {dst:?}");
3220 assert_eq!(got, want, "{src:?} -> {dst:?} {dt:?}");
3221 }
3222 }
3223 }
3224
3225 #[test]
3228 fn broadcast_to_shape_repeats_innermost_cells_with_the_right_value() {
3229 for (src, dst) in [
3230 (tvec!(3usize, 1), tvec!(3usize, 5)),
3231 (tvec!(2usize, 1, 1), tvec!(2usize, 4, 3)),
3232 (tvec!(1usize, 16, 1, 1), tvec!(1usize, 16, 4, 4)),
3233 (tvec!(1usize, 1), tvec!(2usize, 3, 4)),
3234 (tvec!(1024usize, 1, 1), tvec!(1024usize, 4, 1)),
3235 (tvec!(4usize, 1, 7), tvec!(4usize, 3, 7)),
3236 (tvec!(2usize, 3, 1, 1), tvec!(2usize, 3, 5, 6)),
3237 ] {
3238 let t = ramp::<u32>(&src, 0);
3239 let got = t.broadcast_to_shape(&dst).unwrap();
3240 let want = Tensor::broadcast_to_shape_t::<u32>(&t, &dst).unwrap();
3241 assert_eq!(got, want, "{src:?} -> {dst:?}");
3242 }
3243 }
3244
3245 #[test]
3246 fn broadcast_to_shape_carries_values_and_rejects_mismatches() {
3247 let t = tensor2(&[[1u8, 2, 3], [4, 5, 6]]);
3248 let got = t.clone().into_shape(&[2, 1, 3]).unwrap().broadcast_to_shape(&[2, 2, 3]).unwrap();
3249 assert_eq!(got, tensor3(&[[[1u8, 2, 3], [1, 2, 3]], [[4, 5, 6], [4, 5, 6]]]));
3250 assert!(t.broadcast_to_shape(&[3, 3]).is_err());
3251 assert!(t.broadcast_to_shape(&[3]).is_err());
3252 }
3253
3254 #[test]
3255 fn slice_keeps_the_datum_type_and_the_values() {
3256 let t = ramp::<u32>(&tvec!(2usize, 3, 4), 0);
3257 let got = t.slice(1, 1, 3).unwrap();
3258 let mut want = Tensor::zero::<u32>(&[2, 2, 4]).unwrap();
3259 want.assign_slice(0..2, &t, 1..3, 1).unwrap();
3260 assert_eq!(got, want);
3261 let quantized = Tensor::zero_dt(
3262 i8::datum_type().quantize(QParams::ZpScale { zero_point: 3, scale: 0.5 }),
3263 &[2, 4],
3264 )
3265 .unwrap();
3266 assert_eq!(quantized.slice(1, 0, 2).unwrap().datum_type(), quantized.datum_type());
3267 }
3268
3269 #[test]
3270 fn ulp_bounds_are_distinguished_by_equality() {
3271 assert_eq!(Approximation::Ulp(1), Approximation::Ulp(1));
3272 assert_ne!(Approximation::Ulp(1), Approximation::Ulp(2));
3273 }
3274
3275 #[derive(Debug)]
3278 struct LateStorage {
3279 elsewhere: Vec<u8>,
3280 here: std::sync::OnceLock<PlainStorage>,
3281 materializations: std::sync::atomic::AtomicUsize,
3282 }
3283
3284 impl LateStorage {
3285 fn new(bytes: &[u8]) -> LateStorage {
3286 LateStorage {
3287 elsewhere: bytes.to_vec(),
3288 here: std::sync::OnceLock::new(),
3289 materializations: std::sync::atomic::AtomicUsize::new(0),
3290 }
3291 }
3292 fn count(t: &Tensor) -> usize {
3293 t.storage_as::<LateStorage>()
3294 .unwrap()
3295 .materializations
3296 .load(std::sync::atomic::Ordering::Relaxed)
3297 }
3298 }
3299
3300 impl PartialEq for LateStorage {
3301 fn eq(&self, other: &Self) -> bool {
3302 self.elsewhere == other.elsewhere
3303 }
3304 }
3305 impl Eq for LateStorage {}
3306
3307 impl std::fmt::Display for LateStorage {
3308 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3309 write!(f, "LateStorage")
3310 }
3311 }
3312
3313 impl TensorStorage for LateStorage {
3314 fn byte_len(&self) -> usize {
3315 self.elsewhere.len()
3316 }
3317 fn is_empty(&self) -> bool {
3318 self.elsewhere.is_empty()
3319 }
3320 fn deep_clone(&self) -> Box<dyn TensorStorage> {
3321 Box::new(LateStorage::new(&self.elsewhere))
3322 }
3323 fn as_plain_ram(&self) -> Option<&PlainStorage> {
3324 self.here.get()
3325 }
3326 fn as_plain_ram_mut(&mut self) -> Option<&mut PlainStorage> {
3327 None
3328 }
3329 fn into_plain_ram(self: Box<Self>) -> Option<PlainStorage> {
3330 let me = *self;
3331 me.materialize_plain_ram().ok()?;
3332 me.here.into_inner()
3333 }
3334 fn dyn_hash(&self, _state: &mut dyn std::hash::Hasher) {}
3335 fn exotic_fact(&self, _shape: &[usize]) -> TractResult<Option<Box<dyn ExoticFact>>> {
3336 Ok(None)
3337 }
3338 fn is_exotic(&self) -> bool {
3339 false
3340 }
3341 fn in_ram(&self) -> bool {
3342 self.here.get().is_some()
3343 }
3344 fn materialize_plain_ram(&self) -> TractResult<&PlainStorage> {
3345 if let Some(here) = self.here.get() {
3346 return Ok(here);
3347 }
3348 self.materializations.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3349 let blob = Blob::from_bytes(&self.elsewhere)?;
3350 Ok(self.here.get_or_init(|| PlainStorage::from(blob)))
3351 }
3352 fn slice(
3353 &self,
3354 dt: DatumType,
3355 shape: &[usize],
3356 axis: usize,
3357 start: usize,
3358 end: usize,
3359 ) -> TractResult<Option<Tensor>> {
3360 if axis != 0 || shape[..axis].iter().product::<usize>() != 1 {
3362 return Ok(None);
3363 }
3364 let row = shape[1..].iter().product::<usize>() * dt.size_of();
3365 let mut sliced: TVec<usize> = shape.into();
3366 sliced[0] = end - start;
3367 Ok(Some(Tensor::from_storage(
3368 dt,
3369 &sliced,
3370 LateStorage::new(&self.elsewhere[start * row..end * row]),
3371 )))
3372 }
3373 }
3374
3375 fn late_tensor(shape: &[usize], values: &[f32]) -> Tensor {
3376 let host = Tensor::from_shape(shape, values).unwrap();
3377 Tensor::from_storage(f32::datum_type(), shape, LateStorage::new(host.as_bytes()))
3378 }
3379
3380 #[test]
3381 fn late_storage_stays_put_until_the_bytes_are_read() {
3382 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3383 assert!(!t.in_ram());
3385 assert!(t.as_plain_ram().is_none());
3386 assert_eq!(t.datum_type(), f32::datum_type());
3387 assert_eq!(t.shape(), &[2, 3]);
3388 assert_eq!(LateStorage::count(&t), 0);
3389 assert_eq!(
3391 t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(),
3392 &[1f32, 2., 3., 4., 5., 6.]
3393 );
3394 assert_eq!(LateStorage::count(&t), 1);
3395 assert_eq!(t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap()[0], 1f32);
3396 assert_eq!(LateStorage::count(&t), 1);
3397 }
3398
3399 #[test]
3400 fn late_storage_is_plain_but_not_in_ram_until_asked() {
3401 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3402 assert!(t.is_plain());
3403 assert!(!t.in_ram());
3404 let t = t.into_plain_ram().unwrap();
3405 assert!(t.in_ram());
3406 assert!(t.has_plain_ram_storage());
3407 assert_eq!(
3408 t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(),
3409 &[1f32, 2., 3., 4., 5., 6.]
3410 );
3411 }
3412
3413 #[test]
3414 fn late_storage_slices_without_materializing_when_it_can() {
3415 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3416 let row = t.slice(0, 1, 2).unwrap();
3417 assert_eq!(LateStorage::count(&t), 0);
3418 assert!(row.storage_as::<LateStorage>().is_some());
3419 assert_eq!(row.shape(), &[1, 3]);
3420 assert_eq!(row.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(), &[4f32, 5., 6.]);
3421 }
3422
3423 #[test]
3424 fn late_storage_falls_back_to_a_copy_on_a_gappy_slice() {
3425 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3426 let col = t.slice(1, 1, 3).unwrap();
3427 assert!(col.in_ram());
3429 assert_eq!(col.shape(), &[2, 2]);
3430 assert_eq!(col.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(), &[2f32, 3., 5., 6.]);
3431 assert_eq!(LateStorage::count(&t), 1);
3432 }
3433}