1use crate::core_ops::Operation;
12use crate::{Tensor, TensorElement};
13use std::sync::Arc;
14use torsh_core::error::{Result, TorshError};
15
16impl<T: TensorElement + Copy + Default> Tensor<T> {
17 pub fn stack(tensors: &[Self], dim: isize) -> Result<Self> {
33 if tensors.is_empty() {
34 return Err(TorshError::InvalidArgument(
35 "stack requires at least one tensor".to_string(),
36 ));
37 }
38
39 let first_shape = tensors[0].shape().to_vec();
41 for tensor in tensors.iter().skip(1) {
42 if tensor.shape().dims() != first_shape.as_slice() {
43 return Err(TorshError::ShapeMismatch {
44 expected: first_shape.clone(),
45 got: tensor.shape().to_vec(),
46 });
47 }
48 }
49
50 let ndim = first_shape.len();
51 let dim = if dim < 0 {
52 ((ndim + 1) as isize + dim) as usize
53 } else {
54 dim as usize
55 };
56
57 if dim > ndim {
58 return Err(TorshError::InvalidArgument(format!(
59 "Dimension {} out of range for stacking {}-D tensors",
60 dim, ndim
61 )));
62 }
63
64 let mut output_shape = first_shape.to_vec();
66 output_shape.insert(dim, tensors.len());
67
68 let elem_count: usize = first_shape.iter().product();
70 let mut result_data = Vec::with_capacity(elem_count * tensors.len());
71
72 let outer_size: usize = first_shape[..dim].iter().product();
74 let inner_size: usize = first_shape[dim..].iter().product();
75
76 let sources: Vec<Vec<T>> = tensors
79 .iter()
80 .map(|tensor| tensor.to_vec())
81 .collect::<Result<Vec<_>>>()?;
82
83 for outer in 0..outer_size {
84 for source in &sources {
85 let start = outer * inner_size;
87 let run = source.get(start..start + inner_size).ok_or_else(|| {
88 TorshError::IndexError {
89 index: start + inner_size,
90 size: source.len(),
91 }
92 })?;
93 result_data.extend_from_slice(run);
94 }
95 }
96
97 let device = tensors[0].device.clone();
98 let mut result = Self::from_data(result_data, output_shape, device)?;
99
100 let any_requires_grad = tensors.iter().any(|t| t.requires_grad);
103 if crate::should_record_grad(any_requires_grad) {
104 result.requires_grad = true;
105 result.operation = Operation::Stack {
106 inputs: tensors.iter().map(|t| Arc::new(t.clone())).collect(),
107 dim,
108 };
109 }
110
111 Ok(result)
112 }
113
114 pub fn chunk(&self, chunks: usize, dim: isize) -> Result<Vec<Self>> {
119 if chunks == 0 {
120 return Err(TorshError::InvalidArgument(
121 "chunks must be greater than 0".to_string(),
122 ));
123 }
124
125 let ndim = self.ndim();
126 let dim = if dim < 0 {
127 (ndim as isize + dim) as usize
128 } else {
129 dim as usize
130 };
131
132 if dim >= ndim {
133 return Err(TorshError::InvalidArgument(format!(
134 "Dimension {} out of range for {}-D tensor",
135 dim, ndim
136 )));
137 }
138
139 let dim_size = self.shape().dims()[dim];
140 let chunk_size = (dim_size + chunks - 1) / chunks; let mut result = Vec::new();
143 let mut start = 0;
144
145 while start < dim_size {
146 let end = (start + chunk_size).min(dim_size);
147 let slice_tensor = self.narrow(dim as i32, start as i64, end - start)?;
148 result.push(slice_tensor);
149 start = end;
150 }
151
152 Ok(result)
153 }
154
155 pub fn split(&self, split_size: usize, dim: isize) -> Result<Vec<Self>> {
160 if split_size == 0 {
161 return Err(TorshError::InvalidArgument(
162 "split_size must be greater than 0".to_string(),
163 ));
164 }
165
166 let ndim = self.ndim();
167 let dim = if dim < 0 {
168 (ndim as isize + dim) as usize
169 } else {
170 dim as usize
171 };
172
173 if dim >= ndim {
174 return Err(TorshError::InvalidArgument(format!(
175 "Dimension {} out of range for {}-D tensor",
176 dim, ndim
177 )));
178 }
179
180 let dim_size = self.shape().dims()[dim];
181 let mut result = Vec::new();
182 let mut start = 0;
183
184 while start < dim_size {
185 let size = split_size.min(dim_size - start);
186 let slice_tensor = self.narrow(dim as i32, start as i64, size)?;
187 result.push(slice_tensor);
188 start += split_size;
189 }
190
191 Ok(result)
192 }
193
194 pub fn flip(&self, dims: &[isize]) -> Result<Self> {
199 if dims.is_empty() {
200 return Ok(self.clone());
201 }
202
203 let ndim = self.ndim();
204
205 let mut norm_dims = Vec::new();
207 for &dim in dims {
208 let d = if dim < 0 {
209 (ndim as isize + dim) as usize
210 } else {
211 dim as usize
212 };
213
214 if d >= ndim {
215 return Err(TorshError::InvalidArgument(format!(
216 "Dimension {} out of range for {}-D tensor",
217 dim, ndim
218 )));
219 }
220 norm_dims.push(d);
221 }
222
223 let data = self.to_vec()?;
225 let shape = self.shape().to_vec();
226 let mut result_data = vec![T::default(); data.len()];
227
228 let mut strides = vec![1; ndim];
230 for i in (0..ndim - 1).rev() {
231 strides[i] = strides[i + 1] * shape[i + 1];
232 }
233
234 for i in 0..data.len() {
236 let mut indices = vec![0; ndim];
237 let mut remainder = i;
238
239 for d in 0..ndim {
240 indices[d] = remainder / strides[d];
241 remainder %= strides[d];
242 }
243
244 for &flip_dim in &norm_dims {
246 indices[flip_dim] = shape[flip_dim] - 1 - indices[flip_dim];
247 }
248
249 let mut flipped_idx = 0;
251 for d in 0..ndim {
252 flipped_idx += indices[d] * strides[d];
253 }
254
255 result_data[flipped_idx] = data[i];
256 }
257
258 Self::from_data(result_data, shape.to_vec(), self.device)
259 }
260
261 pub fn fliplr(&self) -> Result<Self> {
266 if self.ndim() < 2 {
267 return Err(TorshError::InvalidArgument(
268 "fliplr requires at least 2 dimensions".to_string(),
269 ));
270 }
271 self.flip(&[-1])
272 }
273
274 pub fn flipud(&self) -> Result<Self> {
279 if self.ndim() < 1 {
280 return Err(TorshError::InvalidArgument(
281 "flipud requires at least 1 dimension".to_string(),
282 ));
283 }
284 self.flip(&[0])
285 }
286
287 pub fn roll(&self, shifts: &[isize], dims: &[isize]) -> Result<Self> {
292 if shifts.len() != dims.len() {
293 return Err(TorshError::InvalidArgument(
294 "shifts and dims must have the same length".to_string(),
295 ));
296 }
297
298 if dims.is_empty() {
299 let data = self.to_vec()?;
301 let shift = if shifts.is_empty() { 0 } else { shifts[0] };
302 let n = data.len();
303 let shift = ((shift % n as isize) + n as isize) as usize % n;
304
305 let mut result_data = vec![T::default(); n];
306 for (i, &val) in data.iter().enumerate() {
307 result_data[(i + shift) % n] = val;
308 }
309
310 return Self::from_data(result_data, self.shape().dims().to_vec(), self.device);
311 }
312
313 let ndim = self.ndim();
314
315 let mut norm_dims = Vec::new();
317 for &dim in dims {
318 let d = if dim < 0 {
319 (ndim as isize + dim) as usize
320 } else {
321 dim as usize
322 };
323
324 if d >= ndim {
325 return Err(TorshError::InvalidArgument(format!(
326 "Dimension {} out of range for {}-D tensor",
327 dim, ndim
328 )));
329 }
330 norm_dims.push(d);
331 }
332
333 let data = self.to_vec()?;
334 let shape = self.shape().to_vec();
335 let mut result_data = vec![T::default(); data.len()];
336
337 let mut strides = vec![1; ndim];
339 for i in (0..ndim - 1).rev() {
340 strides[i] = strides[i + 1] * shape[i + 1];
341 }
342
343 for i in 0..data.len() {
345 let mut indices = vec![0; ndim];
346 let mut remainder = i;
347
348 for d in 0..ndim {
349 indices[d] = remainder / strides[d];
350 remainder %= strides[d];
351 }
352
353 for (dim_idx, &roll_dim) in norm_dims.iter().enumerate() {
355 let shift = shifts[dim_idx];
356 let dim_size = shape[roll_dim] as isize;
357 let rolled =
358 ((indices[roll_dim] as isize + shift) % dim_size + dim_size) % dim_size;
359 indices[roll_dim] = rolled as usize;
360 }
361
362 let mut rolled_idx = 0;
364 for d in 0..ndim {
365 rolled_idx += indices[d] * strides[d];
366 }
367
368 result_data[rolled_idx] = data[i];
369 }
370
371 Self::from_data(result_data, shape.to_vec(), self.device)
372 }
373
374 pub fn rot90(&self, k: isize, dims: &[isize]) -> Result<Self> {
379 if dims.len() != 2 {
380 return Err(TorshError::InvalidArgument(
381 "dims must contain exactly 2 dimensions".to_string(),
382 ));
383 }
384
385 let ndim = self.ndim();
386 if ndim < 2 {
387 return Err(TorshError::InvalidArgument(
388 "rot90 requires at least 2 dimensions".to_string(),
389 ));
390 }
391
392 let dim0 = if dims[0] < 0 {
394 (ndim as isize + dims[0]) as usize
395 } else {
396 dims[0] as usize
397 };
398
399 let dim1 = if dims[1] < 0 {
400 (ndim as isize + dims[1]) as usize
401 } else {
402 dims[1] as usize
403 };
404
405 if dim0 >= ndim || dim1 >= ndim {
406 return Err(TorshError::InvalidArgument("dims out of range".to_string()));
407 }
408
409 if dim0 == dim1 {
410 return Err(TorshError::InvalidArgument(
411 "dims must be different".to_string(),
412 ));
413 }
414
415 let k = ((k % 4) + 4) % 4;
417
418 let mut result = self.clone();
419 for _ in 0..k {
420 result = result.transpose_view(dim0, dim1)?;
422 result = result.flip(&[dim1 as isize])?;
423 }
424
425 Ok(result)
426 }
427
428 pub fn tile(&self, repeats: &[usize]) -> Result<Self> {
433 if repeats.is_empty() {
434 return Ok(self.clone());
435 }
436
437 let shape = self.shape().to_vec();
438 let ndim = shape.len();
439
440 let mut new_shape = shape.to_vec();
442 if repeats.len() > ndim {
443 let diff = repeats.len() - ndim;
444 for _ in 0..diff {
445 new_shape.insert(0, 1);
446 }
447 }
448
449 let mut output_shape = new_shape.clone();
451 let repeat_offset = if repeats.len() < output_shape.len() {
452 output_shape.len() - repeats.len()
453 } else {
454 0
455 };
456
457 for (i, &rep) in repeats.iter().enumerate() {
458 let idx = repeat_offset + i;
459 if idx < output_shape.len() {
460 output_shape[idx] *= rep;
461 }
462 }
463
464 self.repeat(repeats)
466 }
467
468 pub fn repeat_interleave(&self, repeats: usize, dim: Option<isize>) -> Result<Self> {
483 if repeats == 0 {
484 return Err(TorshError::InvalidArgument(
485 "repeats must be positive".to_string(),
486 ));
487 }
488
489 match dim {
490 None => {
491 let data = self.to_vec()?;
493 let mut result_data = Vec::with_capacity(data.len() * repeats);
494
495 for &val in data.iter() {
496 for _ in 0..repeats {
497 result_data.push(val);
498 }
499 }
500
501 Self::from_data(result_data, vec![data.len() * repeats], self.device)
502 }
503 Some(d) => {
504 let ndim = self.ndim();
505 let dim = if d < 0 {
506 (ndim as isize + d) as usize
507 } else {
508 d as usize
509 };
510
511 if dim >= ndim {
512 return Err(TorshError::InvalidArgument(format!(
513 "Dimension {} out of range for {}-D tensor",
514 d, ndim
515 )));
516 }
517
518 let shape = self.shape().to_vec();
519 let data = self.to_vec()?;
520
521 let mut output_shape = shape.clone();
523 output_shape[dim] *= repeats;
524
525 let dim_size = shape[dim];
527 let outer_size: usize = shape[..dim].iter().product();
528 let inner_size: usize = shape[dim + 1..].iter().product();
529
530 let mut result_data = Vec::with_capacity(data.len() * repeats);
531
532 for outer in 0..outer_size {
533 for d in 0..dim_size {
534 for _ in 0..repeats {
535 for inner in 0..inner_size {
536 let idx = outer * dim_size * inner_size + d * inner_size + inner;
537 result_data.push(data[idx]);
538 }
539 }
540 }
541 }
542
543 Self::from_data(result_data, output_shape, self.device)
544 }
545 }
546 }
547
548 pub fn unflatten(&self, dim: isize, sizes: &[usize]) -> Result<Self> {
563 if sizes.is_empty() {
564 return Err(TorshError::InvalidArgument(
565 "sizes cannot be empty".to_string(),
566 ));
567 }
568
569 let shape = self.shape().to_vec();
570 let ndim = shape.len();
571
572 let dim = if dim < 0 {
574 (ndim as isize + dim) as usize
575 } else {
576 dim as usize
577 };
578
579 if dim >= ndim {
580 return Err(TorshError::InvalidArgument(format!(
581 "Dimension {} out of range for {}-D tensor",
582 dim, ndim
583 )));
584 }
585
586 let sizes_product: usize = sizes.iter().product();
588 if sizes_product != shape[dim] {
589 return Err(TorshError::InvalidArgument(format!(
590 "sizes product {} does not match dimension size {}",
591 sizes_product, shape[dim]
592 )));
593 }
594
595 let mut new_shape = Vec::new();
597 new_shape.extend_from_slice(&shape[..dim]);
598 new_shape.extend_from_slice(sizes);
599 new_shape.extend_from_slice(&shape[dim + 1..]);
600
601 let data = self.to_vec()?;
603 Self::from_data(data, new_shape, self.device)
604 }
605
606 pub fn take_along_dim(&self, indices: &Tensor<i64>, dim: Option<isize>) -> Result<Self> {
622 match dim {
623 None => {
624 let data = self.to_vec()?;
626 let idx_data = indices.to_vec()?;
627
628 let mut result = Vec::with_capacity(idx_data.len());
629
630 for &idx in idx_data.iter() {
631 if idx < 0 || idx as usize >= data.len() {
632 return Err(TorshError::InvalidArgument(format!(
633 "Index {} out of range for tensor with {} elements",
634 idx,
635 data.len()
636 )));
637 }
638 result.push(data[idx as usize]);
639 }
640
641 Self::from_data(result, indices.shape().to_vec(), self.device)
642 }
643 Some(d) => {
644 let ndim = self.ndim();
645 let dim = if d < 0 {
646 (ndim as isize + d) as usize
647 } else {
648 d as usize
649 };
650
651 if dim >= ndim {
652 return Err(TorshError::InvalidArgument(format!(
653 "Dimension {} out of range for {}-D tensor",
654 d, ndim
655 )));
656 }
657
658 let self_shape = self.shape().to_vec();
659 let indices_shape = indices.shape().to_vec();
660
661 if self_shape.len() != indices_shape.len() {
663 return Err(TorshError::ShapeMismatch {
664 expected: self_shape.clone(),
665 got: indices_shape.clone(),
666 });
667 }
668
669 for (i, (&s, &idx_s)) in self_shape.iter().zip(indices_shape.iter()).enumerate() {
670 if i != dim && s != idx_s {
671 return Err(TorshError::ShapeMismatch {
672 expected: self_shape.clone(),
673 got: indices_shape.clone(),
674 });
675 }
676 }
677
678 let data = self.to_vec()?;
679 let idx_data = indices.to_vec()?;
680
681 let dim_size = self_shape[dim];
682 let outer_size: usize = self_shape[..dim].iter().product();
683 let inner_size: usize = self_shape[dim + 1..].iter().product();
684
685 let indices_dim_size = indices_shape[dim];
686 let mut result = Vec::with_capacity(idx_data.len());
687
688 for outer in 0..outer_size {
689 for d in 0..indices_dim_size {
690 for inner in 0..inner_size {
691 let idx_flat =
692 outer * indices_dim_size * inner_size + d * inner_size + inner;
693 let gather_idx = idx_data[idx_flat];
694
695 if gather_idx < 0 || gather_idx as usize >= dim_size {
696 return Err(TorshError::InvalidArgument(format!(
697 "Index {} out of range for dimension size {}",
698 gather_idx, dim_size
699 )));
700 }
701
702 let src_idx = outer * dim_size * inner_size
703 + (gather_idx as usize) * inner_size
704 + inner;
705
706 result.push(data[src_idx]);
707 }
708 }
709 }
710
711 Self::from_data(result, indices_shape, self.device)
712 }
713 }
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use torsh_core::device::DeviceType;
721
722 #[test]
724 fn test_stack_1d() {
725 let a = Tensor::from_data(vec![1.0f32, 2.0], vec![2], DeviceType::Cpu)
726 .expect("failed to create tensor a");
727 let b = Tensor::from_data(vec![3.0f32, 4.0], vec![2], DeviceType::Cpu)
728 .expect("failed to create tensor b");
729
730 let result = Tensor::stack(&[a, b], 0).expect("stack should succeed for 1d tensors");
731
732 assert_eq!(result.shape().dims(), &[2, 2]);
733 let data = result.data().expect("failed to get stacked tensor data");
734 assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0]);
735 }
736
737 #[test]
738 fn test_stack_negative_dim() {
739 let a = Tensor::from_data(vec![1.0f32, 2.0], vec![2], DeviceType::Cpu)
740 .expect("failed to create tensor a");
741 let b = Tensor::from_data(vec![3.0f32, 4.0], vec![2], DeviceType::Cpu)
742 .expect("failed to create tensor b");
743
744 let result = Tensor::stack(&[a, b], -1).expect("stack should succeed with negative dim");
745 assert_eq!(result.shape().dims(), &[2, 2]);
746 }
747
748 #[test]
750 fn test_chunk_even() {
751 let tensor = Tensor::from_data(
752 vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
753 vec![6],
754 DeviceType::Cpu,
755 )
756 .expect("failed to create tensor for chunk_even");
757
758 let chunks = tensor.chunk(3, 0).expect("chunk into 3 should succeed");
759 assert_eq!(chunks.len(), 3);
760 assert_eq!(chunks[0].shape().dims(), &[2]);
761 assert_eq!(
762 chunks[0].data().expect("failed to get chunk 0 data"),
763 vec![1.0, 2.0]
764 );
765 assert_eq!(
766 chunks[1].data().expect("failed to get chunk 1 data"),
767 vec![3.0, 4.0]
768 );
769 assert_eq!(
770 chunks[2].data().expect("failed to get chunk 2 data"),
771 vec![5.0, 6.0]
772 );
773 }
774
775 #[test]
776 fn test_chunk_uneven() {
777 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0, 5.0], vec![5], DeviceType::Cpu)
778 .expect("failed to create tensor for chunk_uneven");
779
780 let chunks = tensor.chunk(2, 0).expect("uneven chunk should succeed");
781 assert_eq!(chunks.len(), 2);
782 assert_eq!(chunks[0].shape().dims(), &[3]);
783 assert_eq!(chunks[1].shape().dims(), &[2]);
784 }
785
786 #[test]
788 fn test_split_even() {
789 let tensor = Tensor::from_data(
790 vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
791 vec![6],
792 DeviceType::Cpu,
793 )
794 .expect("failed to create tensor for split_even");
795
796 let splits = tensor.split(2, 0).expect("split by 2 should succeed");
797 assert_eq!(splits.len(), 3);
798 assert_eq!(
799 splits[0].data().expect("failed to get split 0 data"),
800 vec![1.0, 2.0]
801 );
802 assert_eq!(
803 splits[1].data().expect("failed to get split 1 data"),
804 vec![3.0, 4.0]
805 );
806 }
807
808 #[test]
809 fn test_split_uneven() {
810 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0, 5.0], vec![5], DeviceType::Cpu)
811 .expect("failed to create tensor for split_uneven");
812
813 let splits = tensor.split(2, 0).expect("uneven split should succeed");
814 assert_eq!(splits.len(), 3);
815 assert_eq!(splits[0].shape().dims(), &[2]);
816 assert_eq!(splits[1].shape().dims(), &[2]);
817 assert_eq!(splits[2].shape().dims(), &[1]); }
819
820 #[test]
822 fn test_flip_1d() {
823 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![4], DeviceType::Cpu)
824 .expect("failed to create 1d tensor for flip");
825
826 let result = tensor.flip(&[0]).expect("flip dim 0 should succeed");
827 assert_eq!(
828 result.data().expect("failed to get flipped data"),
829 vec![4.0, 3.0, 2.0, 1.0]
830 );
831 }
832
833 #[test]
834 fn test_flip_2d() {
835 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
836 .expect("failed to create 2d tensor for flip");
837
838 let result = tensor.flip(&[0]).expect("flip 2d dim 0 should succeed");
839 assert_eq!(
840 result.data().expect("failed to get 2d flipped data"),
841 vec![3.0, 4.0, 1.0, 2.0]
842 );
843 }
844
845 #[test]
846 fn test_fliplr() {
847 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
848 .expect("failed to create tensor for fliplr");
849
850 let result = tensor.fliplr().expect("fliplr should succeed");
851 assert_eq!(
852 result.data().expect("failed to get fliplr data"),
853 vec![2.0, 1.0, 4.0, 3.0]
854 );
855 }
856
857 #[test]
858 fn test_flipud() {
859 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
860 .expect("failed to create tensor for flipud");
861
862 let result = tensor.flipud().expect("flipud should succeed");
863 assert_eq!(
864 result.data().expect("failed to get flipud data"),
865 vec![3.0, 4.0, 1.0, 2.0]
866 );
867 }
868
869 #[test]
871 fn test_roll_1d() {
872 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![4], DeviceType::Cpu)
873 .expect("failed to create tensor for roll");
874
875 let result = tensor.roll(&[1], &[0]).expect("roll by 1 should succeed");
876 assert_eq!(
877 result.data().expect("failed to get rolled data"),
878 vec![4.0, 1.0, 2.0, 3.0]
879 );
880 }
881
882 #[test]
883 fn test_roll_negative() {
884 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![4], DeviceType::Cpu)
885 .expect("failed to create tensor for negative roll");
886
887 let result = tensor
888 .roll(&[-1], &[0])
889 .expect("negative roll should succeed");
890 assert_eq!(
891 result.data().expect("failed to get negatively rolled data"),
892 vec![2.0, 3.0, 4.0, 1.0]
893 );
894 }
895
896 #[test]
898 fn test_rot90_once() {
899 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
900 .expect("failed to create tensor for rot90");
901
902 let result = tensor.rot90(1, &[0, 1]).expect("rot90 once should succeed");
903 assert_eq!(result.shape().dims(), &[2, 2]);
904 }
906
907 #[test]
908 fn test_rot90_twice() {
909 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
910 .expect("failed to create tensor for rot90 twice");
911
912 let result = tensor
913 .rot90(2, &[0, 1])
914 .expect("rot90 twice should succeed");
915 assert_eq!(result.shape().dims(), &[2, 2]);
916 assert_eq!(
918 result.data().expect("failed to get rot90 twice data"),
919 vec![4.0, 3.0, 2.0, 1.0]
920 );
921 }
922
923 #[test]
925 fn test_tile_1d() {
926 let tensor = Tensor::from_data(vec![1.0f32, 2.0], vec![2], DeviceType::Cpu)
927 .expect("failed to create tensor for tile 1d");
928
929 let result = tensor.tile(&[2]).expect("tile 1d should succeed");
930 assert_eq!(result.shape().dims(), &[4]);
931 assert_eq!(
932 result.data().expect("failed to get tiled 1d data"),
933 vec![1.0, 2.0, 1.0, 2.0]
934 );
935 }
936
937 #[test]
938 fn test_tile_2d() {
939 let tensor = Tensor::from_data(vec![1.0f32, 2.0], vec![1, 2], DeviceType::Cpu)
940 .expect("failed to create tensor for tile 2d");
941
942 let result = tensor.tile(&[2, 1]).expect("tile 2d should succeed");
943 assert_eq!(result.shape().dims(), &[2, 2]);
944 }
945
946 #[test]
948 fn test_repeat_interleave_flatten() {
949 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0], vec![3], DeviceType::Cpu)
950 .expect("failed to create tensor for repeat_interleave");
951
952 let result = tensor
953 .repeat_interleave(2, None)
954 .expect("repeat_interleave flatten should succeed");
955 assert_eq!(result.shape().dims(), &[6]);
956 assert_eq!(
957 result.data().expect("failed to get repeat_interleave data"),
958 vec![1.0, 1.0, 2.0, 2.0, 3.0, 3.0]
959 );
960 }
961
962 #[test]
963 fn test_repeat_interleave_dim() {
964 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![2, 2], DeviceType::Cpu)
965 .expect("failed to create tensor for repeat_interleave dim");
966
967 let result = tensor
968 .repeat_interleave(2, Some(0))
969 .expect("repeat_interleave along dim 0 should succeed");
970 assert_eq!(result.shape().dims(), &[4, 2]);
971 }
972
973 #[test]
975 fn test_unflatten_basic() {
976 let tensor = Tensor::from_data(
977 vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
978 vec![6],
979 DeviceType::Cpu,
980 )
981 .expect("failed to create tensor for unflatten");
982
983 let result = tensor
984 .unflatten(0, &[2, 3])
985 .expect("unflatten to [2,3] should succeed");
986 assert_eq!(result.shape().dims(), &[2, 3]);
987 assert_eq!(
988 result.data().expect("failed to get unflattened data"),
989 vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
990 );
991 }
992
993 #[test]
994 fn test_unflatten_multiple_dims() {
995 let tensor = Tensor::from_data(vec![1.0f32; 24], vec![24], DeviceType::Cpu)
996 .expect("failed to create tensor for unflatten multiple dims");
997
998 let result = tensor
999 .unflatten(0, &[2, 3, 4])
1000 .expect("unflatten to [2,3,4] should succeed");
1001 assert_eq!(result.shape().dims(), &[2, 3, 4]);
1002 }
1003
1004 #[test]
1006 fn test_take_along_dim_flatten() {
1007 let tensor = Tensor::from_data(vec![1.0f32, 2.0, 3.0, 4.0], vec![4], DeviceType::Cpu)
1008 .expect("failed to create tensor for take_along_dim");
1009
1010 let indices = Tensor::from_data(vec![0i64, 2, 1], vec![3], DeviceType::Cpu)
1011 .expect("failed to create indices tensor");
1012
1013 let result = tensor
1014 .take_along_dim(&indices, None)
1015 .expect("take_along_dim flatten should succeed");
1016 assert_eq!(result.shape().dims(), &[3]);
1017 assert_eq!(
1018 result
1019 .data()
1020 .expect("failed to get take_along_dim flatten data"),
1021 vec![1.0, 3.0, 2.0]
1022 );
1023 }
1024
1025 #[test]
1026 fn test_take_along_dim_2d() {
1027 let tensor = Tensor::from_data(
1028 vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0],
1029 vec![2, 3],
1030 DeviceType::Cpu,
1031 )
1032 .expect("failed to create 2d tensor for take_along_dim");
1033
1034 let indices = Tensor::from_data(vec![0i64, 2, 1, 1, 0, 2], vec![2, 3], DeviceType::Cpu)
1035 .expect("failed to create 2d indices tensor");
1036
1037 let result = tensor
1038 .take_along_dim(&indices, Some(1))
1039 .expect("take_along_dim 2d should succeed");
1040 assert_eq!(result.shape().dims(), &[2, 3]);
1041 assert_eq!(
1044 result.data().expect("failed to get take_along_dim 2d data"),
1045 vec![1.0, 3.0, 2.0, 5.0, 4.0, 6.0]
1046 );
1047 }
1048}