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
215impl Tensor {
216 #[inline]
223 fn plain_ram_storage(&self) -> &PlainStorage {
224 self.storage.materialize_plain_ram().expect("Non-plain storage")
225 }
226
227 #[inline]
228 fn plain_ram_storage_mut(&mut self) -> &mut PlainStorage {
229 self.storage.as_plain_ram_mut().expect("Non-plain storage")
230 }
231
232 pub fn storage_as<T: TensorStorage>(&self) -> Option<&T> {
233 self.storage.as_storage().downcast_ref::<T>()
234 }
235
236 pub fn try_storage_as<T: TensorStorage>(&self) -> TractResult<&T> {
237 self.storage_as::<T>().context("Unexpected tensor storage type")
238 }
239
240 pub fn from_storage(
241 dt: DatumType,
242 shape: &[usize],
243 storage: impl TensorStorage + 'static,
244 ) -> Tensor {
245 let len = shape.iter().product::<usize>();
246 let strides = Self::natural_strides(shape);
247 Tensor {
248 dt,
249 shape: shape.into(),
250 strides,
251 len,
252 storage: StorageKind::Exotic(Box::new(storage)),
253 }
254 }
255
256 #[inline]
258 pub fn as_plain_ram(&self) -> Option<PlainView<'_>> {
259 let storage = self.storage.as_plain_ram()?;
260 Some(PlainView::new(self, storage))
261 }
262
263 #[inline]
269 pub fn try_as_plain_ram(&self) -> TractResult<PlainView<'_>> {
270 let storage = self.storage.materialize_plain_ram()?;
271 Ok(PlainView::new(self, storage))
272 }
273
274 #[inline]
278 pub fn as_plain_ram_storage(&self) -> Option<&PlainStorage> {
279 self.storage.as_plain_ram()
280 }
281
282 #[inline]
285 pub fn as_plain_ram_storage_mut(&mut self) -> Option<&mut PlainStorage> {
286 self.storage.as_plain_ram_mut()
287 }
288
289 #[inline]
296 pub fn is_plain(&self) -> bool {
297 !self.storage.is_exotic()
298 }
299
300 #[inline]
303 pub fn is_exotic(&self) -> bool {
304 self.storage.is_exotic()
305 }
306
307 #[inline]
311 pub fn is_plain_ram(&self) -> bool {
312 self.as_plain_ram_storage().is_some()
313 }
314
315 #[inline]
324 pub fn in_ram(&self) -> bool {
325 self.storage.in_ram()
326 }
327
328 pub fn exotic_fact(&self) -> TractResult<Option<Box<dyn crate::exotic::ExoticFact>>> {
330 self.storage.as_storage().exotic_fact(&self.shape)
331 }
332
333 #[inline]
335 pub fn as_plain_ram_mut(&mut self) -> Option<PlainViewMut<'_>> {
336 let storage = self.storage.as_plain_ram_mut()?;
337 Some(PlainViewMut::new(self.dt, &self.shape, &self.strides, self.len, storage))
338 }
339
340 #[inline]
342 pub fn try_as_plain_ram_mut(&mut self) -> TractResult<PlainViewMut<'_>> {
343 self.as_plain_ram_mut().context("Tensor storage is not plain")
344 }
345
346 #[inline]
348 pub unsafe fn uninitialized<T: Datum>(shape: &[usize]) -> TractResult<Tensor> {
349 unsafe { Self::uninitialized_dt(T::datum_type(), shape) }
350 }
351
352 #[inline]
354 pub unsafe fn uninitialized_dt(dt: DatumType, shape: &[usize]) -> TractResult<Tensor> {
355 unsafe { Self::uninitialized_aligned_dt(dt, shape, vector_size()) }
356 }
357
358 #[inline]
360 pub unsafe fn uninitialized_aligned<T: Datum>(
361 shape: &[usize],
362 alignment: usize,
363 ) -> TractResult<Tensor> {
364 unsafe { Self::uninitialized_aligned_dt(T::datum_type(), shape, alignment) }
365 }
366
367 pub unsafe fn uninitialized_aligned_dt(
369 dt: DatumType,
370 shape: &[usize],
371 alignment: usize,
372 ) -> TractResult<Tensor> {
373 let bytes = shape
378 .iter()
379 .try_fold(dt.size_of(), |acc, &d| acc.checked_mul(d))
380 .filter(|&b| b <= isize::MAX as usize)
381 .ok_or_else(|| format_err!("tensor shape {shape:?} of {dt:?} is too large"))?;
382 let storage = StorageKind::Plain(PlainStorage::from(unsafe {
383 Blob::new_for_size_and_align(bytes, alignment)
384 }));
385 let mut tensor = Tensor { strides: tvec!(), dt, shape: shape.into(), storage, len: 0 };
386 if tensor.shape.len() == 0 {
387 tensor.len = 1;
388 } else {
389 tensor.update_strides_and_len();
390 }
391 if !tensor.storage.is_empty() {
392 unsafe fn write_defaults<T: Datum + Default>(tensor: &mut Tensor) {
393 unsafe {
394 let len = tensor.len;
395 let dst = tensor.as_slice_mut_unchecked::<T>().as_mut_ptr();
396 for i in 0..len {
397 std::ptr::write(dst.add(i), T::default());
398 }
399 }
400 }
401 if dt == String::datum_type() {
402 unsafe { write_defaults::<String>(&mut tensor) }
403 } else if dt == Blob::datum_type() {
404 unsafe { write_defaults::<Blob>(&mut tensor) }
405 } else if dt == TDim::datum_type() {
406 unsafe { write_defaults::<TDim>(&mut tensor) }
407 } else if cfg!(debug_assertions) {
408 assert!(dt.is_copy());
409 if dt == DatumType::F32 {
410 tensor.fill_t(f32::NAN).unwrap();
411 } else {
412 tensor.as_bytes_mut().iter_mut().for_each(|x| *x = (-1i8) as u8);
414 }
415 }
416 }
417 Ok(tensor)
418 }
419
420 pub fn stack_tensors(
421 axis: usize,
422 tensors: &[impl std::borrow::Borrow<Tensor>],
423 ) -> TractResult<Tensor> {
424 ensure!(tensors.len() > 0);
425 let rank = tensors[0].borrow().rank();
426 ensure!(axis < rank);
427 ensure!(tensors.iter().all(|t| t.borrow().rank() == rank));
428 let dt = tensors[0].borrow().datum_type();
429 ensure!(tensors.iter().all(|t| t.borrow().datum_type() == dt));
430 let mut shape: TVec<usize> = tensors[0].borrow().shape().into();
431 for ax in 0..rank {
432 if ax != axis {
433 ensure!(tensors.iter().all(|t| t.borrow().shape()[ax] == shape[ax]));
434 }
435 }
436 shape[axis] = tensors.iter().map(|v| v.borrow().shape()[axis]).sum();
437 unsafe {
438 let mut result = Tensor::uninitialized_dt(dt, &shape)?;
439 let outer: usize = shape[..axis].iter().product();
442 let out_stride = shape[axis..].iter().product::<usize>() * dt.size_of();
443 const SMALL_BLOCK_BYTES: usize = 64;
451 if dt.is_copy()
452 && outer > 0
453 && tensors.iter().all(|t| t.borrow().storage.as_plain_ram().is_some())
454 {
455 let out = result.plain_ram_storage_mut().as_mut_ptr();
456 let mut offset = 0isize;
457 for v in tensors {
458 let v = v.borrow();
459 let block = v.storage.byte_len() / outer;
460 let src = v.plain_ram_storage().as_ptr();
461 let dst = out.offset(offset);
462 if outer == 1 {
463 std::ptr::copy_nonoverlapping(src, dst, block);
464 } else if block >= SMALL_BLOCK_BYTES {
465 for o in 0..outer {
466 std::ptr::copy_nonoverlapping(
467 src.add(o * block),
468 dst.add(o * out_stride),
469 block,
470 );
471 }
472 } else {
473 match dt.size_of() {
476 1 => copy_blocks::<u8>(src, dst, outer, block, out_stride),
477 2 => copy_blocks::<u16>(src, dst, outer, block, out_stride),
478 4 => copy_blocks::<u32>(src, dst, outer, block, out_stride),
479 8 => copy_blocks::<u64>(src, dst, outer, block, out_stride),
480 16 => copy_blocks::<u128>(src, dst, outer, block, out_stride),
481 _ => {
482 for o in 0..outer {
483 std::ptr::copy_nonoverlapping(
484 src.add(o * block),
485 dst.add(o * out_stride),
486 block,
487 );
488 }
489 }
490 }
491 }
492 offset += block as isize;
493 }
494 } else {
495 let mut offset = 0;
496 for t in tensors {
497 let t = t.borrow();
498 let len = t.shape()[axis];
499 result.assign_slice_from_resolved(
500 &[],
501 offset..offset + len,
502 t,
503 &[],
504 0..len,
505 axis,
506 );
507 offset += len;
508 }
509 }
510
511 Ok(result)
512 }
513 }
514
515 pub fn clear<T: Datum + num_traits::Zero + Clone>(&mut self) -> TractResult<()> {
516 self.fill_t(T::zero())
517 }
518
519 pub fn zero<T: Datum + num_traits::Zero>(shape: &[usize]) -> TractResult<Tensor> {
520 unsafe {
521 let mut t = Tensor::uninitialized::<T>(shape)?;
522 t.clear::<T>()?;
523 Ok(t)
524 }
525 }
526
527 pub fn zero_scalar<T: Datum + num_traits::Zero>() -> TractResult<Tensor> {
528 Tensor::zero::<T>(&[])
529 }
530
531 pub fn zero_scalar_dt(dt: DatumType) -> TractResult<Tensor> {
532 Tensor::zero_dt(dt, &[])
533 }
534
535 pub fn zero_dt(dt: DatumType, shape: &[usize]) -> TractResult<Tensor> {
536 Tensor::zero_aligned_dt(dt, shape, vector_size())
537 }
538
539 pub fn fill_t<T: Datum + Clone>(&mut self, value: T) -> TractResult<()> {
540 self.try_as_plain_ram_mut()?
541 .as_slice_mut::<T>()?
542 .iter_mut()
543 .for_each(|item| *item = value.clone());
544 Ok(())
545 }
546
547 pub fn zero_aligned_dt(
548 dt: DatumType,
549 shape: &[usize],
550 alignment: usize,
551 ) -> TractResult<Tensor> {
552 if shape.iter().product::<usize>() == 0 {
553 unsafe { return Tensor::uninitialized_dt(dt, shape) };
554 }
555 if dt.is_quantized() {
556 unsafe {
557 let mut t = Tensor::uninitialized_dt(dt, shape)?;
558 let zp = dt.zp_scale().0;
559 match dt.unquantized() {
560 DatumType::I8 => t
561 .try_as_plain_ram_mut()?
562 .as_slice_mut::<i8>()?
563 .iter_mut()
564 .for_each(|item| *item = zp as _),
565 DatumType::U8 => t
566 .try_as_plain_ram_mut()?
567 .as_slice_mut::<u8>()?
568 .iter_mut()
569 .for_each(|item| *item = zp as _),
570 DatumType::I32 => t
571 .try_as_plain_ram_mut()?
572 .as_slice_mut::<i32>()?
573 .iter_mut()
574 .for_each(|item| *item = zp as _),
575 _ => unreachable!(),
576 }
577 Ok(t)
578 }
579 } else if dt == DatumType::Bool {
580 let mut t = unsafe { Tensor::uninitialized_dt(dt, shape)? };
581 t.fill_t::<bool>(false)?;
582 Ok(t)
583 } else {
584 dispatch_zerolike!(Self::zero_aligned(dt)(shape, alignment))
585 }
586 }
587
588 pub fn zero_aligned<T: Datum + num_traits::Zero>(
589 shape: &[usize],
590 alignment: usize,
591 ) -> TractResult<Tensor> {
592 unsafe {
593 let mut tensor = Self::uninitialized_aligned::<T>(shape, alignment)?;
594 tensor.clear::<T>()?;
595 Ok(tensor)
596 }
597 }
598
599 pub fn from_shape<T: Datum + Copy>(shape: &[usize], data: &[T]) -> TractResult<Tensor> {
602 Self::from_shape_align(shape, data, vector_size())
603 }
604
605 pub fn from_shape_align<T: Datum + Copy>(
608 shape: &[usize],
609 data: &[T],
610 align: usize,
611 ) -> TractResult<Tensor> {
612 ensure!(
613 data.len() == shape.iter().product::<usize>(),
614 "Shape product must be equal to data length"
615 );
616 unsafe {
617 let bytes = std::slice::from_raw_parts(
618 data.as_ptr() as *const u8,
619 data.len() * T::datum_type().size_of(),
620 );
621 let dt = T::datum_type();
622 Self::from_raw_dt_align(dt, shape, bytes, align)
623 }
624 }
625
626 pub unsafe fn from_raw<T: Datum>(shape: &[usize], content: &[u8]) -> TractResult<Tensor> {
630 unsafe { Tensor::from_raw_dt(T::datum_type(), shape, content) }
631 }
632
633 pub unsafe fn from_raw_aligned<T: Datum>(
634 shape: &[usize],
635 content: &[u8],
636 align: usize,
637 ) -> TractResult<Tensor> {
638 unsafe { Tensor::from_raw_dt_align(T::datum_type(), shape, content, align) }
639 }
640
641 pub unsafe fn from_raw_dt(
642 dt: DatumType,
643 shape: &[usize],
644 content: &[u8],
645 ) -> TractResult<Tensor> {
646 unsafe { Self::from_raw_dt_align(dt, shape, content, vector_size()) }
647 }
648
649 pub unsafe fn from_raw_dt_align(
650 dt: DatumType,
651 shape: &[usize],
652 content: &[u8],
653 align: usize,
654 ) -> TractResult<Tensor> {
655 let len = shape
660 .iter()
661 .try_fold(1usize, |acc, &d| acc.checked_mul(d))
662 .ok_or_else(|| format_err!("tensor shape {shape:?} overflows"))?;
663 let expected = len
664 .checked_mul(dt.size_of())
665 .ok_or_else(|| format_err!("tensor shape {shape:?} of {dt:?} is too large"))?;
666 ensure!(
667 content.len() == expected,
668 "Raw tensor data length ({}) does not match shape {:?} of {:?} ({} bytes)",
669 content.len(),
670 shape,
671 dt,
672 expected
673 );
674 let mut tensor = unsafe { Tensor::uninitialized_aligned_dt(dt, shape, align) }?;
675 tensor.as_bytes_mut().copy_from_slice(content);
676 Ok(tensor)
677 }
678
679 pub unsafe fn from_slice_align<T: Datum>(content: &[T], align: usize) -> TractResult<Tensor> {
680 let bytes = if content.len() == 0 {
681 &[]
682 } else {
683 unsafe {
684 std::slice::from_raw_parts(
685 content.as_ptr() as *const u8,
686 content.len() * T::datum_type().size_of(),
687 )
688 }
689 };
690 unsafe { Self::from_raw_dt_align(T::datum_type(), &[content.len()], bytes, align) }
691 }
692
693 #[inline]
695 pub fn rank(&self) -> usize {
696 self.shape.len()
697 }
698
699 #[inline]
701 pub fn shape(&self) -> &[usize] {
702 &self.shape
703 }
704
705 #[inline]
707 #[allow(clippy::len_without_is_empty)]
708 pub fn len(&self) -> usize {
709 self.len
710 }
711
712 #[inline]
714 #[allow(clippy::len_without_is_empty)]
715 pub fn volume(&self) -> usize {
716 self.len
717 }
718
719 #[inline]
721 pub fn strides(&self) -> &[isize] {
722 &self.strides
723 }
724
725 fn update_strides_and_len(&mut self) {
726 self.strides.clear();
727 if self.shape.len() == 0 {
728 self.len = 1;
729 return;
730 }
731 compute_natural_stride_to(&mut self.strides, &self.shape);
732 self.len = unsafe { *self.strides.get_unchecked(0) as usize * self.shape.get_unchecked(0) };
733 }
734
735 pub unsafe fn set_shape_unchecked(&mut self, shape: &[usize]) {
737 if shape != &*self.shape {
738 self.shape.clear();
739 self.shape.extend_from_slice(shape);
740 self.update_strides_and_len();
741 }
742 }
743
744 pub unsafe fn set_geometry_unchecked(&mut self, shape: &[usize], strides: &[isize]) {
746 self.shape.clear();
747 self.shape.extend_from_slice(shape);
748 self.strides.clear();
749 self.strides.extend_from_slice(strides);
750 }
751
752 pub fn set_shape(&mut self, shape: &[usize]) -> TractResult<()> {
754 if self.len() != shape.iter().product::<usize>() {
755 bail!("Invalid reshape {:?} to {:?}", self.shape, shape);
756 }
757 unsafe { self.set_shape_unchecked(shape) }
758 Ok(())
759 }
760
761 pub fn permute_axes(self, axes: &[usize]) -> TractResult<Tensor> {
762 ensure!(axes.iter().duplicates().next().is_none());
763 ensure!(axes.iter().all(|a| *a < self.rank()));
764 unsafe {
765 #[inline]
766 unsafe fn permute<T: Datum>(axes: &[usize], input: Tensor) -> Tensor {
767 unsafe { input.into_array_unchecked::<T>().permuted_axes(axes).into_tensor() }
768 }
769 let dt = self.datum_type();
770 let mut t = dispatch_datum_by_size!(permute(self.datum_type())(axes, self));
771 t.set_datum_type(dt);
772 Ok(t)
773 }
774 }
775
776 pub fn move_axis(self, from: usize, to: usize) -> TractResult<Tensor> {
777 let mut permutation: Vec<usize> = (0..self.rank()).collect();
778 permutation.remove(from);
779 permutation.insert(to, from);
780 self.permute_axes(&permutation)
781 }
782
783 pub fn collapse_axis_with_next(mut self, axis: usize) -> Tensor {
784 let removed = self.shape.remove(axis + 1);
785 self.shape[axis] *= removed;
786 self.update_strides_and_len();
787 self
788 }
789
790 pub fn split_axis(mut self, axis: usize, outer_dim: usize) -> TractResult<Tensor> {
791 if !self.shape[axis].is_multiple_of(outer_dim) {
792 bail!(
793 "Invalid axis split, shape is {:?}, axis split at {}, outer {}",
794 self.shape,
795 axis,
796 outer_dim
797 );
798 }
799 self.shape.insert(axis + 1, self.shape[axis] / outer_dim);
800 self.shape[axis] = outer_dim;
801 self.update_strides_and_len();
802 Ok(self)
803 }
804
805 pub fn into_shape(mut self, shape: &[usize]) -> TractResult<Tensor> {
807 self.set_shape(shape)?;
808 Ok(self)
809 }
810
811 pub fn insert_axis(&mut self, axis: usize) -> TractResult<()> {
812 self.shape.insert(axis, 1);
813 self.strides.insert(axis, self.strides.get(axis).copied().unwrap_or(1));
814 Ok(())
815 }
816
817 pub fn remove_axis(&mut self, axis: usize) -> TractResult<()> {
818 ensure!(self.shape[axis] == 1, "Remove a non-1 axis: axis {} in {:?}", axis, self);
819 self.shape.remove(axis);
820 self.strides.remove(axis);
821 Ok(())
822 }
823
824 pub fn broadcast_into_rank(mut self, rank: usize) -> TractResult<Tensor> {
825 self.broadcast_to_rank(rank)?;
826 self.update_strides_and_len();
827 Ok(self)
828 }
829
830 pub fn broadcast_to_rank(&mut self, rank: usize) -> TractResult<()> {
831 if rank < self.rank() {
832 bail!("Can only broadcast to higher rank")
833 }
834 while self.shape.len() < rank {
835 self.shape.insert(0, 1)
836 }
837 self.update_strides_and_len();
838 Ok(())
839 }
840
841 pub fn broadcast_scalar_to_shape(&self, shape: &[usize]) -> TractResult<Tensor> {
842 if self.rank() > 0 {
843 bail!("broadcast_scalar_to_shape called on {:?}, which is not a salar", self);
844 }
845 unsafe fn make<T: Datum>(src: &Tensor, dst: &mut Tensor) {
846 unsafe {
847 let value: &T = src.to_scalar_unchecked::<T>();
848 dst.as_slice_mut_unchecked::<T>().iter_mut().for_each(|item| *item = value.clone())
849 };
850 }
851 unsafe {
852 let mut t = Tensor::uninitialized_dt(self.datum_type(), shape)?;
853 dispatch_datum_by_size!(make(self.datum_type())(self, &mut t));
854 Ok(t)
855 }
856 }
857
858 fn broadcast_to_shape_t<T: Datum>(&self, shape: &[usize]) -> TractResult<Tensor> {
859 unsafe {
860 let view = self.to_array_view_unchecked::<T>();
861 let mut output = view
862 .broadcast(shape)
863 .with_context(|| format!("Broadcasting {view:?} to {shape:?}"))?
864 .into_owned()
865 .into_tensor();
866 output.set_datum_type(self.datum_type());
867 Ok(output)
868 }
869 }
870
871 pub fn broadcast_to_shape(&self, shape: &[usize]) -> TractResult<Tensor> {
872 if !self.dt.is_copy() {
873 return dispatch_datum!(Self::broadcast_to_shape_t(self.dt)(self, shape));
874 }
875 ensure!(
876 self.rank() <= shape.len(),
877 "Broadcasting {self:?} to {shape:?} would lose {} axes",
878 self.rank() - shape.len()
879 );
880 let offset = shape.len() - self.rank();
881 let mut src: TVec<usize> = tvec!(1; shape.len());
882 src[offset..].copy_from_slice(self.shape());
883 ensure!(
884 izip!(&src, shape).all(|(s, d)| *s == 1 || s == d),
885 "Broadcasting {self:?} to {shape:?}"
886 );
887 let mut split = shape.len();
891 while split > 0 && src[split - 1] == shape[split - 1] {
892 split -= 1;
893 }
894 let dt_size = self.dt.size_of();
895 let run = shape[split..].iter().product::<usize>() * dt_size;
896 let outer: usize = shape[..split].iter().product();
897 let mut src_strides: TVec<usize> = tvec!(0; split);
898 let mut acc = run;
899 for ax in (0..split).rev() {
900 src_strides[ax] = if src[ax] == 1 { 0 } else { acc };
901 acc *= src[ax];
902 }
903 let mut output = unsafe { Tensor::uninitialized_dt(self.dt, shape)? };
904 if run == 0 || outer == 0 {
905 return Ok(output);
906 }
907 let source = self.as_bytes();
908 let dst = output.as_bytes_mut();
909 let mut coords: TVec<usize> = tvec!(0; split);
910 for block in 0..outer {
911 let from: usize = izip!(&coords, &src_strides).map(|(c, s)| c * s).sum();
912 dst[block * run..][..run].copy_from_slice(&source[from..][..run]);
913 for ax in (0..split).rev() {
914 coords[ax] += 1;
915 if coords[ax] < shape[ax] {
916 break;
917 }
918 coords[ax] = 0;
919 }
920 }
921 Ok(output)
922 }
923
924 pub fn broadcast_vector_to_shape(&self, shape: &[usize], axis: usize) -> TractResult<Tensor> {
925 ensure!(self.rank() == 1);
926 ensure!(shape[axis] == self.len());
927 if !self.datum_type().is_copy() {
928 let mut vec_shape = vec![1; shape.len()];
929 vec_shape[axis] = self.len();
930 return self.clone().into_shape(&vec_shape)?.broadcast_to_shape(shape);
931 }
932 unsafe {
933 let mut output = Tensor::uninitialized_dt(self.datum_type(), shape)?;
934 if output.len() == 0 {
935 return Ok(output);
936 }
937 let inner_len = shape[axis + 1..].iter().product::<usize>();
938
939 unsafe fn splat<T>(input: &Tensor, output: &mut Tensor, inner_len: usize)
940 where
941 T: Datum + Copy,
942 {
943 unsafe {
944 for ix in 0..input.len() {
945 let value: T = input.as_slice_unchecked()[ix];
946 output.as_slice_mut_unchecked::<T>()[ix * inner_len..(ix + 1) * inner_len]
947 .iter_mut()
948 .for_each(|item| *item = value);
949 }
950 }
951 }
952 dispatch_copy_by_size!(splat(self.datum_type())(&self, &mut output, inner_len));
953
954 let outer_len = shape[0..axis].iter().product::<usize>();
955 let repeat_bytes_len = inner_len * self.as_bytes().len();
956 let bytes = output.as_bytes_mut();
957 for ix in 1..outer_len {
958 bytes.copy_within(0..repeat_bytes_len, ix * repeat_bytes_len);
959 }
960
961 Ok(output)
962 }
963 }
964 pub fn assign_slice(
965 &mut self,
966 range: impl std::ops::RangeBounds<usize>,
967 src: &Tensor,
968 src_range: impl std::ops::RangeBounds<usize>,
969 axis: usize,
970 ) -> TractResult<()> {
971 self.assign_slice_at_prefix(&[], range, src, &[], src_range, axis)
972 }
973
974 pub fn assign_slice_at_prefix(
980 &mut self,
981 prefix: &[usize],
982 range: impl std::ops::RangeBounds<usize>,
983 src: &Tensor,
984 src_prefix: &[usize],
985 src_range: impl std::ops::RangeBounds<usize>,
986 axis: usize,
987 ) -> TractResult<()> {
988 ensure!(self.rank() == src.rank());
989 ensure!(axis < self.rank());
990 let range = clip_range_bounds(self.shape[axis], range);
991 let src_range = clip_range_bounds(src.shape[axis], src_range);
992 ensure!(
993 src.datum_type() == self.datum_type(),
994 "Attempt to assign into {:?} from {:?}, datum type mismatch",
995 self.datum_type(),
996 src.datum_type()
997 );
998 ensure!(
999 src_range.len() == range.len(),
1000 "Attempt to assign a range of {:?} from a range of {:?}",
1001 range,
1002 src_range,
1003 );
1004 ensure!(
1005 prefix.len() == src_prefix.len() && prefix.len() <= axis,
1006 "Attempt to assign axis {axis} at prefixes {prefix:?} and {src_prefix:?}"
1007 );
1008 ensure!(
1009 izip!(prefix, self.shape()).all(|(ix, dim)| ix < dim)
1010 && izip!(src_prefix, src.shape()).all(|(ix, dim)| ix < dim),
1011 "Attempt to assign into {self:?} at {prefix:?} from {src:?} at {src_prefix:?}"
1012 );
1013 ensure!(
1014 izip!(prefix.len().., &self.shape[prefix.len()..], &src.shape[prefix.len()..])
1015 .all(|(ix, dst, src)| ix == axis || src == dst),
1016 "Attempt to assign a {}-axis range of {:?} from a range of {:?}",
1017 axis,
1018 self,
1019 src
1020 );
1021 ensure!(
1022 src_range.end <= src.shape()[axis],
1023 "Assigning from invalid slice (axis {}, {:?}) of {:?}",
1024 axis,
1025 src_range,
1026 src
1027 );
1028 ensure!(
1029 range.end <= self.shape()[axis],
1030 "Assigning to invalid slice (axis {}, {:?}) of {:?}",
1031 axis,
1032 range,
1033 self
1034 );
1035 unsafe { self.assign_slice_from_resolved(prefix, range, src, src_prefix, src_range, axis) };
1036 Ok(())
1037 }
1038
1039 pub unsafe fn assign_slice_unchecked(
1040 &mut self,
1041 range: impl std::ops::RangeBounds<usize>,
1042 src: &Tensor,
1043 src_range: impl std::ops::RangeBounds<usize>,
1044 axis: usize,
1045 ) {
1046 let range = clip_range_bounds(self.shape[axis], range);
1047 let src_range = clip_range_bounds(src.shape[axis], src_range);
1048 unsafe { self.assign_slice_from_resolved(&[], range, src, &[], src_range, axis) };
1049 }
1050
1051 fn prefix_offset(&self, prefix: &[usize]) -> usize {
1054 izip!(prefix, &self.strides).map(|(ix, stride)| ix * *stride as usize).sum::<usize>()
1055 * self.datum_type().size_of()
1056 }
1057
1058 #[allow(clippy::ptr_eq)]
1059 unsafe fn assign_slice_from_resolved(
1060 &mut self,
1061 prefix: &[usize],
1062 range: std::ops::Range<usize>,
1063 src: &Tensor,
1064 src_prefix: &[usize],
1065 src_range: std::ops::Range<usize>,
1066 axis: usize,
1067 ) {
1068 unsafe {
1069 use ndarray::Slice;
1070 unsafe fn assign_slice_t<T: Datum>(
1071 to: &mut Tensor,
1072 to_prefix: &[usize],
1073 to_range: Range<usize>,
1074 from: &Tensor,
1075 from_prefix: &[usize],
1076 from_range: Range<usize>,
1077 axis: usize,
1078 ) {
1079 unsafe {
1080 let mut to_view = to.to_array_view_mut_unchecked::<T>();
1081 let mut from_view = from.to_array_view_unchecked::<T>();
1082 for (ax, (to, from)) in izip!(to_prefix, from_prefix).enumerate() {
1083 to_view.slice_axis_inplace(Axis(ax), Slice::from(*to..*to + 1));
1084 from_view.slice_axis_inplace(Axis(ax), Slice::from(*from..*from + 1));
1085 }
1086 to_view
1087 .slice_axis_mut(Axis(axis), Slice::from(to_range))
1088 .assign(&from_view.slice_axis(Axis(axis), Slice::from(from_range)))
1089 }
1090 }
1091 if self.datum_type().is_copy() {
1092 let post = self.strides[axis] as usize * self.datum_type().size_of();
1097 let len = post * range.len();
1098 if len > 0 {
1099 let outer: usize = self.shape[prefix.len()..axis].iter().product();
1100 let dst_block = post * self.shape[axis];
1101 let src_block = post * src.shape[axis];
1102 let src_ptr = src
1103 .plain_ram_storage()
1104 .as_ptr()
1105 .add(src.prefix_offset(src_prefix) + post * src_range.start);
1106 let aliasing =
1107 self.plain_ram_storage().as_ptr() == src.plain_ram_storage().as_ptr();
1108 let dst_offset = self.prefix_offset(prefix) + post * range.start;
1109 let dst_ptr = self.plain_ram_storage_mut().as_mut_ptr().add(dst_offset);
1110 for run in 0..outer {
1111 let from = src_ptr.add(run * src_block);
1112 let to = dst_ptr.add(run * dst_block);
1113 if aliasing {
1114 std::ptr::copy(from, to, len);
1115 } else {
1116 std::ptr::copy_nonoverlapping(from, to, len);
1117 }
1118 }
1119 }
1120 } else {
1121 dispatch_datum!(assign_slice_t(self.datum_type())(
1122 self, prefix, range, src, src_prefix, src_range, axis
1123 ));
1124 }
1125 }
1126 }
1127 pub fn fill_slice(
1130 &mut self,
1131 range: impl std::ops::RangeBounds<usize>,
1132 value: &Tensor,
1133 axis: usize,
1134 ) -> TractResult<()> {
1135 self.fill_slice_at_prefix(&[], range, value, axis)
1136 }
1137
1138 pub fn fill_slice_at_prefix(
1143 &mut self,
1144 prefix: &[usize],
1145 range: impl std::ops::RangeBounds<usize>,
1146 value: &Tensor,
1147 axis: usize,
1148 ) -> TractResult<()> {
1149 ensure!(axis < self.rank(), "Filling axis {axis} of {self:?}");
1150 ensure!(
1151 prefix.len() <= axis,
1152 "Filling axis {axis} of {self:?} at prefix {prefix:?}, which reaches it"
1153 );
1154 ensure!(
1155 izip!(prefix, self.shape()).all(|(ix, dim)| ix < dim),
1156 "Filling {self:?} at prefix {prefix:?}"
1157 );
1158 ensure!(
1159 value.datum_type() == self.datum_type() && value.len() == 1,
1160 "Filling {:?} with {value:?}",
1161 self.datum_type()
1162 );
1163 let range = clip_range_bounds(self.shape[axis], range);
1164 ensure!(
1165 range.end <= self.shape[axis],
1166 "Filling invalid slice (axis {axis}, {range:?}) of {self:?}"
1167 );
1168 if !self.datum_type().is_copy() {
1169 return dispatch_datum!(Self::fill_slice_t(self.datum_type())(
1170 self, prefix, range, value, axis
1171 ));
1172 }
1173 let dt_size = self.datum_type().size_of();
1177 let post = self.strides[axis] as usize * dt_size;
1178 let len = post * range.len();
1179 if len == 0 {
1180 return Ok(());
1181 }
1182 let block = post * self.shape[axis];
1183 let runs: usize = self.shape[prefix.len()..axis].iter().product();
1184 let start = self.prefix_offset(prefix) + range.start * post;
1185 let value = &value.as_bytes()[..dt_size];
1186 let data = self.as_bytes_mut();
1187 for run in 0..runs {
1188 let run = &mut data[start + run * block..start + run * block + len];
1189 run[..dt_size].copy_from_slice(value);
1190 let mut written = dt_size;
1191 while written < len {
1192 let grow = written.min(len - written);
1193 run.copy_within(0..grow, written);
1194 written += grow;
1195 }
1196 }
1197 Ok(())
1198 }
1199
1200 fn fill_slice_t<T: Datum>(
1201 &mut self,
1202 prefix: &[usize],
1203 range: Range<usize>,
1204 value: &Tensor,
1205 axis: usize,
1206 ) -> TractResult<()> {
1207 let value = value.try_as_plain_ram()?.to_scalar::<T>()?.clone();
1208 let mut view = self.to_plain_array_view_mut::<T>()?;
1209 for (ax, ix) in prefix.iter().enumerate() {
1210 view.slice_axis_inplace(Axis(ax), (*ix..*ix + 1).into());
1211 }
1212 view.slice_axis_mut(Axis(axis), range.into()).fill(value);
1213 Ok(())
1214 }
1215
1216 #[inline]
1218 pub fn datum_type(&self) -> DatumType {
1219 self.dt
1220 }
1221
1222 #[inline]
1224 pub unsafe fn set_datum_type(&mut self, dt: DatumType) {
1225 self.dt = dt
1226 }
1227
1228 pub fn dump(&self, force_full: bool) -> TractResult<String> {
1232 if self.as_plain_ram_storage().is_none() {
1233 return Ok(format!(
1234 "{},{:?} (non-plain storage)",
1235 self.shape.iter().join(","),
1236 self.dt,
1237 ));
1238 }
1239 unsafe fn dump_t<D: Datum>(tensor: &Tensor, n: usize) -> String {
1240 unsafe {
1241 if let Some(qp) = tensor.datum_type().qparams() {
1242 let integers = tensor.cast_to::<i32>().unwrap();
1243 integers.as_slice_unchecked::<i32>()[0..n]
1244 .iter()
1245 .map(|x| format!("[{}]({})", x, qp.dq(*x)))
1246 .join(", ")
1247 } else {
1248 tensor.as_slice_unchecked::<D>()[0..n].iter().join(", ")
1249 }
1250 }
1251 }
1252 unsafe {
1253 let trunc = self.len() > 12 && !force_full;
1254 let data = dispatch_datum!(dump_t(self.datum_type())(
1255 self,
1256 if trunc { 12 } else { self.len() }
1257 ));
1258 Ok(format!(
1259 "{},{:?} {}{}",
1260 self.shape.iter().join(","),
1261 self.dt,
1262 data,
1263 if trunc { "..." } else { "" }
1264 ))
1265 }
1266 }
1267
1268 pub fn close_enough(
1270 &self,
1271 other: &Self,
1272 approx: impl Into<Approximation> + std::fmt::Debug,
1273 ) -> TractResult<()> {
1274 let approx = approx.into();
1275 if self.shape() != other.shape() {
1276 bail!("Shape mismatch {:?} != {:?}", self.shape(), other.shape())
1277 }
1278 if let Approximation::Ulp(max_ulp) = approx {
1279 return self.ulp_close_enough(other, max_ulp);
1280 }
1281 let (atol, rtol, outliers) = approx.atol_rtol_outliers(&self.datum_type());
1282 let ma = self.cast_to::<f32>()?;
1283 let ma = ma.to_plain_array_view::<f32>()?;
1284 let mb = other.cast_to::<f32>()?;
1285 let mb = mb.to_plain_array_view::<f32>()?;
1286 let mut first_outlier = None;
1287 let mut outliers_count = 0;
1288 ndarray::indices_of(&ma).into_iter().for_each(|indices| {
1289 let a = ma[&indices];
1290 let b = mb[&indices];
1291 if !((a.is_nan() && b.is_nan())
1292 || (a.is_infinite() && b.is_infinite() && a.signum() == b.signum())
1293 || (a - b).abs() <= atol as f32 + rtol as f32 * b.abs())
1294 {
1295 if outliers_count == 0 {
1296 first_outlier = Some(indices.as_array_view().to_vec());
1297 }
1298 outliers_count += 1;
1299 }
1300 });
1301 if self.volume() > 0 && outliers_count as f64 / self.volume() as f64 > outliers {
1302 let indices = first_outlier.unwrap();
1303 let a = ma[&*indices];
1304 let b = mb[&*indices];
1305 let ulp = self
1306 .max_ulp_distance(other)
1307 .map(|(d, _)| format!("{d}"))
1308 .unwrap_or_else(|_| "n/a".to_string());
1309 bail!(
1310 "Mismatch. First outlier: {:?} for {:?}) at {:?} {} != {}. Outliers: {} / {} = {:0.5} > {:0.5}. Max ULP ({:?}): {}.",
1311 approx,
1312 self.datum_type(),
1313 indices,
1314 a,
1315 b,
1316 outliers_count,
1317 self.volume(),
1318 outliers_count as f64 / self.volume() as f64,
1319 outliers,
1320 self.ulp_comparison_dt(),
1321 ulp,
1322 );
1323 }
1324 Ok(())
1325 }
1326
1327 pub fn ulp_comparison_dt(&self) -> DatumType {
1333 match self.datum_type() {
1334 dt @ (DatumType::F16 | DatumType::F32 | DatumType::F64) => dt,
1335 _ => DatumType::F32,
1336 }
1337 }
1338
1339 pub fn max_ulp_distance(&self, other: &Self) -> TractResult<(u64, Option<usize>)> {
1345 if self.shape() != other.shape() {
1346 bail!("Shape mismatch {:?} != {:?}", self.shape(), other.shape())
1347 }
1348 let dt = self.ulp_comparison_dt();
1349 let a = self.cast_to_dt(dt)?;
1350 let b = other.cast_to_dt(dt)?;
1351 fn worst<D: Datum + crate::ulp::UlpFloat>(
1352 a: &Tensor,
1353 b: &Tensor,
1354 ) -> TractResult<(u64, Option<usize>)> {
1355 let a = a.to_plain_array_view::<D>()?;
1356 let b = b.to_plain_array_view::<D>()?;
1357 Ok(crate::ulp::max_ulp_distance(a.iter().copied(), b.iter().copied()))
1358 }
1359 match dt {
1360 DatumType::F16 => worst::<f16>(&a, &b),
1361 DatumType::F32 => worst::<f32>(&a, &b),
1362 DatumType::F64 => worst::<f64>(&a, &b),
1363 dt => bail!("No ULP comparison for {dt:?}"),
1364 }
1365 }
1366
1367 fn ulp_close_enough(&self, other: &Self, max_ulp: u64) -> TractResult<()> {
1370 let (worst, at) = self.max_ulp_distance(other)?;
1371 if worst <= max_ulp {
1372 return Ok(());
1373 }
1374 let dt = self.ulp_comparison_dt();
1375 let indices = at
1376 .map(|flat| {
1377 let mut rest = flat;
1378 let mut indices = vec![0; self.rank()];
1379 for (ix, dim) in self.shape().iter().enumerate().rev() {
1380 indices[ix] = rest % dim;
1381 rest /= dim;
1382 }
1383 format!("{indices:?}")
1384 })
1385 .unwrap_or_else(|| "?".to_string());
1386 let a = self.cast_to::<f64>()?;
1387 let b = other.cast_to::<f64>()?;
1388 let (a, b) = (a.to_plain_array_view::<f64>()?, b.to_plain_array_view::<f64>()?);
1389 let flat = at.unwrap_or(0);
1390 bail!(
1391 "Mismatch. Max ULP distance ({dt:?}): {} > {}, at {} ({} != {}).",
1392 worst,
1393 max_ulp,
1394 indices,
1395 a.iter().nth(flat).copied().unwrap_or(f64::NAN),
1396 b.iter().nth(flat).copied().unwrap_or(f64::NAN),
1397 );
1398 }
1399
1400 pub fn into_plain_array<D: Datum>(self) -> TractResult<ArrayD<D>> {
1402 Ok(self.to_plain_array_view::<D>()?.to_owned())
1403 }
1404
1405 pub unsafe fn into_array_unchecked<D: Datum>(self) -> ArrayD<D> {
1407 unsafe { self.to_array_view_unchecked::<D>().to_owned() }
1408 }
1409
1410 #[inline]
1414 pub fn to_plain_array_view<D: Datum>(&self) -> TractResult<ArrayViewD<'_, D>> {
1415 self.try_as_plain_ram()?.to_array_view::<D>()
1416 }
1417
1418 #[inline]
1422 pub fn to_plain_array_view_mut<D: Datum>(&mut self) -> TractResult<ArrayViewMutD<'_, D>> {
1423 self.check_for_access::<D>()?;
1424 ensure!(self.storage.as_plain_ram_mut().is_some(), "Tensor storage is not plain");
1425 unsafe { Ok(self.to_array_view_mut_unchecked()) }
1426 }
1427
1428 fn check_for_access<D: Datum>(&self) -> TractResult<()> {
1429 ensure!(
1430 self.datum_type().unquantized() == D::datum_type().unquantized(),
1431 "Tensor datum type error: tensor is {:?}, accessed as {:?}",
1432 self.datum_type(),
1433 D::datum_type(),
1434 );
1435 Ok(())
1436 }
1437
1438 pub unsafe fn to_array_view_unchecked<D: Datum>(&self) -> ArrayViewD<'_, D> {
1440 if self.len() != 0 {
1441 unsafe {
1442 ArrayViewD::from_shape_ptr(
1443 &*self.shape,
1444 self.plain_ram_storage().as_ptr() as *const D,
1445 )
1446 }
1447 } else {
1448 ArrayViewD::from_shape(&*self.shape, &[]).unwrap()
1449 }
1450 }
1451
1452 pub unsafe fn to_array_view_mut_unchecked<D: Datum>(&mut self) -> ArrayViewMutD<'_, D> {
1454 if self.len() != 0 {
1455 unsafe {
1456 let ptr = self.plain_ram_storage_mut().as_mut_ptr() as *mut D;
1457 ArrayViewMutD::from_shape_ptr(&*self.shape, ptr)
1458 }
1459 } else {
1460 ArrayViewMutD::from_shape(&*self.shape, &mut []).unwrap()
1461 }
1462 }
1463
1464 pub fn as_ptr<D: Datum>(&self) -> TractResult<*const D> {
1466 self.check_for_access::<D>()?;
1467 Ok(self.plain_ram_storage().as_ptr() as *const D)
1468 }
1469
1470 pub unsafe fn as_ptr_unchecked<D: Datum>(&self) -> *const D {
1472 self.plain_ram_storage().as_ptr() as *const D
1473 }
1474
1475 pub unsafe fn as_ptr_mut_unchecked<D: Datum>(&mut self) -> *mut D {
1477 self.plain_ram_storage_mut().as_mut_ptr() as *mut D
1478 }
1479
1480 pub fn as_ptr_mut<D: Datum>(&mut self) -> TractResult<*mut D> {
1482 self.as_ptr::<D>().map(|p| p as *mut D)
1483 }
1484
1485 pub unsafe fn as_slice_unchecked<D: Datum>(&self) -> &[D] {
1487 if self.storage.byte_len() == 0 {
1488 &[]
1489 } else {
1490 unsafe { std::slice::from_raw_parts::<D>(self.as_ptr_unchecked(), self.len()) }
1491 }
1492 }
1493
1494 pub unsafe fn as_slice_mut_unchecked<D: Datum>(&mut self) -> &mut [D] {
1496 if self.storage.byte_len() == 0 {
1497 &mut []
1498 } else {
1499 unsafe { std::slice::from_raw_parts_mut::<D>(self.as_ptr_mut_unchecked(), self.len()) }
1500 }
1501 }
1502
1503 pub fn to_scalar_tensor(&self) -> TractResult<Tensor> {
1505 fn to_scalar_tensor_t<D: Datum>(t: &Tensor) -> TractResult<Tensor> {
1506 Ok(litteral::tensor0(t.try_as_plain_ram()?.to_scalar::<D>()?.clone()))
1507 }
1508 dispatch_datum!(to_scalar_tensor_t(self.datum_type())(self))
1509 }
1510
1511 pub unsafe fn to_scalar_unchecked<D: Datum>(&self) -> &D {
1513 unsafe { &*(self.plain_ram_storage().as_ptr() as *const D) }
1514 }
1515
1516 pub fn to_scalar_mut<D: Datum>(&mut self) -> TractResult<&mut D> {
1518 self.check_for_access::<D>()?;
1519 if self.len() == 0 {
1520 bail!("to_scalar_mut called on empty tensor ({:?})", self)
1521 }
1522 if self.len() > 1 {
1523 bail!("to_scalar called on a tensor with multiple values ({:?})", self)
1524 }
1525 unsafe { Ok(self.to_scalar_mut_unchecked()) }
1526 }
1527
1528 pub unsafe fn to_scalar_mut_unchecked<D: Datum>(&mut self) -> &mut D {
1530 unsafe { &mut *(self.plain_ram_storage_mut().as_mut_ptr() as *mut D) }
1531 }
1532
1533 pub fn as_bytes(&self) -> &[u8] {
1534 self.plain_ram_storage().as_bytes()
1535 }
1536
1537 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
1538 self.plain_ram_storage_mut().as_bytes_mut()
1539 }
1540
1541 unsafe fn is_uniform_t<T: Datum>(&self) -> bool {
1542 let slice = unsafe { self.as_slice_unchecked::<T>() };
1543 slice[1..].iter().all(|x| x == &slice[0])
1544 }
1545
1546 pub fn is_uniform(&self) -> bool {
1547 if self.as_plain_ram_storage().is_none() {
1548 return false;
1549 }
1550 if self.len() <= 1 {
1551 return true;
1552 }
1553 unsafe { dispatch_datum!(Tensor::is_uniform_t(self.datum_type())(self)) }
1554 }
1555
1556 unsafe fn as_uniform_t<T: Datum>(&self) -> Tensor {
1557 let v: T = unsafe { self.as_slice_unchecked::<T>() }[0].clone();
1558 litteral::tensor0(v)
1559 }
1560
1561 pub fn as_uniform(&self) -> Option<Tensor> {
1562 if self.len() >= 1 && self.is_uniform() {
1563 unsafe {
1564 let mut t = dispatch_datum!(Tensor::as_uniform_t(self.datum_type())(self));
1565 t.set_datum_type(self.datum_type());
1566 Some(t)
1567 }
1568 } else {
1569 None
1570 }
1571 }
1572
1573 pub fn is_all_zero(&self) -> TractResult<bool> {
1574 Ok(self.len() == 0 || self.as_uniform().map(|t| t.is_zero().unwrap()).unwrap_or(false))
1575 }
1576
1577 pub fn is_zero(&self) -> TractResult<bool> {
1578 Ok(self == &Tensor::zero_scalar_dt(self.dt)?)
1579 }
1580
1581 unsafe fn natural_cast<
1582 Source: Datum + num_traits::AsPrimitive<Target>,
1583 Target: Datum + Copy,
1584 >(
1585 &self,
1586 other: &mut Tensor,
1587 ) {
1588 unsafe {
1589 self.as_slice_unchecked::<Source>()
1590 .iter()
1591 .zip(other.as_slice_mut_unchecked::<Target>().iter_mut())
1592 .for_each(|(s, d)| *d = s.as_())
1593 };
1594 }
1595
1596 unsafe fn cast_number_to_bool<Source: Datum + num_traits::Zero>(&self, other: &mut Tensor) {
1597 unsafe {
1598 self.as_slice_unchecked::<Source>()
1599 .iter()
1600 .zip(other.as_slice_mut_unchecked::<bool>().iter_mut())
1601 .for_each(|(s, d)| *d = !s.is_zero());
1602 }
1603 }
1604
1605 unsafe fn cast_from_string<Target: Datum + core::str::FromStr>(
1606 &self,
1607 other: &mut Tensor,
1608 ) -> TractResult<()> {
1609 unsafe {
1610 for (s, d) in self
1611 .as_slice_unchecked::<String>()
1612 .iter()
1613 .zip(other.as_slice_mut_unchecked::<Target>().iter_mut())
1614 {
1615 *d = s
1616 .parse()
1617 .map_err(|_| format_err!("Can not parse as {:?}", Target::datum_type()))?;
1618 }
1619 Ok(())
1620 }
1621 }
1622
1623 unsafe fn cast_to_string<Source: Datum>(&self, other: &mut Tensor) {
1624 unsafe {
1625 for (s, d) in self
1626 .as_slice_unchecked::<Source>()
1627 .iter()
1628 .zip(other.as_slice_mut_unchecked::<String>().iter_mut())
1629 {
1630 *d = s.to_string()
1631 }
1632 }
1633 }
1634
1635 pub fn cast_to<D: Datum>(&self) -> TractResult<Cow<'_, Tensor>> {
1637 self.cast_to_dt(D::datum_type())
1638 }
1639
1640 #[allow(clippy::redundant_closure_call)]
1642 pub fn cast_to_dt(&self, dst_dt: DatumType) -> TractResult<Cow<'_, Tensor>> {
1643 unsafe {
1644 if self.dt == dst_dt {
1645 return Ok(Cow::Borrowed(self));
1646 }
1647 if self.dt == TDim::datum_type() && (dst_dt.is_integer() || dst_dt.is_float()) {
1648 let slice = self.as_slice_unchecked::<TDim>();
1649 let mut ints = Self::uninitialized::<i64>(&self.shape)?;
1650 let ints_slice = ints.as_slice_mut_unchecked::<i64>();
1651 for i in 0..self.len() {
1652 ints_slice[i] = slice[i].to_i64()?;
1653 }
1654 return Ok(Cow::Owned(ints.cast_to_dt(dst_dt)?.into_owned()));
1655 }
1656 if self.dt == bool::datum_type()
1657 && (dst_dt.is_integer() || dst_dt.is_float() || dst_dt == TDim::datum_type())
1658 {
1659 let slice = self.as_slice_unchecked::<bool>();
1660 let mut ints = Self::uninitialized::<i8>(&self.shape)?;
1661 let ints_slice = ints.as_slice_mut_unchecked::<i8>();
1662 for i in 0..self.len() {
1663 ints_slice[i] = slice[i] as usize as i8;
1664 }
1665 return Ok(Cow::Owned(ints.cast_to_dt(dst_dt)?.into_owned()));
1666 }
1667 let mut result = Self::uninitialized_dt(dst_dt, &self.shape)?;
1668 if self.dt == DatumType::String {
1669 dispatch_numbers!(Self::cast_from_string(dst_dt)(self, &mut result))?;
1670 return Ok(Cow::Owned(result));
1671 }
1672 if dst_dt == DatumType::String {
1673 dispatch_datum!(Self::cast_to_string(self.dt)(self, &mut result));
1674 return Ok(Cow::Owned(result));
1675 }
1676 macro_rules! n {
1677 ($source:ty) => {
1678 if <$source>::datum_type() == self.datum_type() {
1679 match dst_dt {
1680 DatumType::I8 => self.natural_cast::<$source, i8>(&mut result),
1681 DatumType::I16 => self.natural_cast::<$source, i16>(&mut result),
1682 DatumType::I32 => self.natural_cast::<$source, i32>(&mut result),
1683 DatumType::I64 => self.natural_cast::<$source, i64>(&mut result),
1684 DatumType::U8 => self.natural_cast::<$source, u8>(&mut result),
1685 DatumType::U16 => self.natural_cast::<$source, u16>(&mut result),
1686 DatumType::U32 => self.natural_cast::<$source, u32>(&mut result),
1687 DatumType::U64 => self.natural_cast::<$source, u64>(&mut result),
1688 DatumType::F16 => self.natural_cast::<$source, f16>(&mut result),
1689 DatumType::F32 => self.natural_cast::<$source, f32>(&mut result),
1690 DatumType::F64 => self.natural_cast::<$source, f64>(&mut result),
1691 DatumType::TDim => {
1692 let ints = self.cast_to::<i32>()?;
1693 let slice = ints.as_slice_unchecked::<i32>();
1694 let result = result.as_slice_mut_unchecked::<TDim>();
1695 for i in 0..self.len() {
1696 result[i] = slice[i].into();
1697 }
1698 }
1699 DatumType::Bool => self.cast_number_to_bool::<$source>(&mut result),
1700 _ => todo!(),
1701 }
1702 return Ok(Cow::Owned(result));
1703 };
1704 };
1705 }
1706 if !dst_dt.is_quantized() && !self.datum_type().is_quantized() {
1708 n!(u8);
1709 n!(u16);
1710 n!(u32);
1711 n!(u64);
1712 n!(i8);
1713 n!(i16);
1714 n!(i32);
1715 n!(i64);
1716 n!(f16);
1717 n!(f32);
1718 n!(f64);
1719 } else {
1720 let (s_zp, s_scale) = self.datum_type().zp_scale();
1721 let (d_zp, d_scale) = dst_dt.zp_scale();
1722 if self.datum_type().is_quantized() && dst_dt.is_float() {
1723 macro_rules! q_to_fp {
1724 ($source:ty, $dest:ty) => {
1725 if <$source>::datum_type().unquantized()
1726 == self.datum_type().unquantized()
1727 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
1728 {
1729 self.as_slice_unchecked::<$source>()
1730 .iter()
1731 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
1732 .for_each(|(&s, d)| {
1733 *d = (s as $dest - s_zp as $dest) * s_scale as $dest;
1734 });
1735 return Ok(Cow::Owned(result));
1736 }
1737 };
1738 }
1739 q_to_fp!(i8, f64);
1740 q_to_fp!(i8, f32);
1741 q_to_fp!(u8, f64);
1742 q_to_fp!(u8, f32);
1743 }
1744 macro_rules! q8_to_q8 {
1746 ($typ:ty) => {
1747 if dst_dt.unquantized() == <$typ>::datum_type() {
1748 self.as_slice_unchecked::<$typ>()
1749 .iter()
1750 .zip(result.as_slice_mut_unchecked::<$typ>().iter_mut())
1751 .for_each(|(&s, d)| {
1752 *d = (d_zp as i32
1753 + scale_by(s as i32 - s_zp as i32, s_scale / d_scale))
1754 .clamp_cast()
1755 });
1756 return Ok(Cow::Owned(result));
1757 }
1758 };
1759 }
1760
1761 macro_rules! q_via_f32 {
1762 ($source:ty, $dest:ty, $round:expr) => {
1763 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
1764 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
1765 {
1766 self.as_slice_unchecked::<$source>()
1767 .iter()
1768 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
1769 .for_each(|(&s, d)| {
1770 let s_float = (s as f32 - s_zp as f32) * s_scale as f32;
1771 let d_float = s_float as f32 / d_scale as f32 + d_zp as f32;
1772 *d = $round(d_float);
1773 });
1774 return Ok(Cow::Owned(result));
1775 }
1776 };
1777 }
1778
1779 macro_rules! q_n {
1780 (clamp $source:ty, $dest:ty) => {{
1781 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
1782 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
1783 {
1784 self.as_slice_unchecked::<$source>()
1785 .iter()
1786 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
1787 .for_each(|(&s, d)| {
1788 *d = s.clamp_cast();
1789 });
1790 return Ok(Cow::Owned(result));
1791 }
1792 }};
1793 ($source:ty, $dest:ty) => {{
1794 if <$source>::datum_type().unquantized() == self.datum_type().unquantized()
1795 && <$dest>::datum_type().unquantized() == dst_dt.unquantized()
1796 {
1797 self.as_slice_unchecked::<$source>()
1798 .iter()
1799 .zip(result.as_slice_mut_unchecked::<$dest>().iter_mut())
1800 .for_each(|(&s, d)| {
1801 *d = s as $dest;
1802 });
1803 return Ok(Cow::Owned(result));
1804 }
1805 }};
1806 }
1807
1808 if dst_dt.unquantized() == self.datum_type().unquantized()
1809 && dst_dt.is_quantized()
1810 && self.datum_type().is_quantized()
1811 {
1812 q8_to_q8!(i8);
1813 q8_to_q8!(u8);
1814 }
1815
1816 q_via_f32!(f32, i8, |f| round_ties_to_even(f).clamp_cast());
1817 q_via_f32!(f32, u8, |f| round_ties_to_even(f).clamp_cast());
1818 q_via_f32!(f32, i32, |f| round_ties_to_even(f).clamp_cast());
1819 q_via_f32!(i8, f32, |f| f);
1820 q_via_f32!(u8, f32, |f| f);
1821 q_via_f32!(i32, f32, |f| f);
1822
1823 if dst_dt.is_quantized() && self.datum_type().is_quantized() {
1824 q_via_f32!(u8, i8, |f| round_ties_to_even(f).clamp_cast());
1825 q_via_f32!(i8, u8, |f| round_ties_to_even(f).clamp_cast());
1826 q_via_f32!(i32, u8, |f| round_ties_to_even(f).clamp_cast());
1827 q_via_f32!(i32, i8, |f| round_ties_to_even(f).clamp_cast());
1828 q_via_f32!(u8, i32, |f| round_ties_to_even(f).clamp_cast());
1829 q_via_f32!(i8, i32, |f| round_ties_to_even(f).clamp_cast());
1830
1831 q_via_f32!(i8, i8, |f| round_ties_to_even(f).clamp_cast());
1833 q_via_f32!(u8, u8, |f| round_ties_to_even(f).clamp_cast());
1834 }
1835
1836 q_n!(i8, i32);
1837 q_n!(i8, u32);
1838 q_n!(u8, i32);
1839 q_n!(u8, u32);
1840 q_n!(clamp i32, i8);
1841 q_n!(clamp i32, u8);
1842 q_n!(clamp u32, i8);
1843 q_n!(clamp u32, u8);
1844 q_n!(i8, i8);
1845 q_n!(u8, u8);
1846 q_n!(i32, i32);
1847 q_n!(u32, u32);
1848 }
1849
1850 bail!("Unsupported cast from {:?} to {:?}", self.dt, dst_dt)
1851 }
1852 }
1853
1854 pub fn cast_to_scalar<D: Datum + Copy>(&self) -> TractResult<D> {
1856 let casted = self.cast_to::<D>()?;
1857 casted.try_as_plain_ram()?.to_scalar::<D>().copied()
1858 }
1859
1860 pub fn nth(&self, nth: usize) -> TractResult<Tensor> {
1862 if nth >= self.len() {
1863 bail!(
1864 "nth called with {}th element on a tensor of len {} ({:?}",
1865 nth,
1866 self.len(),
1867 self
1868 );
1869 }
1870 unsafe fn nth_t<T: Datum>(me: &Tensor, nth: usize, output: &mut Tensor) {
1871 unsafe {
1872 let value = me.as_slice_unchecked::<T>()[nth].clone();
1873 std::ptr::write(output.as_slice_mut_unchecked::<T>().as_mut_ptr(), value);
1874 }
1875 }
1876 unsafe {
1877 let mut output = Tensor::uninitialized_dt(self.datum_type(), &[])?;
1878 dispatch_datum_by_size!(nth_t(self.datum_type())(self, nth, &mut output));
1879 Ok(output)
1880 }
1881 }
1882
1883 fn eq_dt(&self, other: &Tensor) -> TractResult<bool> {
1885 unsafe fn eq_t<D: Datum>(me: &Tensor, other: &Tensor) -> TractResult<bool> {
1886 unsafe {
1887 if D::datum_type().is_float() {
1888 return dispatch_floatlike!(float_eq_t(D::datum_type())(me, other));
1889 }
1890 Ok(izip!(me.as_slice_unchecked::<D>(), other.as_slice_unchecked::<D>())
1891 .all(|(a, b)| a == b))
1892 }
1893 }
1894
1895 unsafe fn float_eq_t<D: Datum + Float>(me: &Tensor, other: &Tensor) -> TractResult<bool> {
1896 unsafe {
1897 Ok(izip!(me.as_slice_unchecked::<D>(), other.as_slice_unchecked::<D>())
1898 .all(|(a, b)| (a.is_nan() && b.is_nan()) || a == b))
1899 }
1900 }
1901
1902 unsafe {
1903 Ok(self.datum_type() == other.datum_type()
1904 && self.shape() == other.shape()
1905 && dispatch_datum!(eq_t(self.dt)(self, other))?)
1906 }
1907 }
1908
1909 fn from_datum<T: Datum>(mut it: ArrayD<T>) -> Tensor {
1910 unsafe {
1911 let mut t = Self::uninitialized::<T>(it.shape()).unwrap();
1912 if let Some(slice) = it.as_slice_mut() {
1913 if t.datum_type().is_copy() {
1914 std::ptr::copy_nonoverlapping(
1915 slice.as_ptr() as *const i8,
1916 t.as_ptr_mut_unchecked(),
1917 t.plain_ram_storage().layout().size(),
1918 );
1919 } else {
1920 t.as_slice_mut_unchecked::<T>()
1921 .iter_mut()
1922 .zip(slice.iter_mut())
1923 .for_each(|(t, s)| *t = std::mem::take(s));
1924 }
1925 return t;
1926 }
1927 if it.strides().iter().all(|&s| s > 0) && it.as_slice_memory_order().is_some() {
1928 let mut len_and_strides: TVec<(usize, usize)> = tvec!();
1929 for (len, stride) in itertools::izip!(it.shape(), it.strides(), t.strides())
1930 .sorted_by_key(|(_, src, _)| *src)
1931 .map(|(l, _, dst)| (*l as isize, *dst))
1932 {
1933 if !len_and_strides.is_empty()
1934 && len_and_strides.last().unwrap().1 * len_and_strides.last().unwrap().0
1935 == stride as usize
1936 {
1937 len_and_strides.last_mut().unwrap().0 *= len as usize;
1938 } else {
1939 len_and_strides.push((len as usize, stride as usize));
1940 }
1941 }
1942 len_and_strides.reverse();
1943 crate::scatter::scatter_contig_data(
1944 it.as_ptr(),
1945 t.as_ptr_mut_unchecked(),
1946 &len_and_strides,
1947 );
1948 return t;
1949 }
1950 t.as_slice_mut_unchecked().iter_mut().zip(it).for_each(|(t, a)| *t = a);
1952 t
1953 }
1954 }
1955
1956 pub fn deep_clone(&self) -> Tensor {
1957 if self.as_plain_ram_storage().is_none() {
1958 return Tensor {
1959 dt: self.dt,
1960 shape: self.shape.clone(),
1961 strides: self.strides.clone(),
1962 len: self.len,
1963 storage: self.storage.deep_clone(),
1964 };
1965 }
1966 unsafe {
1967 let mut tensor = Tensor::uninitialized_dt(self.datum_type(), self.shape()).unwrap();
1968 if self.len() > 0 {
1969 if self.dt.is_copy() {
1970 self.plain_ram_storage().as_ptr().copy_to_nonoverlapping(
1971 tensor.as_bytes_mut().as_mut_ptr(),
1972 self.plain_ram_storage().layout().size(),
1973 )
1974 } else if self.dt == DatumType::String {
1975 tensor
1976 .as_slice_mut_unchecked::<String>()
1977 .clone_from_slice(self.as_slice_unchecked());
1978 } else if self.dt == DatumType::Blob {
1979 tensor
1980 .as_slice_mut_unchecked::<Blob>()
1981 .clone_from_slice(self.as_slice_unchecked());
1982 } else if self.dt == DatumType::TDim {
1983 tensor
1984 .as_slice_mut_unchecked::<TDim>()
1985 .clone_from_slice(self.as_slice_unchecked());
1986 }
1987 }
1988 tensor
1989 }
1990 }
1991
1992 pub fn slice(&self, axis: usize, start: usize, end: usize) -> TractResult<Tensor> {
1993 if axis >= self.rank() {
1994 bail!("Can not slice at axis {} tensor {:?}", axis, self);
1995 }
1996 if start > self.shape[axis] || end > self.shape[axis] || start >= end {
1997 bail!("Invalid slicing range {start}..{end} on axis {axis} for {self:?}");
1998 }
1999 if let Some(sliced) =
2002 self.storage.as_storage().slice(self.dt, self.shape(), axis, start, end)?
2003 {
2004 return Ok(sliced);
2005 }
2006 let mut shape: TVec<usize> = self.shape().into();
2007 shape[axis] = end - start;
2008 unsafe {
2009 let mut tensor = Tensor::uninitialized_dt(self.datum_type(), &shape)?;
2010 tensor.assign_slice_from_resolved(&[], 0..end - start, self, &[], start..end, axis);
2011 Ok(tensor)
2012 }
2013 }
2014
2015 #[inline]
2016 pub fn view(&self) -> view::TensorView<'_> {
2017 unsafe { view::TensorView::view(self) }
2018 }
2019
2020 #[inline]
2021 pub fn view_at_prefix(&self, prefix: &[usize]) -> TractResult<view::TensorView<'_>> {
2022 view::TensorView::at_prefix(self, prefix)
2023 }
2024
2025 #[inline]
2026 pub fn view_offsetting(&self, coords: &[usize]) -> TractResult<view::TensorView<'_>> {
2027 view::TensorView::offsetting(self, coords)
2028 }
2029
2030 #[inline]
2031 pub unsafe fn view_offsetting_unchecked(&self, coords: &[usize]) -> view::TensorView<'_> {
2032 unsafe { view::TensorView::offsetting_unchecked(self, coords) }
2033 }
2034
2035 #[inline]
2036 pub fn view_mut(&mut self) -> view::TensorView<'_> {
2037 unsafe { view::TensorView::view(self) }
2038 }
2039
2040 #[inline]
2041 pub fn view_at_prefix_mut(&mut self, prefix: &[usize]) -> TractResult<view::TensorView<'_>> {
2042 view::TensorView::at_prefix(self, prefix)
2043 }
2044
2045 #[inline]
2046 pub fn view_offsetting_mut(&mut self, coords: &[usize]) -> TractResult<view::TensorView<'_>> {
2047 view::TensorView::offsetting(self, coords)
2048 }
2049
2050 pub fn offset_u8_as_i8(self: &Arc<Self>) -> Arc<Self> {
2052 let mut t = if let DatumType::U8 = self.dt.unquantized() {
2053 self.try_as_plain_ram()
2054 .unwrap()
2055 .to_array_view::<u8>()
2056 .unwrap()
2057 .mapv(|v| v.wrapping_sub(128) as i8)
2058 .into_tensor()
2059 } else {
2060 return self.clone();
2061 };
2062
2063 if let DatumType::QU8(qp) = self.dt {
2064 if let QParams::ZpScale { zero_point, scale } = qp {
2065 t.dt = DatumType::QI8(QParams::ZpScale { zero_point: zero_point - 128, scale });
2066 } else {
2067 t.dt = DatumType::QI8(qp);
2068 }
2069 }
2070
2071 t.into_arc_tensor()
2072 }
2073
2074 pub fn offset_i8_as_u8(self: &Arc<Self>) -> Arc<Self> {
2076 let mut t = if let DatumType::I8 = self.dt.unquantized() {
2077 self.try_as_plain_ram()
2078 .unwrap()
2079 .to_array_view::<i8>()
2080 .unwrap()
2081 .mapv(|v| (v as u8).wrapping_add(128))
2082 .into_tensor()
2083 } else {
2084 return self.clone();
2085 };
2086
2087 if let DatumType::QI8(qp) = self.dt {
2088 if let QParams::ZpScale { zero_point, scale } = qp {
2089 t.dt = DatumType::QU8(QParams::ZpScale { zero_point: zero_point + 128, scale });
2090 } else {
2091 t.dt = DatumType::QU8(qp);
2092 }
2093 }
2094 t.into_arc_tensor()
2095 }
2096
2097 pub fn to_aligned_default(&self) -> TractResult<Self> {
2098 if self.dt.is_copy() {
2099 unsafe {
2100 let mut t = Self::uninitialized_dt(self.dt, &self.shape)?;
2101 t.as_bytes_mut().copy_from_slice(self.as_bytes());
2102 Ok(t)
2103 }
2104 } else {
2105 let mut t = Self::zero_dt(self.dt, &self.shape)?;
2106 if self.dt == String::datum_type() {
2107 t.try_as_plain_ram_mut()?
2108 .as_slice_mut::<String>()?
2109 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2110 } else if self.dt == Blob::datum_type() {
2111 t.try_as_plain_ram_mut()?
2112 .as_slice_mut::<Blob>()?
2113 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2114 } else if self.dt == TDim::datum_type() {
2115 t.try_as_plain_ram_mut()?
2116 .as_slice_mut::<TDim>()?
2117 .clone_from_slice(self.try_as_plain_ram()?.as_slice()?);
2118 }
2119 Ok(t)
2120 }
2121 }
2122
2123 pub fn natural_strides(shape: &[usize]) -> TVec<isize> {
2124 let mut strides = tvec!();
2125 compute_natural_stride_to(&mut strides, shape);
2126 strides
2127 }
2128
2129 #[inline]
2133 pub fn has_plain_ram_storage(&self) -> bool {
2134 matches!(self.storage, StorageKind::Plain(_))
2135 }
2136
2137 pub fn into_plain_ram(mut self) -> TractResult<Tensor> {
2141 if self.has_plain_ram_storage() {
2142 return Ok(self);
2143 }
2144 ensure!(self.dt.is_copy());
2145 let storage =
2146 std::mem::replace(&mut self.storage, StorageKind::Plain(PlainStorage::default()));
2147 let storage = storage.into_plain_ram().context("Storage can not produce plain bytes")?;
2148 Ok(Tensor {
2149 dt: self.dt,
2150 shape: self.shape.clone(),
2151 strides: self.strides.clone(),
2152 len: self.len,
2153 storage: StorageKind::Plain(storage),
2154 })
2155 }
2156
2157 pub fn into_blob(mut self) -> TractResult<Blob> {
2158 ensure!(self.dt.is_copy());
2159 let storage =
2160 std::mem::replace(&mut self.storage, StorageKind::Plain(PlainStorage::default()));
2161 Ok(storage.into_plain_ram().context("Storage is not plain")?.into_blob())
2162 }
2163}
2164
2165impl PartialEq for Tensor {
2166 fn eq(&self, other: &Tensor) -> bool {
2167 if self.dt != other.dt || self.shape != other.shape {
2168 return false;
2169 }
2170 match (self.storage.as_plain_ram(), other.storage.as_plain_ram()) {
2171 (Some(_), Some(_)) => self.eq_dt(other).unwrap_or(false),
2172 (None, None) => self.storage == other.storage,
2173 _ => false,
2174 }
2175 }
2176}
2177
2178impl Eq for Tensor {}
2179
2180impl fmt::Debug for Tensor {
2181 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2182 let content = self.dump(false).unwrap_or_else(|e| format!("Error : {e:?}"));
2183 write!(formatter, "{content}")
2184 }
2185}
2186
2187#[cfg(feature = "complex")]
2188pub fn reinterpret_inner_dim_as_complex(mut t: Tensor) -> TractResult<Tensor> {
2189 ensure!(
2190 t.shape().last() == Some(&2),
2191 "The last dimension in the tensor shape {:?} must be 2",
2192 t.shape()
2193 );
2194 unsafe {
2195 t.shape.pop();
2196 t.set_datum_type(t.datum_type().complexify()?);
2197 t.update_strides_and_len();
2198 Ok(t)
2199 }
2200}
2201
2202#[cfg(feature = "complex")]
2203pub fn reinterpret_complex_as_inner_dim(mut t: Tensor) -> TractResult<Tensor> {
2204 unsafe {
2205 t.shape.push(2);
2206 t.set_datum_type(t.datum_type().decomplexify()?);
2207 t.update_strides_and_len();
2208 Ok(t)
2209 }
2210}
2211
2212pub fn clip_range_bounds(len: usize, range: impl std::ops::RangeBounds<usize>) -> Range<usize> {
2213 use std::ops::Bound;
2214 let start = match range.start_bound() {
2215 Bound::Included(ix) => *ix,
2216 Bound::Excluded(ix) => ix + 1,
2217 Bound::Unbounded => 0,
2218 };
2219 let end = match range.end_bound() {
2220 Bound::Included(ix) => *ix + 1,
2221 Bound::Excluded(ix) => *ix,
2222 Bound::Unbounded => len,
2223 };
2224 start..end
2225}
2226
2227pub fn natural_strides(shape: &[usize]) -> TVec<isize> {
2228 let mut strides = tvec!();
2229 compute_natural_stride_to(&mut strides, shape);
2230 strides
2231}
2232
2233fn compute_natural_stride_to(strides: &mut TVec<isize>, shape: &[usize]) {
2234 match shape.len() {
2235 0 => (),
2236 1 => strides.push(1),
2237 2 => strides.extend_from_slice(&[shape[1] as isize, 1]),
2238 3 => strides.extend_from_slice(&[(shape[1] * shape[2]) as isize, shape[2] as _, 1]),
2239 4 => strides.extend_from_slice(&[
2240 (shape[1] * shape[2] * shape[3]) as isize,
2241 (shape[2] * shape[3]) as _,
2242 shape[3] as _,
2243 1,
2244 ]),
2245 _ => {
2246 strides.push(1);
2247 for dim in shape.as_ref().iter().skip(1).rev() {
2248 let previous = *strides.last().unwrap();
2249 strides.push(previous * *dim as isize)
2250 }
2251 strides.reverse();
2252 }
2253 }
2254}
2255
2256impl<D: ::ndarray::Dimension, T: Datum> From<Array<T, D>> for Tensor {
2257 fn from(it: Array<T, D>) -> Tensor {
2258 Tensor::from_datum(it.into_dyn())
2259 }
2260}
2261
2262pub trait IntoTensor: Sized {
2264 fn into_tensor(self) -> Tensor;
2268}
2269
2270pub trait IntoArcTensor: Sized {
2272 fn into_arc_tensor(self) -> Arc<Tensor>;
2276}
2277
2278impl<D: ::ndarray::Dimension, T: Datum> IntoTensor for Array<T, D> {
2279 fn into_tensor(self) -> Tensor {
2280 Tensor::from(self)
2281 }
2282}
2283
2284impl<D: ::ndarray::Dimension, T: Datum> IntoArcTensor for Array<T, D> {
2285 fn into_arc_tensor(self) -> Arc<Tensor> {
2286 Arc::new(Tensor::from(self))
2287 }
2288}
2289
2290impl IntoTensor for Tensor {
2291 fn into_tensor(self) -> Tensor {
2292 self
2293 }
2294}
2295
2296impl IntoTensor for Arc<Tensor> {
2297 fn into_tensor(self) -> Tensor {
2298 Arc::try_unwrap(self).unwrap_or_else(|t| (*t).clone())
2299 }
2300}
2301
2302impl IntoArcTensor for Tensor {
2303 fn into_arc_tensor(self) -> Arc<Tensor> {
2304 Arc::new(self)
2305 }
2306}
2307
2308impl IntoArcTensor for Arc<Tensor> {
2309 fn into_arc_tensor(self) -> Arc<Tensor> {
2310 self
2311 }
2312}
2313
2314#[cfg(test)]
2315mod tests {
2316 use crate::dim::SymbolScope;
2317 use crate::prelude::tensor1;
2318
2319 use super::*;
2320 use litteral::tensor0;
2321 use proptest::collection::vec;
2322 use proptest::prelude::*;
2323
2324 #[test]
2327 fn from_raw_rejects_length_mismatch() {
2328 let err = unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 12]) }
2330 .expect_err("from_raw must reject a short content buffer, not panic");
2331 assert!(err.to_string().contains("does not match shape"), "unexpected error: {err}");
2332 assert!(unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 32]) }.is_err());
2334 assert!(unsafe { Tensor::from_raw_dt(f32::datum_type(), &[2, 3], &[0u8; 24]) }.is_ok());
2336 }
2337
2338 #[derive(Debug)]
2339 struct PermuteAxisProblem {
2340 shape: Vec<usize>,
2341 permutation: Vec<usize>,
2342 }
2343
2344 impl Arbitrary for PermuteAxisProblem {
2345 type Strategy = BoxedStrategy<PermuteAxisProblem>;
2346 type Parameters = ();
2347
2348 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
2349 (0..8usize)
2350 .prop_flat_map(|rank| {
2351 let permute: Vec<usize> = (0..rank).collect();
2352 (proptest::collection::vec(1..5usize, rank), Just(permute).prop_shuffle())
2353 })
2354 .prop_map(|(shape, permutation)| PermuteAxisProblem { shape, permutation })
2355 .boxed()
2356 }
2357 }
2358
2359 impl PermuteAxisProblem {
2360 fn input(&self) -> ArrayD<i32> {
2361 let mut i = 0;
2362 ArrayD::from_shape_simple_fn(&*self.shape, || {
2363 i += 1;
2364 i
2365 })
2366 .permuted_axes(&*self.permutation)
2367 }
2368
2369 fn reference(&self) -> Tensor {
2370 let values: Vec<i32> = self.input().iter().copied().collect();
2371 let shape = self.permutation.iter().map(|ix| self.shape[*ix]).collect::<TVec<usize>>();
2372 super::litteral::tensor1(&values).into_shape(&shape).unwrap()
2373 }
2374
2375 fn tract(&self) -> Tensor {
2376 Tensor::from(self.input())
2377 }
2378
2379 fn check(&self) -> proptest::test_runner::TestCaseResult {
2380 prop_assert_eq!(self.tract(), self.reference());
2381 Ok(())
2382 }
2383 }
2384
2385 proptest::proptest! {
2386 #[test]
2387 fn prop(pb: PermuteAxisProblem) {
2388 pb.check().unwrap();
2389 }
2390 }
2391
2392 #[test]
2393 fn t_1_2() {
2394 PermuteAxisProblem { shape: vec![2, 1], permutation: vec![1, 0] }.check().unwrap();
2395 }
2396
2397 #[test]
2398 fn t_2_2() {
2399 PermuteAxisProblem { shape: vec![2, 2], permutation: vec![1, 0] }.check().unwrap();
2400 }
2401
2402 #[derive(Debug)]
2403 struct BroadcastVecToShape {
2404 vec: Vec<f32>,
2405 axis: usize,
2406 shape: TVec<usize>,
2407 }
2408
2409 impl BroadcastVecToShape {
2410 fn check(&self) -> proptest::test_runner::TestCaseResult {
2411 let input = tensor1(&self.vec);
2412 let mut intermediate = tvec![1usize; self.shape.len()];
2413 intermediate[self.axis] = self.vec.len();
2414 let reference = input
2415 .clone()
2416 .into_shape(&intermediate)
2417 .unwrap()
2418 .broadcast_to_shape(&self.shape)
2419 .unwrap();
2420 prop_assert_eq!(
2421 reference,
2422 input.broadcast_vector_to_shape(&self.shape, self.axis).unwrap()
2423 );
2424 Ok(())
2425 }
2426 }
2427
2428 impl Arbitrary for BroadcastVecToShape {
2429 type Strategy = BoxedStrategy<BroadcastVecToShape>;
2430 type Parameters = ();
2431
2432 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
2433 vec(0usize..5, 0usize..4)
2434 .prop_flat_map(|shape| {
2435 (vec(-10f32..10f32, 0usize..5), Just(shape.clone()), 0..shape.len() + 1)
2436 })
2437 .prop_map(|(vec, mut shape, axis)| {
2438 shape.insert(axis, vec.len());
2439 BroadcastVecToShape { vec, shape: shape.into(), axis }
2440 })
2441 .boxed()
2442 }
2443 }
2444
2445 proptest::proptest! {
2446 #[test]
2447 fn broadcast_vector_to_shape_prop(pb: BroadcastVecToShape) {
2448 pb.check().unwrap()
2449 }
2450 }
2451
2452 #[test]
2453 #[cfg(feature = "complex")]
2454 fn test_reinterpret_inner_dim_as_complex() -> TractResult<()> {
2455 let input = crate::internal::tensor2(&[[1.0f32, 2.0], [3.0, 4.0], [5.0, 6.0]]);
2456 let cplx_input = reinterpret_inner_dim_as_complex(input)?;
2457 let expected = crate::internal::tensor1(&[
2458 Complex::new(1.0f32, 2.0),
2459 Complex::new(3.0, 4.0),
2460 Complex::new(5.0, 6.0),
2461 ]);
2462 assert_eq!(expected, cplx_input);
2463 Ok(())
2464 }
2465
2466 #[test]
2467 #[cfg(feature = "complex")]
2468 fn test_reinterpret_inner_dim_as_complex_2() -> TractResult<()> {
2469 let input =
2470 crate::internal::tensor3(&[[[1i32, 2], [1, 2]], [[3, 4], [3, 4]], [[5, 6], [5, 6]]]);
2471 let cplx_input = reinterpret_inner_dim_as_complex(input)?;
2472 let expected = crate::internal::tensor2(&[
2473 [Complex::new(1i32, 2), Complex::new(1, 2)],
2474 [Complex::new(3, 4), Complex::new(3, 4)],
2475 [Complex::new(5, 6), Complex::new(5, 6)],
2476 ]);
2477 assert_eq!(expected, cplx_input);
2478 Ok(())
2479 }
2480
2481 #[test]
2482 fn clone_tdim_tensor() {
2483 let symbols = SymbolScope::default();
2484 let a = symbols.sym("a");
2485 let t = tensor0(TDim::from(a));
2486 let _ = t.clone();
2487 }
2488
2489 #[test]
2490 fn ulp_approximation_accepts_within_bound() -> TractResult<()> {
2491 let a = tensor1(&[1.0f32, 2.0, 3.0]);
2492 let b = tensor1(&[
2493 f32::from_bits(1.0f32.to_bits() + 1),
2494 2.0,
2495 f32::from_bits(3.0f32.to_bits() + 2),
2496 ]);
2497 a.close_enough(&b, Approximation::Ulp(2))?;
2498 assert!(a.close_enough(&b, Approximation::Ulp(1)).is_err());
2499 assert_eq!(a.max_ulp_distance(&b)?, (2, Some(2)));
2500 Ok(())
2501 }
2502
2503 #[test]
2504 fn ulp_approximation_uses_the_tensor_own_float_type() -> TractResult<()> {
2505 let one = f16::from_f32(1.0);
2508 let a = tensor1(&[one]);
2509 let b = tensor1(&[f16::from_bits(one.to_bits() + 1)]);
2510 assert_eq!(a.ulp_comparison_dt(), DatumType::F16);
2511 assert_eq!(a.max_ulp_distance(&b)?, (1, Some(0)));
2512 a.close_enough(&b, Approximation::Ulp(1))?;
2513 Ok(())
2514 }
2515
2516 #[test]
2517 fn ulp_approximation_is_scale_free() -> TractResult<()> {
2518 let a = tensor1(&[1e-30f32, 1e30]);
2521 let b = tensor1(&[
2522 f32::from_bits(1e-30f32.to_bits() + 1),
2523 f32::from_bits(1e30f32.to_bits() + 1),
2524 ]);
2525 a.close_enough(&b, Approximation::Ulp(1))?;
2526 Ok(())
2527 }
2528
2529 #[test]
2530 fn ulp_approximation_rejects_shape_mismatch() {
2531 let a = tensor1(&[1.0f32, 2.0]);
2532 let b = tensor1(&[1.0f32]);
2533 assert!(a.close_enough(&b, Approximation::Ulp(1000)).is_err());
2534 }
2535
2536 fn stack_reference<T: Datum + Copy + num_traits::Zero>(
2541 axis: usize,
2542 tensors: &[Tensor],
2543 ) -> Tensor {
2544 let mut shape: TVec<usize> = tensors[0].shape().into();
2545 shape[axis] = tensors.iter().map(|t| t.shape()[axis]).sum();
2546 let mut out = Tensor::zero::<T>(&shape).unwrap();
2547 let outer: usize = shape[..axis].iter().product();
2548 let inner: usize = shape[axis + 1..].iter().product();
2549 let mid = shape[axis];
2550 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
2551 let mut base = 0;
2552 for t in tensors {
2553 let m = t.shape()[axis];
2554 let tv = unsafe { t.as_slice_unchecked::<T>() };
2555 for o in 0..outer {
2556 for j in 0..m {
2557 for i in 0..inner {
2558 ov[(o * mid + base + j) * inner + i] = tv[(o * m + j) * inner + i];
2559 }
2560 }
2561 }
2562 base += m;
2563 }
2564 out
2565 }
2566
2567 fn ramp<T: Datum + Copy + From<u8>>(shape: &[usize], seed: u8) -> Tensor {
2568 let n: usize = shape.iter().product();
2569 let v: Vec<T> = (0..n).map(|i| T::from(seed.wrapping_add(i as u8))).collect();
2570 Tensor::from_shape(shape, &v).unwrap()
2571 }
2572
2573 macro_rules! stack_agrees_for {
2574 ($name:ident, $t:ty) => {
2575 #[test]
2576 fn $name() {
2577 for shape in [
2578 tvec!(1usize, 256, 1, 1),
2579 tvec!(1usize, 2, 128, 1),
2580 tvec!(1usize, 35, 35, 8),
2581 tvec!(4usize, 3),
2582 tvec!(7usize),
2583 ] {
2584 for axis in 0..shape.len() {
2585 let a: Tensor = ramp::<$t>(&shape, 1);
2586 let b: Tensor = ramp::<$t>(&shape, 100);
2587 let c: Tensor = ramp::<$t>(&shape, 200);
2588 for n in 1..=3 {
2589 let ins = [a.clone(), b.clone(), c.clone()][..n].to_vec();
2590 let got = Tensor::stack_tensors(axis, &ins).unwrap();
2591 let want = stack_reference::<$t>(axis, &ins);
2592 assert_eq!(got, want, "shape {shape:?} axis {axis} n {n}");
2593 }
2594 }
2595 }
2596 }
2597 };
2598 }
2599
2600 stack_agrees_for!(stack_tensors_agrees_u8, u8);
2601 stack_agrees_for!(stack_tensors_agrees_u16, u16);
2602 stack_agrees_for!(stack_tensors_agrees_u32, u32);
2603 stack_agrees_for!(stack_tensors_agrees_u64, u64);
2604
2605 #[test]
2608 fn stack_tensors_tolerates_a_zero_outer_extent() {
2609 let a = Tensor::zero::<f32>(&[0, 2, 3]).unwrap();
2610 let stacked = Tensor::stack_tensors(2, &[a.clone(), a.clone()]).unwrap();
2611 assert_eq!(stacked.shape(), &[0, 2, 6]);
2612 }
2613
2614 fn assign_slice_reference<T: Datum + Copy>(
2618 dst: &Tensor,
2619 dst_range: Range<usize>,
2620 src: &Tensor,
2621 src_range: Range<usize>,
2622 axis: usize,
2623 ) -> Tensor {
2624 let mut out = dst.clone();
2625 let outer: usize = dst.shape()[..axis].iter().product();
2626 let inner: usize = dst.shape()[axis + 1..].iter().product();
2627 let dst_mid = dst.shape()[axis];
2628 let src_mid = src.shape()[axis];
2629 let sv = unsafe { src.as_slice_unchecked::<T>() };
2630 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
2631 for o in 0..outer {
2632 for j in 0..dst_range.len() {
2633 for i in 0..inner {
2634 ov[(o * dst_mid + dst_range.start + j) * inner + i] =
2635 sv[(o * src_mid + src_range.start + j) * inner + i];
2636 }
2637 }
2638 }
2639 out
2640 }
2641
2642 macro_rules! assign_slice_agrees_for {
2643 ($name:ident, $t:ty) => {
2644 #[test]
2645 fn $name() {
2646 for (shape, axis, dst_mid, src_mid, dst_start, len, src_start) in [
2647 (tvec!(1usize, 56, 24), 2, 24, 8, 16, 8, 0),
2648 (tvec!(1usize, 56, 24), 2, 24, 24, 0, 16, 8),
2649 (tvec!(1usize, 32, 4, 128), 3, 128, 128, 0, 64, 64),
2650 (tvec!(1usize, 8, 16, 64), 2, 16, 1, 3, 1, 0),
2651 (tvec!(3usize, 5), 0, 3, 7, 1, 2, 4),
2652 (tvec!(4usize, 3), 1, 3, 3, 0, 3, 0),
2653 (tvec!(7usize), 0, 7, 7, 2, 0, 5),
2654 ] {
2655 let mut dst_shape = shape.clone();
2656 dst_shape[axis] = dst_mid;
2657 let mut src_shape = shape.clone();
2658 src_shape[axis] = src_mid;
2659 let mut got: Tensor = ramp::<$t>(&dst_shape, 1);
2660 let src: Tensor = ramp::<$t>(&src_shape, 100);
2661 let want = assign_slice_reference::<$t>(
2662 &got,
2663 dst_start..dst_start + len,
2664 &src,
2665 src_start..src_start + len,
2666 axis,
2667 );
2668 got.assign_slice(
2669 dst_start..dst_start + len,
2670 &src,
2671 src_start..src_start + len,
2672 axis,
2673 )
2674 .unwrap();
2675 assert_eq!(got, want, "shape {dst_shape:?} axis {axis}");
2676 }
2677 }
2678 };
2679 }
2680
2681 assign_slice_agrees_for!(assign_slice_agrees_u8, u8);
2682 assign_slice_agrees_for!(assign_slice_agrees_u16, u16);
2683 assign_slice_agrees_for!(assign_slice_agrees_u32, u32);
2684 assign_slice_agrees_for!(assign_slice_agrees_u64, u64);
2685
2686 macro_rules! assign_slice_at_prefix_agrees_for {
2689 ($name:ident, $t:ty) => {
2690 #[test]
2691 fn $name() {
2692 for (shape, prefix, src_lead, src_prefix, axis, start, len, src_start) in [
2693 (tvec!(3usize, 5, 7), tvec!(2usize), 4, tvec!(3usize), 2, 3, 4, 0),
2694 (tvec!(3usize, 5, 7), tvec!(0usize), 1, tvec!(0usize), 1, 1, 3, 2),
2695 (tvec!(2usize, 4, 8, 3), tvec!(1usize, 2), 2, tvec!(0usize, 1), 3, 0, 3, 0),
2696 (tvec!(4usize, 6), tvec!(), 4, tvec!(), 1, 2, 4, 2),
2697 ] {
2698 let mut src_shape = shape.clone();
2699 src_shape[0] = src_lead;
2700 let mut got: Tensor = ramp::<$t>(&shape, 1);
2701 let src: Tensor = ramp::<$t>(&src_shape, 100);
2702 let mut want_sub = sub_tensor(&got, &prefix);
2705 want_sub
2706 .assign_slice(
2707 start..start + len,
2708 &sub_tensor(&src, &src_prefix),
2709 src_start..src_start + len,
2710 axis - prefix.len(),
2711 )
2712 .unwrap();
2713 got.assign_slice_at_prefix(
2714 &prefix,
2715 start..start + len,
2716 &src,
2717 &src_prefix,
2718 src_start..src_start + len,
2719 axis,
2720 )
2721 .unwrap();
2722 assert_eq!(
2723 sub_tensor(&got, &prefix),
2724 want_sub,
2725 "shape {shape:?} prefix {prefix:?} axis {axis}"
2726 );
2727 }
2728 }
2729 };
2730 }
2731
2732 fn sub_tensor(t: &Tensor, prefix: &[usize]) -> Tensor {
2733 let mut sub = t.clone();
2734 for ix in prefix {
2735 sub = sub.slice(0, *ix, ix + 1).unwrap();
2736 sub.remove_axis(0).unwrap();
2737 }
2738 sub
2739 }
2740
2741 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u8, u8);
2742 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u16, u16);
2743 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u32, u32);
2744 assign_slice_at_prefix_agrees_for!(assign_slice_at_prefix_agrees_u64, u64);
2745
2746 fn fill_slice_reference<T: Datum + Copy>(
2750 data: &Tensor,
2751 prefix: &[usize],
2752 range: Range<usize>,
2753 value: T,
2754 axis: usize,
2755 ) -> Tensor {
2756 let mut out = data.clone();
2757 let shape = data.shape().to_vec();
2758 let inner: usize = shape[axis + 1..].iter().product();
2759 let mid = shape[axis];
2760 let outer: usize = shape[prefix.len()..axis].iter().product();
2761 let at: usize = izip!(prefix, data.strides()).map(|(ix, s)| ix * *s as usize).sum();
2762 let ov = unsafe { out.as_slice_mut_unchecked::<T>() };
2763 for o in 0..outer {
2764 for j in range.clone() {
2765 for i in 0..inner {
2766 ov[at + (o * mid + j) * inner + i] = value;
2767 }
2768 }
2769 }
2770 out
2771 }
2772
2773 macro_rules! fill_slice_agrees_for {
2774 ($name:ident, $t:ty) => {
2775 #[test]
2776 fn $name() {
2777 for (shape, prefix, axis, start, len) in [
2778 (tvec!(1usize, 56, 24), tvec!(), 2, 16, 8),
2779 (tvec!(3usize, 5, 7), tvec!(), 1, 1, 3),
2780 (tvec!(3usize, 5, 7), tvec!(2usize), 2, 3, 4),
2781 (tvec!(3usize, 5, 7), tvec!(1usize, 4), 2, 0, 7),
2782 (tvec!(4usize, 3), tvec!(), 0, 1, 2),
2783 (tvec!(2usize, 8, 16, 64), tvec!(1usize), 2, 3, 1),
2784 (tvec!(7usize), tvec!(), 0, 2, 0),
2785 ] {
2786 let value: $t = 42 as $t;
2787 let mut got: Tensor = ramp::<$t>(&shape, 1);
2788 let want =
2789 fill_slice_reference::<$t>(&got, &prefix, start..start + len, value, axis);
2790 got.fill_slice_at_prefix(&prefix, start..start + len, &tensor0(value), axis)
2791 .unwrap();
2792 assert_eq!(got, want, "shape {shape:?} prefix {prefix:?} axis {axis}");
2793 }
2794 }
2795 };
2796 }
2797
2798 fill_slice_agrees_for!(fill_slice_agrees_u8, u8);
2799 fill_slice_agrees_for!(fill_slice_agrees_u16, u16);
2800 fill_slice_agrees_for!(fill_slice_agrees_u32, u32);
2801 fill_slice_agrees_for!(fill_slice_agrees_u64, u64);
2802
2803 #[test]
2804 fn fill_slice_carries_non_copy_data() {
2805 let strings = |v: [&str; 6]| {
2806 ndarray::Array2::from_shape_vec((2, 3), v.iter().map(|s| s.to_string()).collect())
2807 .unwrap()
2808 .into_tensor()
2809 };
2810 let mut data = strings(["a", "b", "c", "d", "e", "f"]);
2811 data.fill_slice(1..3, &tensor0("x".to_string()), 1).unwrap();
2812 assert_eq!(data, strings(["a", "x", "x", "d", "x", "x"]));
2813 }
2814
2815 #[test]
2816 fn assign_slice_carries_non_copy_data() {
2817 let strings = |v: [&str; 6]| {
2818 ndarray::Array2::from_shape_vec((2, 3), v.iter().map(|s| s.to_string()).collect())
2819 .unwrap()
2820 .into_tensor()
2821 };
2822 let mut dst = strings(["a", "b", "c", "d", "e", "f"]);
2823 let src = ndarray::Array2::from_shape_vec((2, 1), vec!["x".to_string(), "y".to_string()])
2824 .unwrap()
2825 .into_tensor();
2826 dst.assign_slice(1..2, &src, 0..1, 1).unwrap();
2827 assert_eq!(dst, strings(["a", "x", "c", "d", "y", "f"]));
2828 }
2829
2830 #[test]
2833 fn broadcast_to_shape_agrees_with_the_view() {
2834 for (src, dst) in [
2835 (tvec!(1usize, 8, 1, 7, 4), tvec!(1usize, 8, 4, 7, 4)),
2836 (tvec!(1usize, 1, 1, 7), tvec!(2usize, 3, 5, 7)),
2837 (tvec!(4usize), tvec!(2usize, 3, 5, 4)),
2838 (tvec!(1usize, 5, 3), tvec!(6usize, 5, 3)),
2839 (tvec!(2usize, 3), tvec!(2usize, 3)),
2840 (tvec!(1usize), tvec!(3usize, 1, 2)),
2841 (tvec!(3usize, 1), tvec!(3usize, 0)),
2842 ] {
2843 for dt in [f32::datum_type(), u8::datum_type(), i32::datum_type()] {
2844 let t = Tensor::zero_dt(dt, &src).unwrap().cast_to_dt(dt).unwrap().into_owned();
2845 let got = t.broadcast_to_shape(&dst).unwrap();
2846 let want = dispatch_datum!(Tensor::broadcast_to_shape_t(dt)(&t, &dst)).unwrap();
2847 assert_eq!(got.shape(), &*dst, "{src:?} -> {dst:?}");
2848 assert_eq!(got, want, "{src:?} -> {dst:?} {dt:?}");
2849 }
2850 }
2851 }
2852
2853 #[test]
2854 fn broadcast_to_shape_carries_values_and_rejects_mismatches() {
2855 let t = tensor2(&[[1u8, 2, 3], [4, 5, 6]]);
2856 let got = t.clone().into_shape(&[2, 1, 3]).unwrap().broadcast_to_shape(&[2, 2, 3]).unwrap();
2857 assert_eq!(got, tensor3(&[[[1u8, 2, 3], [1, 2, 3]], [[4, 5, 6], [4, 5, 6]]]));
2858 assert!(t.broadcast_to_shape(&[3, 3]).is_err());
2859 assert!(t.broadcast_to_shape(&[3]).is_err());
2860 }
2861
2862 #[test]
2863 fn slice_keeps_the_datum_type_and_the_values() {
2864 let t = ramp::<u32>(&tvec!(2usize, 3, 4), 0);
2865 let got = t.slice(1, 1, 3).unwrap();
2866 let mut want = Tensor::zero::<u32>(&[2, 2, 4]).unwrap();
2867 want.assign_slice(0..2, &t, 1..3, 1).unwrap();
2868 assert_eq!(got, want);
2869 let quantized = Tensor::zero_dt(
2870 i8::datum_type().quantize(QParams::ZpScale { zero_point: 3, scale: 0.5 }),
2871 &[2, 4],
2872 )
2873 .unwrap();
2874 assert_eq!(quantized.slice(1, 0, 2).unwrap().datum_type(), quantized.datum_type());
2875 }
2876
2877 #[test]
2878 fn ulp_bounds_are_distinguished_by_equality() {
2879 assert_eq!(Approximation::Ulp(1), Approximation::Ulp(1));
2880 assert_ne!(Approximation::Ulp(1), Approximation::Ulp(2));
2881 }
2882
2883 #[derive(Debug)]
2886 struct LateStorage {
2887 elsewhere: Vec<u8>,
2888 here: std::sync::OnceLock<PlainStorage>,
2889 materializations: std::sync::atomic::AtomicUsize,
2890 }
2891
2892 impl LateStorage {
2893 fn new(bytes: &[u8]) -> LateStorage {
2894 LateStorage {
2895 elsewhere: bytes.to_vec(),
2896 here: std::sync::OnceLock::new(),
2897 materializations: std::sync::atomic::AtomicUsize::new(0),
2898 }
2899 }
2900 fn count(t: &Tensor) -> usize {
2901 t.storage_as::<LateStorage>()
2902 .unwrap()
2903 .materializations
2904 .load(std::sync::atomic::Ordering::Relaxed)
2905 }
2906 }
2907
2908 impl PartialEq for LateStorage {
2909 fn eq(&self, other: &Self) -> bool {
2910 self.elsewhere == other.elsewhere
2911 }
2912 }
2913 impl Eq for LateStorage {}
2914
2915 impl std::fmt::Display for LateStorage {
2916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2917 write!(f, "LateStorage")
2918 }
2919 }
2920
2921 impl TensorStorage for LateStorage {
2922 fn byte_len(&self) -> usize {
2923 self.elsewhere.len()
2924 }
2925 fn is_empty(&self) -> bool {
2926 self.elsewhere.is_empty()
2927 }
2928 fn deep_clone(&self) -> Box<dyn TensorStorage> {
2929 Box::new(LateStorage::new(&self.elsewhere))
2930 }
2931 fn as_plain_ram(&self) -> Option<&PlainStorage> {
2932 self.here.get()
2933 }
2934 fn as_plain_ram_mut(&mut self) -> Option<&mut PlainStorage> {
2935 None
2936 }
2937 fn into_plain_ram(self: Box<Self>) -> Option<PlainStorage> {
2938 let me = *self;
2939 me.materialize_plain_ram().ok()?;
2940 me.here.into_inner()
2941 }
2942 fn dyn_hash(&self, _state: &mut dyn std::hash::Hasher) {}
2943 fn exotic_fact(&self, _shape: &[usize]) -> TractResult<Option<Box<dyn ExoticFact>>> {
2944 Ok(None)
2945 }
2946 fn is_exotic(&self) -> bool {
2947 false
2948 }
2949 fn in_ram(&self) -> bool {
2950 self.here.get().is_some()
2951 }
2952 fn materialize_plain_ram(&self) -> TractResult<&PlainStorage> {
2953 if let Some(here) = self.here.get() {
2954 return Ok(here);
2955 }
2956 self.materializations.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2957 let blob = Blob::from_bytes(&self.elsewhere)?;
2958 Ok(self.here.get_or_init(|| PlainStorage::from(blob)))
2959 }
2960 fn slice(
2961 &self,
2962 dt: DatumType,
2963 shape: &[usize],
2964 axis: usize,
2965 start: usize,
2966 end: usize,
2967 ) -> TractResult<Option<Tensor>> {
2968 if axis != 0 || shape[..axis].iter().product::<usize>() != 1 {
2970 return Ok(None);
2971 }
2972 let row = shape[1..].iter().product::<usize>() * dt.size_of();
2973 let mut sliced: TVec<usize> = shape.into();
2974 sliced[0] = end - start;
2975 Ok(Some(Tensor::from_storage(
2976 dt,
2977 &sliced,
2978 LateStorage::new(&self.elsewhere[start * row..end * row]),
2979 )))
2980 }
2981 }
2982
2983 fn late_tensor(shape: &[usize], values: &[f32]) -> Tensor {
2984 let host = Tensor::from_shape(shape, values).unwrap();
2985 Tensor::from_storage(f32::datum_type(), shape, LateStorage::new(host.as_bytes()))
2986 }
2987
2988 #[test]
2989 fn late_storage_stays_put_until_the_bytes_are_read() {
2990 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
2991 assert!(!t.in_ram());
2993 assert!(t.as_plain_ram().is_none());
2994 assert_eq!(t.datum_type(), f32::datum_type());
2995 assert_eq!(t.shape(), &[2, 3]);
2996 assert_eq!(LateStorage::count(&t), 0);
2997 assert_eq!(
2999 t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(),
3000 &[1f32, 2., 3., 4., 5., 6.]
3001 );
3002 assert_eq!(LateStorage::count(&t), 1);
3003 assert_eq!(t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap()[0], 1f32);
3004 assert_eq!(LateStorage::count(&t), 1);
3005 }
3006
3007 #[test]
3008 fn late_storage_is_plain_but_not_in_ram_until_asked() {
3009 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3010 assert!(t.is_plain());
3011 assert!(!t.in_ram());
3012 let t = t.into_plain_ram().unwrap();
3013 assert!(t.in_ram());
3014 assert!(t.has_plain_ram_storage());
3015 assert_eq!(
3016 t.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(),
3017 &[1f32, 2., 3., 4., 5., 6.]
3018 );
3019 }
3020
3021 #[test]
3022 fn late_storage_slices_without_materializing_when_it_can() {
3023 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3024 let row = t.slice(0, 1, 2).unwrap();
3025 assert_eq!(LateStorage::count(&t), 0);
3026 assert!(row.storage_as::<LateStorage>().is_some());
3027 assert_eq!(row.shape(), &[1, 3]);
3028 assert_eq!(row.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(), &[4f32, 5., 6.]);
3029 }
3030
3031 #[test]
3032 fn late_storage_falls_back_to_a_copy_on_a_gappy_slice() {
3033 let t = late_tensor(&[2, 3], &[1f32, 2., 3., 4., 5., 6.]);
3034 let col = t.slice(1, 1, 3).unwrap();
3035 assert!(col.in_ram());
3037 assert_eq!(col.shape(), &[2, 2]);
3038 assert_eq!(col.try_as_plain_ram().unwrap().as_slice::<f32>().unwrap(), &[2f32, 3., 5., 6.]);
3039 assert_eq!(LateStorage::count(&t), 1);
3040 }
3041}