1use std::iter::repeat_n;
4use std::ops::Range;
5
6use smallvec::{SmallVec, smallvec};
7
8use crate::errors::{DimensionError, ExpandError, FromDataError, ReshapeError, SliceError};
9use crate::index_iterator::{DynIndices, NdIndices};
10use crate::overlap::{is_contiguous, may_have_internal_overlap};
11use crate::slice_range::{IntoSliceItems, SliceItem};
12use crate::type_num::{OptionalUInt, U0, U1, U2, U3, U4, U5, Unknown};
13
14pub fn is_valid_permutation(ndim: usize, permutation: &[usize]) -> bool {
17 permutation.len() == ndim
18 && (0..ndim).all(|dim| permutation.iter().filter(|d| **d == dim).count() == 1)
19}
20
21pub(crate) fn merge_axes<S: SizeArray, St: SizeArray>(
28 shape: &S,
29 strides: &St,
30) -> SmallVec<[(usize, usize); 4]> {
31 let last_dim = shape.len().saturating_sub(1);
32 let (Some(prev_size), Some(prev_stride)) = (shape.get(last_dim), strides.get(last_dim)) else {
33 return SmallVec::new();
34 };
35
36 let mut merged: SmallVec<[(usize, usize); 4]> = SmallVec::with_capacity(shape.len());
37 merged.push((prev_size, prev_stride));
38
39 for (outer_size, outer_stride) in shape.iter().zip(strides.iter()).rev().skip(1) {
40 let (inner_size, inner_stride) = merged.last_mut().unwrap();
41 let can_merge = outer_size == 1 || (outer_stride == *inner_stride * *inner_size);
42 if can_merge {
43 *inner_size *= outer_size;
44 } else {
45 merged.push((outer_size, outer_stride));
46 }
47 }
48
49 merged.reverse();
50
51 merged
52}
53
54macro_rules! debug_assert_dim_valid {
56 ($layout:ident, $dim:expr) => {
57 debug_assert!(
58 $dim < $layout.ndim(),
59 "dim {} out of bounds for tensor with {} dims",
60 $dim,
61 $layout.ndim()
62 )
63 };
64}
65
66pub trait SizeArray: Clone + std::fmt::Debug + PartialEq<Self> {
73 fn len(&self) -> usize;
75
76 fn is_empty(&self) -> bool {
77 self.len() == 0
78 }
79
80 fn get(&self, index: usize) -> Option<usize>;
82
83 fn iter(&self) -> impl ExactSizeIterator<Item = usize> + DoubleEndedIterator;
85}
86
87impl<T: Clone + AsRef<[usize]> + std::fmt::Debug + PartialEq<Self>> SizeArray for T {
88 fn len(&self) -> usize {
89 self.as_ref().len()
90 }
91
92 fn get(&self, index: usize) -> Option<usize> {
93 self.as_ref().get(index).copied()
94 }
95
96 fn iter(&self) -> impl ExactSizeIterator<Item = usize> + DoubleEndedIterator {
97 self.as_ref().iter().copied()
98 }
99}
100
101pub trait Layout {
112 type Shape<'a>: SizeArray
114 where
115 Self: 'a;
116
117 type Strides<'a>: SizeArray
119 where
120 Self: 'a;
121
122 type Index<'a>: AsRef<[usize]> + Clone + std::fmt::Debug + PartialEq<Self::Index<'a>>;
124
125 type Indices;
127
128 fn offset_unchecked(&self, index: Self::Index<'_>) -> usize {
135 index
136 .as_ref()
137 .iter()
138 .zip(self.strides().iter())
139 .map(|(idx, stride)| *idx * stride)
140 .sum()
141 }
142
143 fn offset(&self, index: Self::Index<'_>) -> Option<usize>;
151
152 fn ndim(&self) -> usize;
154
155 fn len(&self) -> usize;
157
158 fn is_contiguous(&self) -> bool {
161 is_contiguous(&self.shape(), &self.strides())
162 }
163
164 fn is_broadcast(&self) -> bool {
167 !self.is_empty() && self.strides().iter().any(|s| s == 0)
168 }
169
170 fn is_empty(&self) -> bool {
172 self.len() == 0
173 }
174
175 fn shape(&self) -> Self::Shape<'_>;
177
178 fn size(&self, dim: usize) -> usize {
180 self.shape().get(dim).expect("invalid dim index")
181 }
182
183 fn strides(&self) -> Self::Strides<'_>;
185
186 fn stride(&self, dim: usize) -> usize {
188 self.strides().get(dim).expect("invalid dimension")
189 }
190
191 fn indices(&self) -> Self::Indices;
193
194 fn can_broadcast_to(&self, target_shape: &[usize]) -> bool {
196 if self.ndim() > target_shape.len() {
197 return false;
198 }
199
200 let target_dims = target_shape[target_shape.len() - self.shape().len()..]
206 .iter()
207 .copied();
208
209 self.shape()
210 .iter()
211 .zip(target_dims)
212 .all(|(a, b)| a == b || a == 1)
213 }
214
215 fn can_broadcast_with(&self, shape: &[usize]) -> bool {
225 let a = self.shape();
232 let b = shape;
233
234 let a_pad = b.len().saturating_sub(a.len());
235 let b_pad = a.len().saturating_sub(b.len());
236
237 let a_iter = a.iter().rev().chain(repeat_n(1, a_pad));
238 let b_iter = b.iter().copied().rev().chain(repeat_n(1, b_pad));
239
240 a_iter.zip(b_iter).all(|(a, b)| a == b || a == 1 || b == 1)
241 }
242
243 fn min_data_len(&self) -> usize {
246 if self.shape().iter().any(|d| d == 0) {
247 return 0;
248 }
249 let max_offset: usize = self
250 .shape()
251 .iter()
252 .zip(self.strides().iter())
253 .map(|(size, stride)| (size - 1) * stride)
254 .sum();
255 max_offset + 1
256 }
257
258 fn reshaped_for_view<S: IntoLayout>(&self, shape: S) -> Result<S::Layout, ReshapeError> {
264 if !self.is_contiguous() {
265 return Err(ReshapeError::NotContiguous);
266 }
267 self.reshaped_for_copy(shape)
268 }
269
270 fn reshaped_for_copy<S: IntoLayout>(&self, shape: S) -> Result<S::Layout, ReshapeError> {
272 let layout = shape.into_layout();
273 if layout.len() != self.len() {
274 return Err(ReshapeError::LengthMismatch);
275 }
276 Ok(layout)
277 }
278}
279
280pub unsafe trait TrustedLayout: Layout {}
291
292pub(crate) trait LayoutExt: Layout {
297 #[inline]
299 fn must_offset(&self, index: Self::Index<'_>) -> usize {
300 self.offset(index.clone()).unwrap_or_else(|| {
301 panic!(
302 "index {:?} out of bounds for shape {:?}",
303 index.as_ref(),
304 self.shape()
305 )
306 })
307 }
308}
309
310impl<L: Layout> LayoutExt for L {}
311
312pub trait MatrixLayout {
314 fn rows(&self) -> usize;
315 fn cols(&self) -> usize;
316 fn row_stride(&self) -> usize;
317 fn col_stride(&self) -> usize;
318}
319
320pub enum OverlapPolicy {
326 AllowOverlap,
327 DisallowOverlap,
328}
329
330#[derive(Clone, Copy, Debug, PartialEq)]
333pub struct NdLayout<const N: usize> {
334 shape: [usize; N],
335 strides: [usize; N],
336}
337
338impl<const N: usize> Layout for NdLayout<N> {
339 type Shape<'a> = [usize; N];
340 type Strides<'a> = [usize; N];
341 type Index<'a> = [usize; N];
342 type Indices = NdIndices<N>;
343
344 fn ndim(&self) -> usize {
345 N
346 }
347
348 fn len(&self) -> usize {
349 self.shape.iter().product()
350 }
351
352 #[inline]
353 fn offset(&self, index: [usize; N]) -> Option<usize> {
354 if !self.index_valid(index) {
355 return None;
356 }
357 Some(self.offset_unchecked(index))
358 }
359
360 #[inline]
361 fn offset_unchecked(&self, index: [usize; N]) -> usize {
362 let mut offset = 0;
363 for i in 0..N {
364 offset += index[i] * self.strides[i];
365 }
366 offset
367 }
368
369 #[inline]
370 fn shape(&self) -> Self::Shape<'_> {
371 self.shape
372 }
373
374 #[inline]
375 fn strides(&self) -> [usize; N] {
376 self.strides
377 }
378
379 fn indices(&self) -> Self::Indices {
380 NdIndices::from_shape(self.shape)
381 }
382}
383
384unsafe impl<const N: usize> TrustedLayout for NdLayout<N> {}
385
386impl<L: Layout> Layout for &L {
387 type Shape<'b>
388 = L::Shape<'b>
389 where
390 Self: 'b;
391 type Strides<'b>
392 = L::Strides<'b>
393 where
394 Self: 'b;
395 type Index<'b> = L::Index<'b>;
396 type Indices = L::Indices;
397
398 fn ndim(&self) -> usize {
399 (*self).ndim()
400 }
401
402 fn len(&self) -> usize {
403 (*self).len()
404 }
405
406 fn offset(&self, index: Self::Index<'_>) -> Option<usize> {
407 (*self).offset(index)
408 }
409
410 fn offset_unchecked(&self, index: Self::Index<'_>) -> usize {
411 (*self).offset_unchecked(index)
412 }
413
414 fn shape(&self) -> Self::Shape<'_> {
415 (*self).shape()
416 }
417
418 fn strides(&self) -> Self::Strides<'_> {
419 (*self).strides()
420 }
421
422 fn indices(&self) -> Self::Indices {
423 (*self).indices()
424 }
425}
426
427unsafe impl<L: TrustedLayout> TrustedLayout for &L {}
430
431impl MatrixLayout for NdLayout<2> {
432 #[inline]
433 fn rows(&self) -> usize {
434 self.size(0)
435 }
436
437 #[inline]
438 fn cols(&self) -> usize {
439 self.size(1)
440 }
441
442 #[inline]
443 fn row_stride(&self) -> usize {
444 self.stride(0)
445 }
446
447 #[inline]
448 fn col_stride(&self) -> usize {
449 self.stride(1)
450 }
451}
452
453fn slice_layout(
461 in_shape: impl AsRef<[usize]>,
462 in_strides: impl AsRef<[usize]>,
463 mut out_shape: impl AsMut<[usize]>,
464 mut out_strides: impl AsMut<[usize]>,
465 range: &[SliceItem],
466) -> Result<(usize, usize), SliceError> {
467 let in_shape = in_shape.as_ref();
468 let out_shape = out_shape.as_mut();
469 let out_strides = out_strides.as_mut();
470
471 let mut ndim = 0;
472 let mut offset = 0;
473
474 for (in_dim, (&size, &stride)) in in_shape.iter().zip(in_strides.as_ref()).enumerate() {
475 let (offset_adjust, new_size_stride) = match range.get(in_dim) {
476 Some(&SliceItem::Index(idx)) => {
477 let pos_idx = if idx >= 0 { idx } else { idx + size as isize };
478 if pos_idx < 0 || pos_idx >= size as isize {
479 return Err(SliceError::InvalidIndex {
480 axis: in_dim,
481 index: idx,
482 size,
483 });
484 }
485 (stride * pos_idx as usize, None)
486 }
487 Some(SliceItem::Range(range)) => {
488 let resolved = range.resolve(size).ok_or(SliceError::InvalidRange {
489 axis: in_dim,
490 range: *range,
491 size,
492 })?;
493 let step: usize = range
494 .step()
495 .try_into()
496 .map_err(|_| SliceError::InvalidStep {
497 axis: in_dim,
498 step: range.step(),
499 })?;
500 let new_size = if step == 1 {
501 resolved.end - resolved.start
503 } else {
504 range.index_range(size).steps()
505 };
506 let new_stride = stride * step;
507 (stride * resolved.start, Some((new_size, new_stride)))
508 }
509 None => (0, Some((size, stride))),
510 };
511
512 offset += offset_adjust;
513 if let Some((new_size, new_stride)) = new_size_stride {
514 out_shape[ndim] = new_size;
515 out_strides[ndim] = new_stride;
516 ndim += 1;
517 }
518 }
519
520 if out_shape.contains(&0) {
521 offset = 0;
522 }
523
524 Ok((ndim, offset))
525}
526
527fn broadcast_strides<'a>(
530 from_shape: &'a [usize],
531 from_strides: &'a [usize],
532 to_shape: &'a [usize],
533) -> impl Iterator<Item = usize> + 'a {
534 let pad = to_shape.len() - from_shape.len();
535 repeat_n(0, pad).chain(from_shape.iter().zip(from_strides).enumerate().map(
536 move |(i, (size, stride))| {
537 if *size == 1 && to_shape[i + pad] > 1 {
538 0
539 } else {
540 *stride
541 }
542 },
543 ))
544}
545
546impl<const N: usize> NdLayout<N> {
547 pub fn as_dyn(&self) -> DynLayout {
549 self.into()
550 }
551
552 fn index_valid(&self, index: [usize; N]) -> bool {
554 let mut valid = true;
555 for i in 0..N {
556 valid = valid && index[i] < self.shape[i]
557 }
558 valid
559 }
560
561 fn contiguous_strides(shape: [usize; N]) -> [usize; N] {
564 let mut strides = [0; N];
565 for i in 0..N {
566 strides[i] = shape[i + 1..].iter().product();
567 }
568 strides
569 }
570}
571
572impl<'a, const N: usize> TryFrom<&'a DynLayout> for NdLayout<N> {
573 type Error = DimensionError;
574
575 fn try_from(value: &'a DynLayout) -> Result<NdLayout<N>, DimensionError> {
578 let shape = value.shape();
579 let shape: [usize; N] = shape.try_into().map_err(|_| DimensionError {
580 actual: shape.len(),
581 expected: N,
582 })?;
583 let strides = value.strides();
584 let strides: [usize; N] = strides.try_into().map_err(|_| DimensionError {
585 actual: strides.len(),
586 expected: N,
587 })?;
588 Ok(NdLayout { shape, strides })
589 }
590}
591
592#[derive(Debug, PartialEq)]
598pub struct DynLayout {
599 shape_and_strides: SmallVec<[usize; 8]>,
605}
606
607impl Clone for DynLayout {
608 fn clone(&self) -> DynLayout {
609 DynLayout {
610 shape_and_strides: SmallVec::from_slice(self.shape_and_strides.as_slice()),
614 }
615 }
616}
617
618impl Layout for DynLayout {
619 type Shape<'a> = &'a [usize];
620 type Strides<'a> = &'a [usize];
621 type Index<'a> = &'a [usize];
622 type Indices = DynIndices;
623
624 fn len(&self) -> usize {
626 self.shape().iter().product()
627 }
628
629 #[inline]
630 fn offset(&self, index: Self::Index<'_>) -> Option<usize> {
631 let shape = self.shape();
632 let strides = self.strides();
633 let mut valid = index.as_ref().len() == shape.len();
634 let mut offset = 0;
635 for (idx, (size, stride)) in index.as_ref().iter().zip(shape.iter().zip(strides)) {
636 valid = valid && idx < size;
637 offset += idx * stride;
638 }
639 valid.then_some(offset)
640 }
641
642 fn is_empty(&self) -> bool {
643 self.len() == 0
644 }
645
646 #[inline]
648 fn ndim(&self) -> usize {
649 self.shape_and_strides.len() / 2
650 }
651
652 #[inline]
654 fn shape(&self) -> &[usize] {
655 &self.shape_and_strides[0..self.ndim()]
656 }
657
658 #[inline]
660 fn size(&self, dim: usize) -> usize {
661 debug_assert_dim_valid!(self, dim);
662 self.shape_and_strides[dim]
663 }
664
665 #[inline]
667 fn strides(&self) -> Self::Strides<'_> {
668 &self.shape_and_strides[self.ndim()..]
669 }
670
671 #[inline]
673 fn stride(&self, dim: usize) -> usize {
674 debug_assert_dim_valid!(self, dim);
675 self.shape_and_strides[self.ndim() + dim]
676 }
677
678 fn indices(&self) -> DynIndices {
679 DynIndices::from_shape(self.shape())
680 }
681}
682
683unsafe impl TrustedLayout for DynLayout {}
684
685impl DynLayout {
686 pub fn make_contiguous(&mut self) {
687 self.shape_and_strides = Self::contiguous_shape_and_strides(self.shape());
688 }
689
690 fn permute_iter<I: Clone + Iterator<Item = usize>>(&mut self, dims: I) {
691 let shape_iter = dims.clone().map(|dim| self.size(dim));
692 let stride_iter = dims.map(|dim| self.stride(dim));
693 self.shape_and_strides = shape_iter.chain(stride_iter).collect();
694 }
695
696 fn permute(&mut self, dims: &[usize]) {
699 assert!(
700 is_valid_permutation(self.ndim(), dims),
701 "permutation is invalid"
702 );
703 self.permute_iter(dims.iter().copied());
704 }
705
706 fn transpose(&mut self) {
708 self.permute_iter((0..self.ndim()).rev());
709 }
710
711 fn contiguous_shape_and_strides(shape: &[usize]) -> SmallVec<[usize; 8]> {
713 let mut strides_and_shape: SmallVec<[usize; 8]> = SmallVec::from_slice(shape);
714 strides_and_shape.resize(shape.len() * 2, 0);
715 let mut stride = 1;
716 for i in (0..shape.len()).rev() {
717 strides_and_shape[shape.len() + i] = stride;
718 stride *= shape[i];
719 }
720 strides_and_shape
721 }
722}
723
724impl<L: Layout> From<&L> for DynLayout {
725 fn from(layout: &L) -> DynLayout {
726 let shape: SmallVec<[usize; 5]> = layout.shape().iter().collect();
727 let strides: SmallVec<[usize; 5]> = layout.strides().iter().collect();
728 DynLayout::from_shape_and_strides(&shape, &strides, OverlapPolicy::AllowOverlap)
729 .expect("invalid layout")
730 }
731}
732
733impl<const N: usize> From<NdLayout<N>> for DynLayout {
734 fn from(value: NdLayout<N>) -> DynLayout {
735 Self::from(&value)
736 }
737}
738
739pub trait MutLayout: Layout + Clone {
760 fn from_shape_and_strides(
767 shape: Self::Shape<'_>,
768 strides: Self::Strides<'_>,
769 overlap: OverlapPolicy,
770 ) -> Result<Self, FromDataError>;
771
772 fn index_axis(&self, axis: usize, index: usize) -> (Range<usize>, <Self as RemoveDim>::Output)
776 where
777 Self: RemoveDim,
778 {
779 assert!(axis < self.ndim());
780 assert!(index < self.size(axis));
781
782 let layout = self.remove_dim(axis);
783
784 let range = if layout.is_empty() {
785 0..0
786 } else {
787 let start_offset = self.stride(axis) * index;
788 start_offset..start_offset + layout.min_data_len()
789 };
790
791 (range, layout)
792 }
793
794 fn move_axis(&mut self, from: usize, to: usize);
796
797 fn permuted(&self, order: Self::Index<'_>) -> Self;
799
800 fn resize_dim(&mut self, dim: usize, size: usize);
802
803 fn transposed(&self) -> Self;
806
807 fn slice<const M: usize>(
811 &self,
812 range: &[SliceItem],
813 ) -> Result<(Range<usize>, NdLayout<M>), SliceError>;
814
815 fn slice_dyn(&self, range: &[SliceItem]) -> Result<(Range<usize>, DynLayout), SliceError>;
819
820 fn slice_axis(
824 &self,
825 axis: usize,
826 range: Range<usize>,
827 ) -> Result<(Range<usize>, Self), SliceError> {
828 if axis >= self.ndim() {
829 return Err(SliceError::InvalidAxis { axis });
830 }
831 if range.end < range.start || range.end > self.size(axis) {
832 return Err(SliceError::InvalidRange {
833 axis,
834 range: range.into(),
835 size: self.size(axis),
836 });
837 }
838
839 let mut sliced_layout = self.clone();
840 sliced_layout.resize_dim(axis, range.len());
841 let range = if sliced_layout.is_empty() {
842 0..0
843 } else {
844 let start_offset = range.start * sliced_layout.stride(axis);
845 let end_offset = start_offset + sliced_layout.min_data_len();
846 start_offset..end_offset
847 };
848 Ok((range, sliced_layout))
849 }
850
851 fn squeezed(&self) -> DynLayout;
853
854 fn split(&self, axis: usize, mid: usize) -> ((Range<usize>, Self), (Range<usize>, Self));
859}
860
861pub trait FromShape: Layout {
863 fn from_shape(shape: Self::Shape<'_>) -> Self;
865}
866
867pub trait BroadcastLayout<L: Layout> {
869 fn broadcast<S: IntoLayout<Layout = L>>(&self, shape: S) -> Result<L, ExpandError>;
871}
872
873impl<const N: usize, const M: usize> BroadcastLayout<NdLayout<M>> for NdLayout<N> {
874 fn broadcast<S: IntoLayout<Layout = NdLayout<M>>>(
875 &self,
876 shape: S,
877 ) -> Result<NdLayout<M>, ExpandError> {
878 let shape: [usize; M] = shape.as_ref().try_into().unwrap();
879 if !self.can_broadcast_to(&shape) {
880 return Err(ExpandError::ShapeMismatch);
881 }
882 let mut strides = [0usize; M];
883 for (i, stride) in broadcast_strides(&self.shape(), &self.strides(), &shape).enumerate() {
884 strides[i] = stride;
885 }
886
887 Ok(NdLayout { shape, strides })
888 }
889}
890
891impl<const N: usize> BroadcastLayout<DynLayout> for NdLayout<N> {
892 fn broadcast<S: IntoLayout<Layout = DynLayout>>(
893 &self,
894 shape: S,
895 ) -> Result<DynLayout, ExpandError> {
896 let dyn_layout: DynLayout = self.into();
897 dyn_layout.broadcast(shape.as_ref())
898 }
899}
900
901impl BroadcastLayout<DynLayout> for DynLayout {
902 fn broadcast<S: IntoLayout<Layout = DynLayout>>(
903 &self,
904 shape: S,
905 ) -> Result<DynLayout, ExpandError> {
906 let to_shape = shape.as_ref();
907
908 if !self.can_broadcast_to(to_shape) {
909 return Err(ExpandError::ShapeMismatch);
910 }
911
912 let mut shape_and_strides = SmallVec::with_capacity(to_shape.len() * 2);
913 shape_and_strides.extend(to_shape.iter().copied());
914 shape_and_strides.extend(broadcast_strides(self.shape(), self.strides(), to_shape));
915
916 Ok(DynLayout { shape_and_strides })
917 }
918}
919
920impl<const N: usize> BroadcastLayout<NdLayout<N>> for DynLayout {
921 fn broadcast<S: IntoLayout<Layout = NdLayout<N>>>(
922 &self,
923 shape: S,
924 ) -> Result<NdLayout<N>, ExpandError> {
925 let dyn_broadcast = self.broadcast(shape.as_ref())?;
926 let layout = (&dyn_broadcast)
927 .try_into()
928 .map_err(|_| ExpandError::ShapeMismatch)?;
929 Ok(layout)
930 }
931}
932
933impl<const N: usize> FromShape for NdLayout<N> {
934 fn from_shape(shape: [usize; N]) -> Self {
935 Self {
936 shape,
937 strides: Self::contiguous_strides(shape),
938 }
939 }
940}
941
942impl<const N: usize> MutLayout for NdLayout<N> {
943 fn from_shape_and_strides(
944 shape: Self::Shape<'_>,
945 strides: Self::Strides<'_>,
946 overlap: OverlapPolicy,
947 ) -> Result<Self, FromDataError> {
948 let layout = NdLayout { shape, strides };
949
950 match overlap {
951 OverlapPolicy::DisallowOverlap => {
952 if may_have_internal_overlap(layout.shape, layout.strides) {
953 return Err(FromDataError::MayOverlap);
954 }
955 }
956 OverlapPolicy::AllowOverlap => {}
957 }
958
959 Ok(layout)
960 }
961
962 fn move_axis(&mut self, from: usize, to: usize) {
963 assert!(from < N && to < N);
964 let mut dyn_layout = self.as_dyn();
965 dyn_layout.move_axis(from, to);
966 *self = NdLayout::try_from(&dyn_layout).unwrap();
967 }
968
969 fn permuted(&self, dims: [usize; N]) -> NdLayout<N> {
970 assert!(is_valid_permutation(N, &dims), "permutation is invalid");
971 let mut shape = [0; N];
972 let mut strides = [0; N];
973 for i in 0..N {
974 shape[i] = self.shape[dims[i]];
975 strides[i] = self.strides[dims[i]];
976 }
977 NdLayout { shape, strides }
978 }
979
980 fn resize_dim(&mut self, dim: usize, size: usize) {
981 self.shape[dim] = size;
982 }
983
984 fn transposed(&self) -> NdLayout<N> {
985 let dims = std::array::from_fn(|i| N - i - 1);
986 self.permuted(dims)
987 }
988
989 fn slice<const M: usize>(
990 &self,
991 range: &[SliceItem],
992 ) -> Result<(Range<usize>, NdLayout<M>), SliceError> {
993 if self.ndim() < range.len() {
994 return Err(SliceError::TooManyDims {
995 ndim: self.ndim(),
996 range_ndim: range.len(),
997 });
998 }
999
1000 let mut shape: [usize; M] = [0; M];
1001 let mut strides: [usize; M] = [0; M];
1002
1003 let (ndim, offset) =
1004 slice_layout(self.shape, self.strides, &mut shape, &mut strides, range)?;
1005
1006 if ndim != M {
1007 return Err(SliceError::OutputDimsMismatch {
1008 actual: ndim,
1009 expected: M,
1010 });
1011 }
1012
1013 let layout = NdLayout { shape, strides };
1014 Ok((offset..offset + layout.min_data_len(), layout))
1015 }
1016
1017 fn slice_dyn(&self, range: &[SliceItem]) -> Result<(Range<usize>, DynLayout), SliceError> {
1018 self.as_dyn().slice_dyn(range)
1019 }
1020
1021 fn squeezed(&self) -> DynLayout {
1022 self.as_dyn().squeezed()
1023 }
1024
1025 fn split(&self, axis: usize, mid: usize) -> ((Range<usize>, Self), (Range<usize>, Self)) {
1026 assert!(axis < self.ndim());
1027 assert!(mid <= self.size(axis));
1028
1029 let left_shape = std::array::from_fn(|i| if i == axis { mid } else { self.shape[i] });
1030 let right_shape = std::array::from_fn(|i| {
1031 if i == axis {
1032 self.size(axis) - mid
1033 } else {
1034 self.shape[i]
1035 }
1036 });
1037
1038 let left = NdLayout {
1039 shape: left_shape,
1040 strides: self.strides,
1041 };
1042 let right = NdLayout {
1043 shape: right_shape,
1044 strides: self.strides,
1045 };
1046
1047 let mid_offset = mid * self.strides[axis];
1048 let left_offsets = 0..left.min_data_len();
1049 let end_offset = self.min_data_len();
1050
1051 let right_offsets = if right.is_empty() {
1052 end_offset..end_offset
1053 } else {
1054 mid_offset..end_offset
1055 };
1056
1057 ((left_offsets, left), (right_offsets, right))
1058 }
1059}
1060
1061impl FromShape for DynLayout {
1062 fn from_shape(shape: &[usize]) -> Self {
1063 DynLayout {
1064 shape_and_strides: Self::contiguous_shape_and_strides(shape),
1065 }
1066 }
1067}
1068
1069impl MutLayout for DynLayout {
1070 fn from_shape_and_strides(
1071 shape: &[usize],
1072 strides: &[usize],
1073 overlap: OverlapPolicy,
1074 ) -> Result<Self, FromDataError> {
1075 let mut shape_and_strides = SmallVec::with_capacity(shape.len() + strides.len());
1076 shape_and_strides.extend_from_slice(shape);
1077 shape_and_strides.extend_from_slice(strides);
1078 let layout = DynLayout { shape_and_strides };
1079
1080 match overlap {
1081 OverlapPolicy::DisallowOverlap => {
1082 if may_have_internal_overlap(layout.shape(), layout.strides()) {
1083 return Err(FromDataError::MayOverlap);
1084 }
1085 }
1086 OverlapPolicy::AllowOverlap => {}
1087 }
1088
1089 Ok(layout)
1090 }
1091
1092 fn move_axis(&mut self, from: usize, to: usize) {
1093 let ndim = self.ndim();
1094 assert!(from < ndim && to < ndim);
1095
1096 let size = self.shape_and_strides.remove(from);
1097 let stride = self.shape_and_strides.remove(ndim - 1 + from);
1098 self.shape_and_strides.insert(to, size);
1099 self.shape_and_strides.insert(ndim + to, stride);
1100 }
1101
1102 fn permuted(&self, order: &[usize]) -> DynLayout {
1103 let mut permuted = self.clone();
1104 permuted.permute(order);
1105 permuted
1106 }
1107
1108 fn resize_dim(&mut self, dim: usize, size: usize) {
1109 self.shape_and_strides[dim] = size;
1110 }
1111
1112 fn transposed(&self) -> DynLayout {
1113 let mut transposed = self.clone();
1114 transposed.transpose();
1115 transposed
1116 }
1117
1118 fn slice<const M: usize>(
1119 &self,
1120 range: &[SliceItem],
1121 ) -> Result<(Range<usize>, NdLayout<M>), SliceError> {
1122 let (offset_range, dyn_layout) = self.slice_dyn(range)?;
1123 let nd_layout =
1124 NdLayout::try_from(&dyn_layout).map_err(|_| SliceError::OutputDimsMismatch {
1125 actual: dyn_layout.ndim(),
1126 expected: M,
1127 })?;
1128 Ok((offset_range, nd_layout))
1129 }
1130
1131 fn slice_dyn(&self, range: &[SliceItem]) -> Result<(Range<usize>, DynLayout), SliceError> {
1132 if self.ndim() < range.len() {
1133 return Err(SliceError::TooManyDims {
1134 ndim: self.ndim(),
1135 range_ndim: range.len(),
1136 });
1137 }
1138
1139 let out_dims = self.ndim()
1140 - range
1141 .iter()
1142 .filter(|item| matches!(item, SliceItem::Index(_)))
1143 .count();
1144 let mut shape_and_strides = smallvec![0; out_dims * 2];
1145 let (out_shape, out_strides) = shape_and_strides.as_mut_slice().split_at_mut(out_dims);
1146
1147 let (_ndim, offset) =
1148 slice_layout(self.shape(), self.strides(), out_shape, out_strides, range)?;
1149
1150 let layout = Self { shape_and_strides };
1151 Ok((offset..offset + layout.min_data_len(), layout))
1152 }
1153
1154 fn squeezed(&self) -> DynLayout {
1155 let shape = self.shape().iter().copied().filter(|&size| size != 1);
1156 let strides = self
1157 .shape()
1158 .iter()
1159 .zip(self.strides())
1160 .filter_map(|(&size, &stride)| if size != 1 { Some(stride) } else { None });
1161 DynLayout {
1162 shape_and_strides: shape.chain(strides).collect(),
1163 }
1164 }
1165
1166 fn split(&self, axis: usize, mid: usize) -> ((Range<usize>, Self), (Range<usize>, Self)) {
1167 assert!(axis < self.ndim());
1168 assert!(mid <= self.size(axis));
1169
1170 let mut left_shape_strides: SmallVec<[usize; 8]> = (0..self.ndim())
1171 .map(|i| if i == axis { mid } else { self.size(i) })
1172 .collect();
1173 left_shape_strides.extend_from_slice(self.strides());
1174
1175 let mut right_shape_strides: SmallVec<[usize; 8]> = (0..self.ndim())
1176 .map(|i| {
1177 if i == axis {
1178 self.size(axis) - mid
1179 } else {
1180 self.size(i)
1181 }
1182 })
1183 .collect();
1184 right_shape_strides.extend_from_slice(self.strides());
1185
1186 let left = DynLayout {
1187 shape_and_strides: left_shape_strides,
1188 };
1189 let right = DynLayout {
1190 shape_and_strides: right_shape_strides,
1191 };
1192
1193 let mid_offset = mid * self.stride(axis);
1194 let left_offsets = 0..left.min_data_len();
1195 let end_offset = self.min_data_len();
1196
1197 let right_offsets = if right.is_empty() {
1198 end_offset..end_offset
1199 } else {
1200 mid_offset..end_offset
1201 };
1202
1203 ((left_offsets, left), (right_offsets, right))
1204 }
1205}
1206
1207pub trait IntoLayout: AsRef<[usize]> + std::fmt::Debug {
1212 type Layout: MutLayout;
1214
1215 fn into_layout(self) -> Self::Layout;
1217}
1218
1219impl<const N: usize> IntoLayout for [usize; N] {
1220 type Layout = NdLayout<N>;
1221
1222 #[inline]
1223 fn into_layout(self) -> NdLayout<N> {
1224 NdLayout::from_shape(self)
1225 }
1226}
1227
1228impl IntoLayout for &[usize] {
1229 type Layout = DynLayout;
1230
1231 #[inline]
1232 fn into_layout(self) -> DynLayout {
1233 DynLayout::from_shape(self)
1234 }
1235}
1236
1237pub trait ResizeLayout: MutLayout {
1243 fn insert_axis(&mut self, index: usize);
1246
1247 #[track_caller]
1254 fn remove_axis(&mut self, index: usize) {
1255 assert!(
1256 self.size(index) == 1,
1257 "cannot remove axis of size {}",
1258 self.size(index)
1259 );
1260 self.remove_axis_of_any_size(index)
1261 }
1262
1263 fn remove_axis_of_any_size(&mut self, index: usize);
1268
1269 fn merge_axes(&mut self);
1274}
1275
1276impl ResizeLayout for DynLayout {
1277 fn insert_axis(&mut self, index: usize) {
1278 let ndim = self.ndim();
1279 let new_size = 1;
1280
1281 let (max_stride, size_for_max_stride) = self
1285 .strides()
1286 .iter()
1287 .copied()
1288 .zip(self.shape().iter().copied())
1289 .max_by_key(|(stride, _size)| *stride)
1290 .unwrap_or((1, 1));
1291 let new_stride = max_stride * size_for_max_stride;
1292
1293 self.shape_and_strides.insert(index, new_size);
1294 self.shape_and_strides.insert(ndim + 1 + index, new_stride);
1295 }
1296
1297 fn remove_axis_of_any_size(&mut self, index: usize) {
1298 self.shape_and_strides.remove(index);
1299 self.shape_and_strides.remove(self.ndim() + index);
1300 }
1301
1302 fn merge_axes(&mut self) {
1303 let merged = merge_axes(&self.shape(), &self.strides());
1304 self.shape_and_strides = merged
1305 .iter()
1306 .map(|dim| dim.0)
1307 .chain(merged.iter().map(|dim| dim.1))
1308 .collect();
1309 }
1310}
1311
1312pub trait AsIndex<L: Layout> {
1318 fn as_index(&self) -> L::Index<'_>;
1320}
1321
1322impl<T: AsRef<[usize]>> AsIndex<DynLayout> for T {
1323 fn as_index(&self) -> &[usize] {
1324 self.as_ref()
1325 }
1326}
1327
1328impl<const N: usize> AsIndex<NdLayout<N>> for [usize; N] {
1329 fn as_index(&self) -> [usize; N] {
1330 *self
1331 }
1332}
1333
1334impl AsIndex<NdLayout<1>> for usize {
1335 fn as_index(&self) -> [usize; 1] {
1336 [*self]
1337 }
1338}
1339
1340pub trait AsShape<L: Layout> {
1342 fn as_shape(&self) -> L::Shape<'_>;
1344}
1345
1346impl<T: AsRef<[usize]>> AsShape<DynLayout> for T {
1347 fn as_shape(&self) -> &[usize] {
1348 self.as_ref()
1349 }
1350}
1351
1352impl<const N: usize> AsShape<NdLayout<N>> for [usize; N] {
1353 fn as_shape(&self) -> [usize; N] {
1354 *self
1355 }
1356}
1357
1358impl AsShape<NdLayout<1>> for usize {
1359 fn as_shape(&self) -> [usize; 1] {
1360 [*self]
1361 }
1362}
1363
1364pub trait RemoveDim {
1366 type Output: MutLayout;
1367
1368 fn remove_dim(&self, dim: usize) -> Self::Output;
1370}
1371
1372impl<R: RemoveDim> RemoveDim for &R {
1373 type Output = R::Output;
1374
1375 fn remove_dim(&self, dim: usize) -> Self::Output {
1376 (*self).remove_dim(dim)
1377 }
1378}
1379
1380impl RemoveDim for DynLayout {
1381 type Output = DynLayout;
1382
1383 fn remove_dim(&self, dim: usize) -> DynLayout {
1384 let ndim = self.ndim();
1385 assert!(ndim > 0, "cannot remove axis from tensor with 0 dims");
1386
1387 let shape = (0..ndim - 1).map(|i| {
1388 if i < dim {
1389 self.size(i)
1390 } else {
1391 self.size(i + 1)
1392 }
1393 });
1394 let strides = (0..ndim - 1).map(|i| {
1395 if i < dim {
1396 self.stride(i)
1397 } else {
1398 self.stride(i + 1)
1399 }
1400 });
1401 DynLayout {
1402 shape_and_strides: shape.chain(strides).collect(),
1403 }
1404 }
1405}
1406
1407macro_rules! impl_remove_dim {
1408 ($in_dims:expr, $out_dims:expr) => {
1409 impl RemoveDim for NdLayout<$in_dims> {
1410 type Output = NdLayout<$out_dims>;
1411
1412 fn remove_dim(&self, dim: usize) -> Self::Output {
1413 let shape = std::array::from_fn(|i| {
1414 if i < dim {
1415 self.shape[i]
1416 } else {
1417 self.shape[i + 1]
1418 }
1419 });
1420 let strides = std::array::from_fn(|i| {
1421 if i < dim {
1422 self.strides[i]
1423 } else {
1424 self.strides[i + 1]
1425 }
1426 });
1427 NdLayout { shape, strides }
1428 }
1429 }
1430 };
1431}
1432
1433impl_remove_dim!(1, 0);
1434impl_remove_dim!(2, 1);
1435impl_remove_dim!(3, 2);
1436impl_remove_dim!(4, 3);
1437impl_remove_dim!(5, 4);
1438
1439pub trait InsertDim {
1441 type Output: MutLayout;
1442
1443 fn insert_dim(&self, dim: usize) -> Self::Output;
1445}
1446
1447impl InsertDim for DynLayout {
1448 type Output = DynLayout;
1449
1450 fn insert_dim(&self, dim: usize) -> DynLayout {
1451 let mut clone = self.clone();
1452 clone.insert_axis(dim);
1453 clone
1454 }
1455}
1456
1457macro_rules! impl_insert_dim {
1458 ($in_dims:expr, $out_dims:expr) => {
1459 impl InsertDim for NdLayout<$in_dims> {
1460 type Output = NdLayout<$out_dims>;
1461
1462 fn insert_dim(&self, dim: usize) -> Self::Output {
1463 let new_stride = self
1464 .shape
1465 .iter()
1466 .zip(self.strides.iter())
1467 .map(|(s, st)| s * st)
1468 .max()
1469 .unwrap_or(1);
1470
1471 let shape = std::array::from_fn(|i| {
1472 if i < dim {
1473 self.shape[i]
1474 } else if i == dim {
1475 1
1476 } else {
1477 self.shape[i - 1]
1478 }
1479 });
1480 let strides = std::array::from_fn(|i| {
1481 if i < dim {
1482 self.strides[i]
1483 } else if i == dim {
1484 new_stride
1485 } else {
1486 self.strides[i - 1]
1487 }
1488 });
1489 NdLayout { shape, strides }
1490 }
1491 }
1492 };
1493}
1494
1495impl_insert_dim!(0, 1);
1496impl_insert_dim!(1, 2);
1497impl_insert_dim!(2, 3);
1498impl_insert_dim!(3, 4);
1499impl_insert_dim!(4, 5);
1500
1501pub trait SliceWith<R: IntoSliceItems, IdxCount: OptionalUInt> {
1506 type Layout: MutLayout;
1508
1509 fn slice_with(&self, range: R) -> Result<(Range<usize>, Self::Layout), SliceError>;
1515}
1516
1517impl<R: IntoSliceItems, L: MutLayout> SliceWith<R, Unknown> for L {
1518 type Layout = DynLayout;
1519
1520 fn slice_with(&self, range: R) -> Result<(Range<usize>, Self::Layout), SliceError> {
1521 self.slice_dyn(range.into_slice_items().as_ref())
1522 }
1523}
1524
1525impl<R: IntoSliceItems, const N: usize> SliceWith<R, U0> for NdLayout<N> {
1526 type Layout = NdLayout<N>;
1527
1528 fn slice_with(&self, range: R) -> Result<(Range<usize>, Self::Layout), SliceError> {
1529 self.slice(range.into_slice_items().as_ref())
1530 }
1531}
1532
1533macro_rules! impl_slice_with_dynlayout {
1534 ($range_ndim:ty) => {
1535 impl<R: IntoSliceItems> SliceWith<R, $range_ndim> for DynLayout {
1536 type Layout = DynLayout;
1537
1538 fn slice_with(&self, range: R) -> Result<(Range<usize>, Self::Layout), SliceError> {
1539 self.slice_dyn(range.into_slice_items().as_ref())
1540 }
1541 }
1542 };
1543}
1544
1545impl_slice_with_dynlayout!(U0);
1546impl_slice_with_dynlayout!(U1);
1547impl_slice_with_dynlayout!(U2);
1548impl_slice_with_dynlayout!(U3);
1549impl_slice_with_dynlayout!(U4);
1550impl_slice_with_dynlayout!(U5);
1551
1552macro_rules! impl_slice_with {
1553 ($ndim:literal, $range_ndim:ty, $out_ndim:literal) => {
1554 impl<R: IntoSliceItems> SliceWith<R, $range_ndim> for NdLayout<$ndim> {
1555 type Layout = NdLayout<$out_ndim>;
1556
1557 fn slice_with(&self, range: R) -> Result<(Range<usize>, Self::Layout), SliceError> {
1558 self.slice(range.into_slice_items().as_ref())
1559 }
1560 }
1561 };
1562}
1563
1564impl_slice_with!(1, U1, 0);
1565impl_slice_with!(2, U1, 1);
1566impl_slice_with!(2, U2, 0);
1567impl_slice_with!(3, U1, 2);
1568impl_slice_with!(3, U2, 1);
1569impl_slice_with!(3, U3, 0);
1570impl_slice_with!(4, U1, 3);
1571impl_slice_with!(4, U2, 2);
1572impl_slice_with!(4, U3, 1);
1573impl_slice_with!(4, U4, 0);
1574impl_slice_with!(5, U1, 4);
1575impl_slice_with!(5, U2, 3);
1576impl_slice_with!(5, U3, 2);
1577impl_slice_with!(5, U4, 1);
1578impl_slice_with!(5, U5, 0);
1579
1580#[cfg(test)]
1581mod tests {
1582 use rten_testing::TestCases;
1583
1584 use std::ops::Range;
1585
1586 use super::OverlapPolicy;
1587 use crate::SliceItem;
1588 use crate::errors::{ReshapeError, SliceError};
1589 use crate::layout::{DynLayout, FromShape, Layout, MutLayout, NdLayout, ResizeLayout};
1590
1591 fn layout_with_strides<const N: usize>(shape: [usize; N], strides: [usize; N]) -> NdLayout<N> {
1592 NdLayout::from_shape_and_strides(shape, strides, OverlapPolicy::AllowOverlap).unwrap()
1593 }
1594
1595 #[test]
1596 fn test_is_broadcast() {
1597 let layout = DynLayout::from_shape(&[5, 5]);
1599 assert!(!layout.is_broadcast());
1600
1601 let layout = DynLayout::from_shape(&[5, 0]);
1603 assert!(!layout.is_broadcast());
1604
1605 let layout =
1607 DynLayout::from_shape_and_strides(&[5, 5], &[0, 0], OverlapPolicy::AllowOverlap)
1608 .unwrap();
1609 assert!(layout.is_broadcast());
1610 }
1611
1612 #[test]
1613 fn test_from_shape_and_strides() {
1614 #[derive(Debug)]
1615 struct Case<'a> {
1616 shape: &'a [usize],
1617 strides: &'a [usize],
1618 }
1619
1620 let cases = [
1621 Case {
1623 shape: &[10, 10],
1624 strides: &[10, 1],
1625 },
1626 Case {
1628 shape: &[10, 10],
1629 strides: &[10, 0],
1630 },
1631 ];
1632
1633 cases.test_each(|case| {
1634 let layout = DynLayout::from_shape_and_strides(
1635 case.shape,
1636 case.strides,
1637 OverlapPolicy::AllowOverlap,
1638 )
1639 .unwrap();
1640 assert_eq!(layout.shape(), case.shape);
1641 assert_eq!(layout.strides(), case.strides);
1642 })
1643 }
1644
1645 #[test]
1646 fn test_index_axis() {
1647 #[derive(Debug)]
1648 struct Case {
1649 layout: NdLayout<2>,
1650 axis: usize,
1651 index: usize,
1652 expected: (usize, NdLayout<1>), }
1654
1655 let cases = [
1656 Case {
1657 layout: NdLayout::from_shape([3, 4]),
1658 axis: 0,
1659 index: 1,
1660 expected: (4, layout_with_strides([4], [1])),
1661 },
1662 Case {
1663 layout: NdLayout::from_shape([3, 4]),
1664 axis: 1,
1665 index: 2,
1666 expected: (2, layout_with_strides([3], [4])),
1667 },
1668 Case {
1671 layout: NdLayout::from_shape([0, 3]),
1672 axis: 1,
1673 index: 1,
1674 expected: (0, layout_with_strides([0], [3])),
1675 },
1676 Case {
1677 layout: layout_with_strides([3, 0], [1, 3]),
1678 axis: 0,
1679 index: 1,
1680 expected: (0, layout_with_strides([0], [3])),
1681 },
1682 ];
1683
1684 cases.test_each(|case| {
1685 let Case {
1686 layout,
1687 axis,
1688 index,
1689 expected,
1690 } = case;
1691
1692 let (expected_start, expected_layout) = expected;
1693
1694 let (offsets, sliced_layout) = layout.index_axis(*axis, *index);
1695 assert_eq!(sliced_layout, *expected_layout);
1696 assert_eq!(offsets.start, *expected_start);
1697 assert_eq!(offsets.len(), expected_layout.min_data_len());
1698
1699 let (_, sliced_layout_dyn) = layout.as_dyn().index_axis(*axis, *index);
1700 assert_eq!(sliced_layout_dyn, expected_layout.as_dyn());
1701 })
1702 }
1703
1704 #[test]
1705 #[should_panic(expected = "axis < self.ndim()")]
1706 fn test_index_axis_invalid_axis() {
1707 NdLayout::from_shape([2, 3]).index_axis(2, 0);
1708 }
1709
1710 #[test]
1711 #[should_panic(expected = "index < self.size(axis)")]
1712 fn test_index_axis_invalid_index() {
1713 NdLayout::from_shape([2, 3]).index_axis(0, 3);
1714 }
1715
1716 #[test]
1717 fn test_move_axis() {
1718 let mut layout = DynLayout::from_shape(&[2, 4, 8]);
1719 assert_eq!(layout.strides(), [32, 8, 1]);
1720
1721 layout.move_axis(1, 0);
1722 assert_eq!(layout.shape(), [4, 2, 8]);
1723 assert_eq!(layout.strides(), [8, 32, 1]);
1724
1725 layout.move_axis(0, 1);
1726 assert_eq!(layout.shape(), [2, 4, 8]);
1727 assert_eq!(layout.strides(), [32, 8, 1]);
1728
1729 layout.move_axis(2, 1);
1730 assert_eq!(layout.shape(), [2, 8, 4]);
1731 assert_eq!(layout.strides(), [32, 1, 8]);
1732 }
1733
1734 #[test]
1735 #[should_panic]
1736 fn test_move_axis_invalid_from() {
1737 let mut layout = DynLayout::from_shape(&[2, 4, 8]);
1738 layout.move_axis(3, 0);
1739 }
1740
1741 #[test]
1742 #[should_panic]
1743 fn test_move_axis_invalid_to() {
1744 let mut layout = DynLayout::from_shape(&[2, 4, 8]);
1745 layout.move_axis(0, 3);
1746 }
1747
1748 #[test]
1749 #[should_panic(expected = "permutation is invalid")]
1750 fn test_permute_invalid_len() {
1751 let mut layout = DynLayout::from_shape(&[5, 5]);
1752 layout.permute(&[1, 0, 3]);
1753 }
1754
1755 #[test]
1756 #[should_panic(expected = "permutation is invalid")]
1757 fn test_permute_too_few_dims() {
1758 let mut layout = DynLayout::from_shape(&[5, 5]);
1759 layout.permute(&[1]);
1760 }
1761
1762 #[test]
1763 #[should_panic(expected = "permutation is invalid")]
1764 fn test_permute_repeated_dims() {
1765 let mut layout = DynLayout::from_shape(&[5, 5]);
1766 layout.permute(&[1, 1]);
1767 }
1768
1769 #[test]
1770 fn test_remove_axis_of_any_size() {
1771 let shape = [1, 2, 3, 4];
1772 for d in 0..shape.len() {
1773 let mut layout = DynLayout::from_shape(&shape);
1774 let (expected_shape, expected_strides): (Vec<usize>, Vec<usize>) = layout
1775 .shape()
1776 .iter()
1777 .zip(layout.strides())
1778 .enumerate()
1779 .filter_map(|(i, (size, stride))| if i != d { Some((size, stride)) } else { None })
1780 .unzip();
1781
1782 layout.remove_axis_of_any_size(d);
1783
1784 assert_eq!(layout.shape(), expected_shape);
1785 assert_eq!(layout.strides(), expected_strides);
1786 }
1787 }
1788
1789 #[test]
1790 fn test_reshaped() {
1791 #[derive(Debug)]
1792 struct Case<'a> {
1793 layout: DynLayout,
1794 new_shape: &'a [usize],
1795 for_copy: bool,
1796 error: Option<ReshapeError>,
1797 }
1798
1799 let cases = [
1800 Case {
1802 layout: DynLayout::from_shape(&[2, 2]),
1803 new_shape: &[4],
1804 for_copy: false,
1805 error: None,
1806 },
1807 Case {
1808 layout: DynLayout::from_shape(&[2, 2]).transposed(),
1809 new_shape: &[4],
1810 for_copy: false,
1811 error: Some(ReshapeError::NotContiguous),
1812 },
1813 Case {
1814 layout: DynLayout::from_shape(&[2, 2]),
1815 new_shape: &[3],
1816 for_copy: false,
1817 error: Some(ReshapeError::LengthMismatch),
1818 },
1819 Case {
1821 layout: DynLayout::from_shape(&[2, 2]).transposed(),
1822 new_shape: &[4],
1823 for_copy: true,
1824 error: None,
1825 },
1826 Case {
1827 layout: DynLayout::from_shape(&[2, 2]),
1828 new_shape: &[3],
1829 for_copy: false,
1830 error: Some(ReshapeError::LengthMismatch),
1831 },
1832 ];
1833
1834 cases.test_each(|case| {
1835 let Case {
1836 layout,
1837 new_shape,
1838 for_copy,
1839 error,
1840 } = case;
1841
1842 let reshaped = if *for_copy {
1843 layout.reshaped_for_copy(*new_shape)
1844 } else {
1845 layout.reshaped_for_view(*new_shape)
1846 };
1847
1848 assert_eq!(reshaped.as_ref().err(), error.as_ref());
1849 if let Ok(new_layout) = reshaped {
1850 assert_eq!(new_layout.shape(), *new_shape);
1851 }
1852 })
1853 }
1854
1855 #[test]
1856 fn test_squeezed() {
1857 let layout = DynLayout::from_shape(&[1, 1, 10, 20]);
1858 let squeezed = layout.squeezed();
1859 assert_eq!(squeezed.shape(), &[10, 20]);
1860 assert_eq!(squeezed.strides(), &[20, 1]);
1861 }
1862
1863 #[test]
1864 fn test_slice_axis() {
1865 #[derive(Clone, Debug)]
1866 struct Case<'a> {
1867 shape: &'a [usize],
1868 axis: usize,
1869 range: Range<usize>,
1870 sliced_shape: &'a [usize],
1871 offsets: Range<usize>,
1872 }
1873
1874 let cases = [Case {
1875 shape: &[3, 5],
1876 axis: 1,
1877 range: 2..4,
1878 sliced_shape: &[3, 2],
1879 offsets: 2..14,
1880 }];
1881
1882 cases.test_each_clone(|case| {
1883 let Case {
1884 shape,
1885 axis,
1886 range,
1887 sliced_shape,
1888 offsets,
1889 } = case;
1890
1891 let layout = DynLayout::from_shape(shape);
1892 let (offset_range, sliced_layout) = layout.slice_axis(axis, range).unwrap();
1893 assert_eq!(sliced_layout.shape(), sliced_shape);
1894 assert_eq!(sliced_layout.strides(), layout.strides());
1895 assert_eq!(offset_range, offsets);
1896 })
1897 }
1898
1899 #[test]
1900 fn test_slice_axis_invalid() {
1901 #[derive(Debug)]
1902 struct Case<'a> {
1903 shape: &'a [usize],
1904 axis: usize,
1905 range: Range<usize>,
1906 expected: SliceError,
1907 }
1908
1909 let cases = [
1910 Case {
1911 shape: &[1, 2, 3],
1912 axis: 4,
1913 range: 0..1,
1914 expected: SliceError::InvalidAxis { axis: 4 },
1915 },
1916 Case {
1917 shape: &[1, 2, 3],
1918 axis: 0,
1919 range: 0..2,
1920 expected: SliceError::InvalidRange {
1921 axis: 0,
1922 range: (0..2).into(),
1923 size: 1,
1924 },
1925 },
1926 ];
1927
1928 cases.test_each(|case| {
1929 let layout = DynLayout::from_shape(case.shape);
1930 let result = layout.slice_axis(case.axis, case.range.clone());
1931 assert_eq!(result, Err(case.expected.clone()));
1932 })
1933 }
1934
1935 #[test]
1936 fn test_slice_invalid() {
1937 #[derive(Debug)]
1938 struct Case<'a> {
1939 layout: DynLayout,
1940 ranges: &'a [SliceItem],
1941 expected: SliceError,
1942 }
1943
1944 let cases = [
1945 Case {
1946 layout: DynLayout::from_shape(&[3, 5]),
1947 ranges: &[SliceItem::Index(4), SliceItem::Index(0)],
1948 expected: SliceError::InvalidIndex {
1949 axis: 0,
1950 index: 4,
1951 size: 3,
1952 },
1953 },
1954 Case {
1955 layout: DynLayout::from_shape(&[3, 5]),
1956 ranges: &[SliceItem::Range((1..4).into()), SliceItem::Index(0)],
1957 expected: SliceError::InvalidRange {
1958 axis: 0,
1959 range: (1..4).into(),
1960 size: 3,
1961 },
1962 },
1963 Case {
1964 layout: DynLayout::from_shape(&[3, 5]),
1965 ranges: &[SliceItem::Index(-4)],
1966 expected: SliceError::InvalidIndex {
1967 axis: 0,
1968 index: -4,
1969 size: 3,
1970 },
1971 },
1972 Case {
1973 layout: DynLayout::from_shape(&[3, 5]),
1974 ranges: &[SliceItem::Range((4..).into()), SliceItem::Index(0)],
1975 expected: SliceError::InvalidRange {
1976 axis: 0,
1977 range: (4..).into(),
1978 size: 3,
1979 },
1980 },
1981 Case {
1982 layout: DynLayout::from_shape(&[3, 5]),
1983 ranges: &[SliceItem::full_range(), SliceItem::range(0, None, -1)],
1984 expected: SliceError::InvalidStep { axis: 1, step: -1 },
1985 },
1986 ];
1987
1988 cases.test_each(|case| {
1989 let result = case.layout.slice_dyn(case.ranges);
1990 assert_eq!(result, Err(case.expected.clone()));
1991 })
1992 }
1993
1994 #[test]
1995 fn test_size_stride() {
1996 let layout = DynLayout::from_shape(&[10, 20, 30]);
1997 for (dim, (&size, &stride)) in layout.shape().iter().zip(layout.strides()).enumerate() {
1998 assert_eq!(layout.size(dim), size);
1999 assert_eq!(layout.stride(dim), stride);
2000 }
2001 }
2002
2003 #[test]
2004 fn test_split() {
2005 #[derive(Debug)]
2006 struct Case {
2007 shape: [usize; 2],
2008 strides: Option<[usize; 2]>,
2009 axis: usize,
2010 mid: usize,
2011 }
2012
2013 let mut cases = Vec::new();
2014
2015 let shape = [4, 2];
2017 for axis in 0..shape.len() {
2018 for mid in 0..shape[axis] {
2019 cases.push(Case {
2020 shape,
2021 axis,
2022 mid,
2023 strides: None,
2024 });
2025 }
2026 }
2027
2028 cases.push(Case {
2030 shape: [0, 0],
2031 strides: None,
2032 axis: 0,
2033 mid: 0,
2034 });
2035
2036 cases.push(Case {
2039 shape: [1, 4],
2040 strides: Some([10, 0]),
2041 axis: 0,
2042 mid: 1,
2043 });
2044
2045 fn check_split<L: MutLayout>(layout: L, axis: usize, mid: usize) {
2046 let (left, right) = layout.split(axis, mid);
2047 let (left_offsets, left_layout) = left;
2048 let (right_offsets, right_layout) = right;
2049
2050 assert_eq!(left_layout.strides(), layout.strides());
2051 assert_eq!(right_layout.strides(), layout.strides());
2052
2053 assert_eq!(left_offsets.len(), left_layout.min_data_len());
2054 assert_eq!(right_offsets.len(), right_layout.min_data_len());
2055
2056 let orig_len = layout.min_data_len();
2057 assert!(left_offsets.start <= orig_len && left_offsets.end <= orig_len);
2058 assert!(right_offsets.start <= orig_len && right_offsets.end <= orig_len);
2059
2060 for i in 0..layout.ndim() {
2061 assert_eq!(
2062 left_layout.size(i),
2063 if i == axis { mid } else { layout.size(i) }
2064 );
2065 assert_eq!(
2066 right_layout.size(i),
2067 if i == axis {
2068 layout.size(i) - mid
2069 } else {
2070 layout.size(i)
2071 }
2072 );
2073 }
2074 }
2075
2076 cases.test_each(|case| {
2077 let Case {
2078 shape,
2079 strides,
2080 axis,
2081 mid,
2082 } = case;
2083
2084 let layout = if let Some(strides) = strides {
2085 NdLayout::from_shape_and_strides(*shape, *strides, OverlapPolicy::AllowOverlap)
2086 .unwrap()
2087 } else {
2088 NdLayout::from_shape(*shape)
2089 };
2090 let dyn_layout = if let Some(strides) = strides {
2091 DynLayout::from_shape_and_strides(
2092 shape.as_slice(),
2093 strides.as_slice(),
2094 OverlapPolicy::AllowOverlap,
2095 )
2096 .unwrap()
2097 } else {
2098 DynLayout::from_shape(shape.as_slice())
2099 };
2100
2101 check_split(layout, *axis, *mid);
2102 check_split(dyn_layout, *axis, *mid);
2103 })
2104 }
2105
2106 #[test]
2107 fn test_merge_axes() {
2108 #[derive(Debug)]
2109 struct Case<'a> {
2110 shape: &'a [usize],
2111 strides: &'a [usize],
2112 merged_shape: &'a [usize],
2113 merged_strides: &'a [usize],
2114 }
2115
2116 let cases = [
2117 Case {
2119 shape: &[],
2120 strides: &[],
2121 merged_shape: &[],
2122 merged_strides: &[],
2123 },
2124 Case {
2126 shape: &[10],
2127 strides: &[2],
2128 merged_shape: &[10],
2129 merged_strides: &[2],
2130 },
2131 Case {
2133 shape: &[10, 10],
2134 strides: &[10, 1],
2135 merged_shape: &[100],
2136 merged_strides: &[1],
2137 },
2138 Case {
2140 shape: &[10, 10],
2141 strides: &[1, 10],
2142 merged_shape: &[10, 10],
2143 merged_strides: &[1, 10],
2144 },
2145 Case {
2147 shape: &[1, 10, 10],
2148 strides: &[10, 1, 10],
2149 merged_shape: &[10, 10],
2150 merged_strides: &[1, 10],
2151 },
2152 Case {
2154 shape: &[2, 1, 1, 2],
2155 strides: &[2, 2, 2, 1],
2156 merged_shape: &[4],
2157 merged_strides: &[1],
2158 },
2159 Case {
2163 shape: &[2, 1, 1, 2],
2164 strides: &[2, 4, 4, 1],
2165 merged_shape: &[4],
2166 merged_strides: &[1],
2167 },
2168 ];
2169
2170 cases.test_each(|case| {
2171 let mut layout = DynLayout::from_shape_and_strides(
2172 case.shape,
2173 case.strides,
2174 OverlapPolicy::AllowOverlap,
2175 )
2176 .unwrap();
2177 layout.merge_axes();
2178 assert_eq!(layout.shape(), case.merged_shape);
2179 assert_eq!(layout.strides(), case.merged_strides);
2180 })
2181 }
2182}