1use crate::core_ops::{Operation, ViewKind};
4use crate::{Tensor, TensorElement};
5use std::sync::Arc;
6use torsh_core::error::{Result, TorshError};
7
8#[derive(Debug, Clone)]
10pub enum TensorIndex {
11 Index(i64),
13 Range(Option<i64>, Option<i64>, Option<i64>), All,
17 List(Vec<i64>),
19 Mask(Tensor<bool>),
21 Ellipsis,
23 NewAxis,
25}
26
27impl TensorIndex {
28 pub fn range(start: Option<i64>, stop: Option<i64>) -> Self {
30 TensorIndex::Range(start, stop, None)
31 }
32
33 pub fn range_step(start: Option<i64>, stop: Option<i64>, step: i64) -> Self {
35 TensorIndex::Range(start, stop, Some(step))
36 }
37}
38
39impl<T: TensorElement> Tensor<T> {
41 pub fn index(&self, indices: &[TensorIndex]) -> Result<Self> {
43 let consuming_indices = indices
45 .iter()
46 .filter(|idx| !matches!(idx, TensorIndex::NewAxis | TensorIndex::Ellipsis))
47 .count();
48
49 if consuming_indices > self.ndim() {
50 return Err(TorshError::InvalidArgument(format!(
51 "Too many indices for tensor: tensor has {} dimensions but {} consuming indices were provided",
52 self.ndim(),
53 consuming_indices
54 )));
55 }
56
57 let expanded_indices = self.expand_ellipsis(indices)?;
59
60 let mut output_shape = Vec::new();
62 let mut slices = Vec::new();
63 let mut input_dim_idx = 0; for index in expanded_indices.iter() {
66 if let TensorIndex::NewAxis = index {
67 output_shape.push(1);
69 slices.push((0, 1, 1));
70 continue;
72 }
73
74 let dim_size = if input_dim_idx < self.ndim() {
76 self.shape().dims()[input_dim_idx]
77 } else {
78 return Err(TorshError::InvalidArgument(format!(
79 "Index {} beyond tensor dimensions (tensor has {} dimensions)",
80 input_dim_idx,
81 self.ndim()
82 )));
83 };
84
85 match index {
86 TensorIndex::Index(idx) => {
87 let idx = if *idx < 0 {
89 (dim_size as i64 + idx) as usize
90 } else {
91 *idx as usize
92 };
93
94 if idx >= dim_size {
95 return Err(TorshError::IndexOutOfBounds {
96 index: idx,
97 size: dim_size,
98 });
99 }
100
101 slices.push((idx, idx + 1, 1));
102 input_dim_idx += 1;
104 }
105 TensorIndex::Range(start, stop, step) => {
106 let step = step.unwrap_or(1);
107 if step == 0 {
108 return Err(TorshError::InvalidArgument(
109 "Step cannot be zero".to_string(),
110 ));
111 }
112
113 let start = start
114 .map(|s| {
115 if s < 0 {
116 (dim_size as i64 + s).max(0) as usize
117 } else {
118 s.min(dim_size as i64) as usize
119 }
120 })
121 .unwrap_or(0);
122
123 let stop = stop
124 .map(|s| {
125 if s < 0 {
126 (dim_size as i64 + s).max(0) as usize
127 } else {
128 s.min(dim_size as i64) as usize
129 }
130 })
131 .unwrap_or(dim_size);
132
133 let size = if step > 0 {
134 ((stop as i64 - start as i64 + step - 1) / step).max(0) as usize
135 } else {
136 ((stop as i64 - start as i64 + step + 1) / step).max(0) as usize
137 };
138
139 output_shape.push(size);
140 slices.push((start, stop, step as usize));
141 input_dim_idx += 1;
142 }
143 TensorIndex::All => {
144 output_shape.push(dim_size);
145 slices.push((0, dim_size, 1));
146 input_dim_idx += 1;
147 }
148 TensorIndex::List(indices_list) => {
149 for &idx in indices_list {
151 let normalized_idx = if idx < 0 {
152 (dim_size as i64 + idx) as usize
153 } else {
154 idx as usize
155 };
156
157 if normalized_idx >= dim_size {
158 return Err(TorshError::IndexOutOfBounds {
159 index: normalized_idx,
160 size: dim_size,
161 });
162 }
163 }
164
165 output_shape.push(indices_list.len());
166 slices.push((0, indices_list.len(), 0)); input_dim_idx += 1;
169 }
170 TensorIndex::Mask(mask) => {
171 if mask.ndim() != 1 {
173 return Err(TorshError::InvalidArgument(
174 "Boolean mask must be 1D for single dimension indexing".to_string(),
175 ));
176 }
177
178 if mask.numel() != dim_size {
179 return Err(TorshError::ShapeMismatch {
180 expected: vec![dim_size],
181 got: mask.shape().dims().to_vec(),
182 });
183 }
184
185 let mask_data = mask.to_vec()?;
187 let true_count = mask_data.iter().filter(|&&x| x).count();
188
189 output_shape.push(true_count);
190 slices.push((0, true_count, 0)); input_dim_idx += 1;
193 }
194 TensorIndex::NewAxis => {
195 return Err(TorshError::InvalidArgument(
197 "NewAxis should be handled before this point".to_string(),
198 ));
199 }
200 TensorIndex::Ellipsis => {
201 return Err(TorshError::InvalidArgument(
203 "Ellipsis should be expanded before processing".to_string(),
204 ));
205 }
206 }
207 }
208
209 if output_shape.is_empty() {
211 output_shape.push(1);
212 }
213
214 let narrow = self.narrow_geometry(&expanded_indices, &slices);
217
218 let (mut result, index_map) = if expanded_indices
220 .iter()
221 .any(|idx| matches!(idx, TensorIndex::List(_) | TensorIndex::Mask(_)))
222 {
223 self.extract_advanced_indexing(&expanded_indices, &output_shape)?
224 } else {
225 self.extract_basic_indexing(
226 &expanded_indices,
227 &output_shape,
228 &slices,
229 narrow.is_none(),
230 )?
231 };
232 match narrow {
233 Some((dim, start)) => self.record_view(&mut result, ViewKind::Narrow { dim, start }),
235 None => self.record_gather(&mut result, index_map),
240 }
241 Ok(result)
242 }
243
244 fn narrow_geometry(
260 &self,
261 indices: &[TensorIndex],
262 slices: &[(usize, usize, usize)],
263 ) -> Option<(usize, usize)> {
264 let ndim = self.ndim();
265 if ndim == 0 || indices.len() != ndim || slices.len() != ndim {
266 return None;
267 }
268 let shape = self.shape();
269 let dims = shape.dims();
270
271 let mut narrowed: Option<(usize, usize)> = None;
272 for (axis, index) in indices.iter().enumerate() {
273 if !matches!(index, TensorIndex::All | TensorIndex::Range(..)) {
274 return None;
275 }
276 let (start, stop, step) = slices[axis];
277 if step != 1 {
278 return None;
279 }
280 if start == 0 && stop == dims[axis] {
281 continue;
282 }
283 if narrowed.is_some() {
284 return None;
286 }
287 narrowed = Some((axis, start));
288 }
289 Some(narrowed.unwrap_or((0, 0)))
291 }
292
293 pub(crate) fn record_gather(&self, result: &mut Self, index_map: Vec<usize>) {
296 if crate::should_record_grad(self.requires_grad) {
297 result.requires_grad = true;
298 result.operation = Operation::Gather {
299 input: Arc::new(self.clone()),
300 index_map: Arc::new(index_map),
301 };
302 }
303 }
304
305 fn extract_basic_indexing(
313 &self,
314 indices: &[TensorIndex],
315 output_shape: &[usize],
316 slices: &[(usize, usize, usize)],
317 record_index_map: bool,
318 ) -> Result<(Self, Vec<usize>)> {
319 let input_data = self.to_vec()?;
320
321 let output_size = output_shape.iter().product();
322 let mut output_data = Vec::with_capacity(output_size);
323 let record = record_index_map && crate::should_record_grad(self.requires_grad);
326 let mut index_map = Vec::with_capacity(if record { output_size } else { 0 });
327
328 let input_strides = self.compute_strides();
329 let output_strides = compute_strides_from_shape(output_shape);
330
331 for out_idx in 0..output_size {
332 let mut out_indices = vec![0; output_shape.len()];
334 let mut remaining = out_idx;
335 for (i, &stride) in output_strides.iter().enumerate() {
336 out_indices[i] = remaining / stride;
337 remaining %= stride;
338 }
339
340 let mut input_flat_idx = 0;
342 let mut out_dim = 0;
343 let mut input_dim = 0;
344
345 for (slice_idx, &(start, _, step)) in slices.iter().enumerate() {
346 if slice_idx < indices.len() && matches!(indices[slice_idx], TensorIndex::NewAxis) {
348 out_dim += 1;
349 continue;
350 }
351
352 if input_dim >= input_strides.len() {
354 break;
355 }
356
357 let idx = if slice_idx < indices.len()
358 && matches!(indices[slice_idx], TensorIndex::Index(_))
359 {
360 start
361 } else {
362 start + out_indices[out_dim] * step
363 };
364 input_flat_idx += idx * input_strides[input_dim];
365
366 if !(slice_idx < indices.len()
367 && matches!(indices[slice_idx], TensorIndex::Index(_)))
368 {
369 out_dim += 1;
370 }
371 input_dim += 1;
372 }
373
374 output_data.push(input_data[input_flat_idx]);
375 if record {
376 index_map.push(input_flat_idx);
377 }
378 }
379
380 let result = Self::from_data(output_data, output_shape.to_vec(), self.device)?;
381 Ok((result, index_map))
382 }
383
384 fn extract_advanced_indexing(
390 &self,
391 indices: &[TensorIndex],
392 output_shape: &[usize],
393 ) -> Result<(Self, Vec<usize>)> {
394 let input_data = self.to_vec()?;
395
396 let output_size = output_shape.iter().product();
397 let mut output_data = Vec::with_capacity(output_size);
398 let record = crate::should_record_grad(self.requires_grad);
399 let mut index_map = Vec::with_capacity(if record { output_size } else { 0 });
400
401 let input_strides = self.compute_strides();
402 let output_strides = compute_strides_from_shape(output_shape);
403
404 for out_idx in 0..output_size {
405 let mut out_indices = vec![0; output_shape.len()];
407 let mut remaining = out_idx;
408 for (i, &stride) in output_strides.iter().enumerate() {
409 out_indices[i] = remaining / stride;
410 remaining %= stride;
411 }
412
413 let mut input_flat_idx = 0;
415 let mut out_dim = 0;
416
417 for (dim_idx, index) in indices.iter().enumerate() {
418 if dim_idx >= self.ndim() {
419 break;
420 }
421
422 let input_idx = match index {
423 TensorIndex::Index(idx) => {
424 let dim_size = self.shape().dims()[dim_idx];
425
426 if *idx < 0 {
427 (dim_size as i64 + idx) as usize
428 } else {
429 *idx as usize
430 }
431 }
432 TensorIndex::Range(start, _stop, step) => {
433 let dim_size = self.shape().dims()[dim_idx];
434 let step = step.unwrap_or(1);
435 let start = start
436 .map(|s| {
437 if s < 0 {
438 (dim_size as i64 + s).max(0) as usize
439 } else {
440 s.min(dim_size as i64) as usize
441 }
442 })
443 .unwrap_or(0);
444
445 start + out_indices[out_dim] * (step as usize)
446 }
447 TensorIndex::All => out_indices[out_dim],
448 TensorIndex::List(indices_list) => {
449 let list_idx = out_indices[out_dim];
451 if list_idx >= indices_list.len() {
452 return Err(TorshError::IndexOutOfBounds {
453 index: list_idx,
454 size: indices_list.len(),
455 });
456 }
457
458 let actual_idx = indices_list[list_idx];
459 let dim_size = self.shape().dims()[dim_idx];
460
461 if actual_idx < 0 {
462 (dim_size as i64 + actual_idx) as usize
463 } else {
464 actual_idx as usize
465 }
466 }
467 TensorIndex::Mask(mask) => {
468 let mask_data = mask.to_vec()?;
470
471 let target_true_idx = out_indices[out_dim];
473 let mut true_count = 0;
474 let mut found_idx = None;
475 for (i, &mask_val) in mask_data.iter().enumerate() {
476 if mask_val {
477 if true_count == target_true_idx {
478 found_idx = Some(i);
479 break;
480 }
481 true_count += 1;
482 }
483 }
484
485 match found_idx {
486 Some(idx) => idx,
487 None => {
488 return Err(TorshError::IndexOutOfBounds {
489 index: target_true_idx,
490 size: true_count,
491 });
492 }
493 }
494 }
495 TensorIndex::NewAxis => {
496 continue;
498 }
499 TensorIndex::Ellipsis => {
500 out_indices[out_dim]
502 }
503 };
504
505 input_flat_idx += input_idx * input_strides[dim_idx];
506
507 if !matches!(index, TensorIndex::Index(_) | TensorIndex::NewAxis) {
509 out_dim += 1;
510 }
511 }
512
513 for stride in input_strides
515 .iter()
516 .skip(indices.len())
517 .take(self.ndim() - indices.len())
518 {
519 if out_dim < out_indices.len() {
520 input_flat_idx += out_indices[out_dim] * stride;
521 out_dim += 1;
522 }
523 }
524
525 if input_flat_idx >= input_data.len() {
526 return Err(TorshError::IndexOutOfBounds {
527 index: input_flat_idx,
528 size: input_data.len(),
529 });
530 }
531
532 output_data.push(input_data[input_flat_idx]);
533 if record {
534 index_map.push(input_flat_idx);
535 }
536 }
537
538 let result = Self::from_data(output_data, output_shape.to_vec(), self.device)?;
539 Ok((result, index_map))
540 }
541
542 fn expand_ellipsis(&self, indices: &[TensorIndex]) -> Result<Vec<TensorIndex>> {
544 let mut expanded = Vec::new();
545 let mut found_ellipsis = false;
546
547 let non_expanding_indices = indices
549 .iter()
550 .filter(|idx| !matches!(idx, TensorIndex::Ellipsis | TensorIndex::NewAxis))
551 .count();
552
553 for index in indices {
554 match index {
555 TensorIndex::Ellipsis => {
556 if found_ellipsis {
557 return Err(TorshError::InvalidArgument(
558 "Only one ellipsis (...) is allowed per indexing operation".to_string(),
559 ));
560 }
561 found_ellipsis = true;
562
563 let ellipsis_dims = if self.ndim() >= non_expanding_indices {
565 self.ndim() - non_expanding_indices
566 } else {
567 0
568 };
569
570 for _ in 0..ellipsis_dims {
572 expanded.push(TensorIndex::All);
573 }
574 }
575 _ => {
576 expanded.push(index.clone());
577 }
578 }
579 }
580
581 if !found_ellipsis {
583 let current_dims = expanded
584 .iter()
585 .filter(|idx| !matches!(idx, TensorIndex::NewAxis))
586 .count();
587
588 for _ in current_dims..self.ndim() {
589 expanded.push(TensorIndex::All);
590 }
591 }
592
593 Ok(expanded)
594 }
595
596 pub fn get_1d(&self, index: usize) -> Result<T> {
598 if self.ndim() != 1 {
599 return Err(TorshError::InvalidShape(
600 "get_1d() can only be used on 1D tensors".to_string(),
601 ));
602 }
603
604 if index >= self.shape().dims()[0] {
605 return Err(TorshError::IndexOutOfBounds {
606 index,
607 size: self.shape().dims()[0],
608 });
609 }
610
611 let data = self.data()?;
612 Ok(data[index])
613 }
614
615 pub fn get_2d(&self, row: usize, col: usize) -> Result<T> {
617 if self.ndim() != 2 {
618 return Err(TorshError::InvalidShape(
619 "get_2d() can only be used on 2D tensors".to_string(),
620 ));
621 }
622
623 let shape = self.shape();
624 if row >= shape.dims()[0] || col >= shape.dims()[1] {
625 return Err(TorshError::IndexOutOfBounds {
626 index: row * shape.dims()[1] + col,
627 size: shape.numel(),
628 });
629 }
630
631 let data = self.to_vec()?;
632
633 let index = row * shape.dims()[1] + col;
634 Ok(data[index])
635 }
636
637 pub fn get_3d(&self, x: usize, y: usize, z: usize) -> Result<T> {
639 if self.ndim() != 3 {
640 return Err(TorshError::InvalidShape(
641 "get_3d() can only be used on 3D tensors".to_string(),
642 ));
643 }
644
645 let shape = self.shape();
646 if x >= shape.dims()[0] || y >= shape.dims()[1] || z >= shape.dims()[2] {
647 return Err(TorshError::IndexOutOfBounds {
648 index: x * shape.dims()[1] * shape.dims()[2] + y * shape.dims()[2] + z,
649 size: shape.numel(),
650 });
651 }
652
653 let data = self.to_vec()?;
654
655 let index = x * shape.dims()[1] * shape.dims()[2] + y * shape.dims()[2] + z;
656 Ok(data[index])
657 }
658
659 pub fn set_1d(&mut self, index: usize, value: T) -> Result<()> {
661 if self.ndim() != 1 {
662 return Err(TorshError::InvalidShape(
663 "set_1d() can only be used on 1D tensors".to_string(),
664 ));
665 }
666
667 if index >= self.shape().dims()[0] {
668 return Err(TorshError::IndexOutOfBounds {
669 index,
670 size: self.shape().dims()[0],
671 });
672 }
673
674 let mut data = self.to_vec()?;
675 data[index] = value;
676 *self = Self::from_data(data, self.shape().dims().to_vec(), self.device())?;
677 Ok(())
678 }
679
680 pub fn set_2d(&mut self, row: usize, col: usize, value: T) -> Result<()> {
682 if self.ndim() != 2 {
683 return Err(TorshError::InvalidShape(
684 "set_2d() can only be used on 2D tensors".to_string(),
685 ));
686 }
687
688 let shape = self.shape();
689 if row >= shape.dims()[0] || col >= shape.dims()[1] {
690 return Err(TorshError::IndexOutOfBounds {
691 index: row * shape.dims()[1] + col,
692 size: shape.numel(),
693 });
694 }
695
696 let mut data = self.to_vec()?;
697 let index = row * shape.dims()[1] + col;
698 data[index] = value;
699 *self = Self::from_data(data, self.shape().dims().to_vec(), self.device())?;
700 Ok(())
701 }
702
703 pub fn set_3d(&mut self, x: usize, y: usize, z: usize, value: T) -> Result<()> {
705 if self.ndim() != 3 {
706 return Err(TorshError::InvalidShape(
707 "set_3d() can only be used on 3D tensors".to_string(),
708 ));
709 }
710
711 let shape = self.shape();
712 if x >= shape.dims()[0] || y >= shape.dims()[1] || z >= shape.dims()[2] {
713 return Err(TorshError::IndexOutOfBounds {
714 index: x * shape.dims()[1] * shape.dims()[2] + y * shape.dims()[2] + z,
715 size: shape.numel(),
716 });
717 }
718
719 let mut data = self.to_vec()?;
720 let index = x * shape.dims()[1] * shape.dims()[2] + y * shape.dims()[2] + z;
721 data[index] = value;
722 *self = Self::from_data(data, self.shape().dims().to_vec(), self.device())?;
723 Ok(())
724 }
725
726 pub fn select(&self, dim: i32, index: i64) -> Result<Self> {
728 let ndim = self.ndim() as i32;
729 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
730
731 if dim >= self.ndim() {
732 return Err(TorshError::InvalidArgument(format!(
733 "Dimension {} out of range for tensor with {} dimensions",
734 dim,
735 self.ndim()
736 )));
737 }
738
739 let dim_size = self.shape().dims()[dim] as i64;
740 let index = if index < 0 { dim_size + index } else { index };
741
742 if index < 0 || index >= dim_size {
743 return Err(TorshError::IndexOutOfBounds {
744 index: index as usize,
745 size: dim_size as usize,
746 });
747 }
748
749 let mut indices = Vec::new();
751 for d in 0..self.ndim() {
752 if d == dim {
753 indices.push(TensorIndex::Index(index));
754 } else {
755 indices.push(TensorIndex::All);
756 }
757 }
758
759 self.index(&indices)
761 }
762
763 pub fn slice_with_step(
765 &self,
766 dim: i32,
767 start: Option<i64>,
768 end: Option<i64>,
769 step: Option<i64>,
770 ) -> Result<Self> {
771 let ndim = self.ndim() as i32;
772 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
773
774 if dim >= self.ndim() {
775 return Err(TorshError::InvalidArgument(format!(
776 "Dimension {} out of range for tensor with {} dimensions",
777 dim,
778 self.ndim()
779 )));
780 }
781
782 let mut indices = Vec::new();
784 for d in 0..self.ndim() {
785 if d == dim {
786 indices.push(TensorIndex::Range(start, end, step));
787 } else {
788 indices.push(TensorIndex::All);
789 }
790 }
791
792 self.index(&indices)
794 }
795
796 pub fn narrow(&self, dim: i32, start: i64, length: usize) -> Result<Self> {
844 let ndim = self.ndim() as i32;
845 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
846
847 if dim >= self.ndim() {
848 return Err(TorshError::InvalidArgument(format!(
849 "Dimension {} out of range for tensor with {} dimensions",
850 dim,
851 self.ndim()
852 )));
853 }
854
855 let dim_size = self.shape().dims()[dim] as i64;
856 let start = if start < 0 { dim_size + start } else { start };
857
858 if start < 0 || start >= dim_size {
859 return Err(TorshError::InvalidArgument(format!(
860 "Start index {start} out of range for dimension {dim} with size {dim_size}"
861 )));
862 }
863
864 let end = start + length as i64;
865 if end > dim_size {
866 return Err(TorshError::InvalidArgument(format!(
867 "End index {end} out of range for dimension {dim} with size {dim_size}"
868 )));
869 }
870
871 Ok(self.narrow_view(dim, start as usize, length))
878 }
879
880 pub fn masked_select(&self, mask: &Tensor<bool>) -> Result<Self> {
882 if self.shape() != mask.shape() {
883 return Err(TorshError::ShapeMismatch {
884 expected: self.shape().dims().to_vec(),
885 got: mask.shape().dims().to_vec(),
886 });
887 }
888
889 let self_data = self.data()?;
890 let mask_data = mask.data()?;
891
892 let mut selected_data = Vec::new();
894 for (i, &mask_val) in mask_data.iter().enumerate() {
895 if mask_val {
896 selected_data.push(self_data[i]);
897 }
898 }
899
900 Self::from_data(
902 selected_data.clone(),
903 vec![selected_data.len()],
904 self.device,
905 )
906 }
907
908 pub fn take(&self, indices: &Tensor<i64>) -> Result<Self> {
909 let self_data = self.data()?;
910
911 let indices_data = indices.data()?;
912
913 let self_size = self.shape().numel();
914 let output_shape = indices.shape().dims().to_vec();
915 let output_size = indices.shape().numel();
916 let mut output_data = Vec::with_capacity(output_size);
917
918 for &idx in indices_data.iter() {
920 let idx = if idx < 0 {
921 (self_size as i64 + idx) as usize
922 } else {
923 idx as usize
924 };
925
926 if idx >= self_size {
927 return Err(TorshError::IndexOutOfBounds {
928 index: idx,
929 size: self_size,
930 });
931 }
932
933 output_data.push(self_data[idx]);
934 }
935
936 Self::from_data(output_data, output_shape, self.device)
937 }
938
939 pub fn put(&self, indices: &Tensor<i64>, values: &Self) -> Result<Self> {
941 let self_data = self.data()?;
942
943 let indices_data = indices.data()?;
944 let values_data = values.data()?;
945
946 if indices.shape() != values.shape() {
948 return Err(TorshError::ShapeMismatch {
949 expected: indices.shape().dims().to_vec(),
950 got: values.shape().dims().to_vec(),
951 });
952 }
953
954 let self_size = self.shape().numel();
955 let mut output_data = self_data.clone();
956
957 for (i, &idx) in indices_data.iter().enumerate() {
959 let idx = if idx < 0 {
960 (self_size as i64 + idx) as usize
961 } else {
962 idx as usize
963 };
964
965 if idx >= self_size {
966 return Err(TorshError::IndexOutOfBounds {
967 index: idx,
968 size: self_size,
969 });
970 }
971
972 output_data[idx] = values_data[i];
973 }
974
975 Self::from_data(output_data, self.shape().dims().to_vec(), self.device)
976 }
977
978 pub fn index_select(&self, dim: i32, index: &Tensor<i64>) -> Result<Self> {
986 let ndim = self.ndim() as i32;
987 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
988
989 if dim >= self.ndim() {
990 return Err(TorshError::InvalidArgument(format!(
991 "Dimension {} out of range for tensor with {} dimensions",
992 dim,
993 self.ndim()
994 )));
995 }
996
997 if index.ndim() != 1 {
999 return Err(TorshError::InvalidShape(
1000 "index_select expects a 1D index tensor".to_string(),
1001 ));
1002 }
1003
1004 let mut output_shape = self.shape().dims().to_vec();
1006 output_shape[dim] = index.shape().dims()[0];
1007
1008 let output_size: usize = output_shape.iter().product();
1009 let mut output_data = Vec::with_capacity(output_size);
1010 let record = crate::should_record_grad(self.requires_grad);
1011 let mut index_map = Vec::with_capacity(if record { output_size } else { 0 });
1012
1013 let self_data = self.data()?;
1014
1015 let index_data = index.data()?;
1016
1017 let self_strides = self.compute_strides();
1019 let _output_strides = Self::compute_strides_for_shape(&output_shape);
1020
1021 for out_idx in 0..output_size {
1023 let mut indices = vec![0; self.ndim()];
1025 let mut remaining = out_idx;
1026 for i in (0..self.ndim()).rev() {
1027 indices[i] = remaining % output_shape[i];
1028 remaining /= output_shape[i];
1029 }
1030
1031 let select_idx = indices[dim];
1033 let selected_value = index_data[select_idx] as usize;
1034
1035 if selected_value >= self.shape().dims()[dim] {
1036 return Err(TorshError::IndexOutOfBounds {
1037 index: selected_value,
1038 size: self.shape().dims()[dim],
1039 });
1040 }
1041
1042 indices[dim] = selected_value;
1043
1044 let src_flat_idx = indices
1046 .iter()
1047 .zip(&self_strides)
1048 .map(|(idx, stride)| idx * stride)
1049 .sum::<usize>();
1050
1051 output_data.push(self_data[src_flat_idx]);
1052 if record {
1053 index_map.push(src_flat_idx);
1057 }
1058 }
1059
1060 let mut result = Self::from_data(output_data, output_shape, self.device)?;
1061 if record {
1065 self.record_gather(&mut result, index_map);
1066 }
1067 Ok(result)
1068 }
1069
1070 pub(crate) fn compute_strides(&self) -> Vec<usize> {
1072 Self::compute_strides_for_shape(self.shape().dims())
1073 }
1074
1075 pub(crate) fn compute_strides_for_shape(shape: &[usize]) -> Vec<usize> {
1077 let mut strides = vec![1; shape.len()];
1078 for i in (0..shape.len() - 1).rev() {
1079 strides[i] = strides[i + 1] * shape[i + 1];
1080 }
1081 strides
1082 }
1083}
1084
1085fn compute_strides_from_shape(shape: &[usize]) -> Vec<usize> {
1087 let mut strides = vec![1; shape.len()];
1088 for i in (0..shape.len() - 1).rev() {
1089 strides[i] = strides[i + 1] * shape[i + 1];
1090 }
1091 strides
1092}
1093
1094#[macro_export]
1096macro_rules! idx {
1097 ($idx:expr) => {
1099 vec![TensorIndex::Index($idx)]
1100 };
1101
1102 ($($idx:expr),+ $(,)?) => {
1104 vec![$(TensorIndex::Index($idx)),+]
1105 };
1106}
1107
1108#[macro_export]
1109macro_rules! s {
1110 (..) => {
1112 TensorIndex::All
1113 };
1114
1115 (.. $stop:expr) => {
1117 TensorIndex::range(None, Some($stop))
1118 };
1119
1120 ($start:expr, $stop:expr) => {
1122 TensorIndex::range(Some($start), Some($stop))
1123 };
1124
1125 ($start:expr, $stop:expr, $step:expr) => {
1127 TensorIndex::range_step(Some($start), Some($stop), $step)
1128 };
1129
1130 (ellipsis) => {
1132 TensorIndex::Ellipsis
1133 };
1134
1135 (None) => {
1137 TensorIndex::NewAxis
1138 };
1139}
1140
1141#[macro_export]
1143macro_rules! fancy_idx {
1144 [$($idx:expr),+ $(,)?] => {
1146 TensorIndex::List(vec![$($idx),+])
1147 };
1148}
1149
1150#[macro_export]
1151macro_rules! mask_idx {
1152 [$mask:expr] => {
1154 TensorIndex::Mask($mask)
1155 };
1156}
1157
1158impl<T: TensorElement> Tensor<T> {
1160 pub fn index_with_list(&self, dim: i32, indices: &[i64]) -> Result<Self> {
1162 let ndim = self.ndim() as i32;
1163 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
1164
1165 if dim >= self.ndim() {
1166 return Err(TorshError::InvalidArgument(format!(
1167 "Dimension {} out of range for tensor with {} dimensions",
1168 dim,
1169 self.ndim()
1170 )));
1171 }
1172
1173 let mut index_spec = vec![TensorIndex::All; self.ndim()];
1174 index_spec[dim] = TensorIndex::List(indices.to_vec());
1175
1176 self.index(&index_spec)
1177 }
1178
1179 pub fn index_with_mask(&self, dim: i32, mask: &Tensor<bool>) -> Result<Self> {
1181 let ndim = self.ndim() as i32;
1182 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
1183
1184 if dim >= self.ndim() {
1185 return Err(TorshError::InvalidArgument(format!(
1186 "Dimension {} out of range for tensor with {} dimensions",
1187 dim,
1188 self.ndim()
1189 )));
1190 }
1191
1192 let mut index_spec = vec![TensorIndex::All; self.ndim()];
1193 index_spec[dim] = TensorIndex::Mask(mask.clone());
1194
1195 self.index(&index_spec)
1196 }
1197
1198 pub fn mask_select(&self, mask: &Tensor<bool>) -> Result<Self> {
1200 if self.shape() != mask.shape() {
1201 return Err(TorshError::ShapeMismatch {
1202 expected: self.shape().dims().to_vec(),
1203 got: mask.shape().dims().to_vec(),
1204 });
1205 }
1206
1207 let self_data = self.data()?;
1208
1209 let mask_data = mask.data()?;
1210
1211 let mut selected_data = Vec::new();
1213 for (i, &mask_val) in mask_data.iter().enumerate() {
1214 if mask_val {
1215 selected_data.push(self_data[i]);
1216 }
1217 }
1218
1219 Self::from_data(
1221 selected_data.clone(),
1222 vec![selected_data.len()],
1223 self.device,
1224 )
1225 }
1226
1227 pub fn where_condition<F>(&self, condition: F) -> Result<Tensor<bool>>
1229 where
1230 F: Fn(&T) -> bool,
1231 T: Clone,
1232 {
1233 let data = self.data()?;
1234
1235 let mask_data: Vec<bool> = data.iter().map(condition).collect();
1236
1237 Tensor::from_data(mask_data, self.shape().dims().to_vec(), self.device)
1238 }
1239
1240 pub fn scatter_indexed(&self, dim: i32, index: &Tensor<i64>, src: &Self) -> Result<Self> {
1242 let ndim = self.ndim() as i32;
1243 let dim = if dim < 0 { ndim + dim } else { dim } as usize;
1244
1245 if dim >= self.ndim() {
1246 return Err(TorshError::InvalidArgument(format!(
1247 "Dimension {} out of range for tensor with {} dimensions",
1248 dim,
1249 self.ndim()
1250 )));
1251 }
1252
1253 let self_shape_binding = self.shape();
1254 let self_shape = self_shape_binding.dims();
1255 let index_shape_binding = index.shape();
1256 let index_shape = index_shape_binding.dims();
1257 let src_shape_binding = src.shape();
1258 let src_shape = src_shape_binding.dims();
1259
1260 if index_shape != src_shape {
1262 return Err(TorshError::ShapeMismatch {
1263 expected: index_shape.to_vec(),
1264 got: src_shape.to_vec(),
1265 });
1266 }
1267
1268 if index_shape.len() != self_shape.len() {
1269 return Err(TorshError::InvalidArgument(
1270 "Index tensor must have same number of dimensions as input tensor".to_string(),
1271 ));
1272 }
1273
1274 let mut result_data = self.data()?.clone();
1276 let index_data = index.data()?;
1277 let src_data = src.data()?;
1278 let self_strides = self.compute_strides();
1279
1280 let index_size = index_shape.iter().product();
1281
1282 for flat_idx in 0..index_size {
1284 let mut coords = Vec::new();
1286 let mut temp_idx = flat_idx;
1287
1288 for &dim_size in index_shape.iter().rev() {
1289 coords.push(temp_idx % dim_size);
1290 temp_idx /= dim_size;
1291 }
1292 coords.reverse();
1293
1294 let scatter_idx = index_data[flat_idx];
1296 let dim_size = self_shape[dim] as i64;
1297 let scatter_idx = if scatter_idx < 0 {
1298 dim_size + scatter_idx
1299 } else {
1300 scatter_idx
1301 };
1302
1303 if scatter_idx < 0 || scatter_idx >= dim_size {
1304 return Err(TorshError::IndexOutOfBounds {
1305 index: scatter_idx as usize,
1306 size: dim_size as usize,
1307 });
1308 }
1309
1310 coords[dim] = scatter_idx as usize;
1312 let mut dest_idx = 0;
1313 for (coord, &stride) in coords.iter().zip(self_strides.iter()) {
1314 dest_idx += coord * stride;
1315 }
1316
1317 result_data[dest_idx] = src_data[flat_idx];
1318 }
1319
1320 Self::from_data(result_data, self_shape.to_vec(), self.device)
1321 }
1322}
1323
1324#[cfg(test)]
1325mod tests {
1326 use super::*;
1327 use crate::creation::{tensor_2d, zeros};
1328
1329 #[test]
1330 fn test_index_macros() {
1331 let indices = idx![5];
1333 assert_eq!(indices.len(), 1);
1334
1335 let indices = idx![1, 2, 3];
1337 assert_eq!(indices.len(), 3);
1338
1339 let _all = s![..];
1341 let _range = s![1, 5];
1342 let _range_step = s![1, 10, 2];
1343 let _to = s![..7];
1344
1345 let _fancy = fancy_idx![0, 2, 1];
1347 let _ellipsis = s![ellipsis];
1348 let _newaxis = s![None];
1349 }
1350
1351 #[test]
1352 fn test_get_set() {
1353 let tensor = tensor_2d(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]])
1354 .expect("tensor creation should succeed");
1355
1356 assert_eq!(
1358 tensor.get(&[0, 0]).expect("data access should succeed"),
1359 1.0
1360 );
1361 assert_eq!(
1362 tensor.get(&[0, 1]).expect("data access should succeed"),
1363 2.0
1364 );
1365 assert_eq!(
1366 tensor.get(&[1, 2]).expect("data access should succeed"),
1367 6.0
1368 );
1369
1370 tensor
1372 .set(&[1, 1], 10.0)
1373 .expect("data access should succeed");
1374 assert_eq!(
1375 tensor.get(&[1, 1]).expect("data access should succeed"),
1376 10.0
1377 );
1378
1379 assert!(tensor.get(&[2, 0]).is_err());
1381 assert!(tensor.set(&[0, 3], 0.0).is_err());
1382 }
1383
1384 #[test]
1385 fn test_gather() {
1386 let tensor = tensor_2d(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], &[7.0, 8.0, 9.0]])
1388 .expect("tensor creation should succeed");
1389
1390 let indices = tensor_2d(&[&[0i64, 2, 1], &[1, 0, 2], &[2, 1, 0]])
1392 .expect("tensor creation should succeed");
1393
1394 let result = tensor.gather(1, &indices).expect("gather should succeed");
1395
1396 assert_eq!(
1398 result.get(&[0, 0]).expect("data access should succeed"),
1399 1.0
1400 );
1401 assert_eq!(
1402 result.get(&[0, 1]).expect("data access should succeed"),
1403 3.0
1404 );
1405 assert_eq!(
1406 result.get(&[0, 2]).expect("data access should succeed"),
1407 2.0
1408 );
1409 assert_eq!(
1410 result.get(&[1, 0]).expect("data access should succeed"),
1411 5.0
1412 );
1413 assert_eq!(
1414 result.get(&[1, 1]).expect("data access should succeed"),
1415 4.0
1416 );
1417 assert_eq!(
1418 result.get(&[1, 2]).expect("data access should succeed"),
1419 6.0
1420 );
1421 assert_eq!(
1422 result.get(&[2, 0]).expect("data access should succeed"),
1423 9.0
1424 );
1425 assert_eq!(
1426 result.get(&[2, 1]).expect("data access should succeed"),
1427 8.0
1428 );
1429 assert_eq!(
1430 result.get(&[2, 2]).expect("data access should succeed"),
1431 7.0
1432 );
1433 }
1434
1435 #[test]
1436 fn test_scatter() {
1437 let tensor = zeros::<f32>(&[3, 3]).expect("tensor creation should succeed");
1439
1440 let indices = tensor_2d(&[&[0i64, 2, 1], &[1, 0, 2], &[2, 1, 0]])
1442 .expect("tensor creation should succeed");
1443
1444 let src = tensor_2d(&[&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0], &[7.0, 8.0, 9.0]])
1446 .expect("tensor creation should succeed");
1447
1448 let result = tensor
1449 .scatter(1, &indices, &src)
1450 .expect("scatter should succeed");
1451
1452 assert_eq!(
1454 result.get(&[0, 0]).expect("data access should succeed"),
1455 1.0
1456 );
1457 assert_eq!(
1458 result.get(&[0, 1]).expect("data access should succeed"),
1459 3.0
1460 );
1461 assert_eq!(
1462 result.get(&[0, 2]).expect("data access should succeed"),
1463 2.0
1464 );
1465 assert_eq!(
1466 result.get(&[1, 0]).expect("data access should succeed"),
1467 5.0
1468 );
1469 assert_eq!(
1470 result.get(&[1, 1]).expect("data access should succeed"),
1471 4.0
1472 );
1473 assert_eq!(
1474 result.get(&[1, 2]).expect("data access should succeed"),
1475 6.0
1476 );
1477 assert_eq!(
1478 result.get(&[2, 0]).expect("data access should succeed"),
1479 9.0
1480 );
1481 assert_eq!(
1482 result.get(&[2, 1]).expect("data access should succeed"),
1483 8.0
1484 );
1485 assert_eq!(
1486 result.get(&[2, 2]).expect("data access should succeed"),
1487 7.0
1488 );
1489 }
1490
1491 #[test]
1492 fn test_index_select() {
1493 let tensor = tensor_2d(&[
1495 &[1.0, 2.0, 3.0, 4.0],
1496 &[5.0, 6.0, 7.0, 8.0],
1497 &[9.0, 10.0, 11.0, 12.0],
1498 ])
1499 .expect("tensor creation should succeed");
1500
1501 let row_indices =
1503 crate::creation::tensor_1d(&[0i64, 2]).expect("tensor creation should succeed");
1504 let result = tensor
1505 .index_select(0, &row_indices)
1506 .expect("index_select should succeed");
1507
1508 assert_eq!(result.shape().dims(), &[2, 4]);
1509 assert_eq!(
1510 result.get(&[0, 0]).expect("data access should succeed"),
1511 1.0
1512 );
1513 assert_eq!(
1514 result.get(&[0, 3]).expect("data access should succeed"),
1515 4.0
1516 );
1517 assert_eq!(
1518 result.get(&[1, 0]).expect("data access should succeed"),
1519 9.0
1520 );
1521 assert_eq!(
1522 result.get(&[1, 3]).expect("data access should succeed"),
1523 12.0
1524 );
1525
1526 let col_indices =
1528 crate::creation::tensor_1d(&[1i64, 3]).expect("tensor creation should succeed");
1529 let result = tensor
1530 .index_select(1, &col_indices)
1531 .expect("index_select should succeed");
1532
1533 assert_eq!(result.shape().dims(), &[3, 2]);
1534 assert_eq!(
1535 result.get(&[0, 0]).expect("data access should succeed"),
1536 2.0
1537 );
1538 assert_eq!(
1539 result.get(&[0, 1]).expect("data access should succeed"),
1540 4.0
1541 );
1542 assert_eq!(
1543 result.get(&[2, 0]).expect("data access should succeed"),
1544 10.0
1545 );
1546 assert_eq!(
1547 result.get(&[2, 1]).expect("data access should succeed"),
1548 12.0
1549 );
1550 }
1551
1552 #[test]
1553 fn test_list_indexing() {
1554 let tensor = tensor_2d(&[
1556 &[1.0, 2.0, 3.0, 4.0],
1557 &[5.0, 6.0, 7.0, 8.0],
1558 &[9.0, 10.0, 11.0, 12.0],
1559 ])
1560 .expect("tensor creation should succeed");
1561
1562 let indices = vec![TensorIndex::List(vec![0, 2]), TensorIndex::All];
1564 let result = tensor.index(&indices).expect("indexing should succeed");
1565
1566 assert_eq!(result.shape().dims(), &[2, 4]);
1567 assert_eq!(
1568 result.get(&[0, 0]).expect("data access should succeed"),
1569 1.0
1570 );
1571 assert_eq!(
1572 result.get(&[0, 3]).expect("data access should succeed"),
1573 4.0
1574 );
1575 assert_eq!(
1576 result.get(&[1, 0]).expect("data access should succeed"),
1577 9.0
1578 );
1579 assert_eq!(
1580 result.get(&[1, 3]).expect("data access should succeed"),
1581 12.0
1582 );
1583
1584 let result2 = tensor
1586 .index_with_list(0, &[0, 2])
1587 .expect("index_with_list should succeed");
1588 assert_eq!(result.shape(), result2.shape());
1589 assert_eq!(
1590 result.get(&[0, 0]).expect("data access should succeed"),
1591 result2.get(&[0, 0]).expect("data access should succeed")
1592 );
1593 }
1594
1595 #[test]
1596 fn test_boolean_mask_indexing() {
1597 use crate::creation::tensor_1d;
1598
1599 let tensor =
1601 tensor_1d(&[10.0, 20.0, 30.0, 40.0, 50.0]).expect("tensor creation should succeed");
1602
1603 let mask = Tensor::from_data(
1605 vec![true, false, true, false, true],
1606 vec![5],
1607 crate::DeviceType::Cpu,
1608 )
1609 .expect("tensor creation should succeed");
1610
1611 let result = tensor
1613 .mask_select(&mask)
1614 .expect("mask_select should succeed");
1615 assert_eq!(result.shape().dims(), &[3]);
1616 assert_eq!(result.get(&[0]).expect("data access should succeed"), 10.0);
1617 assert_eq!(result.get(&[1]).expect("data access should succeed"), 30.0);
1618 assert_eq!(result.get(&[2]).expect("data access should succeed"), 50.0);
1619
1620 let result2 = tensor
1622 .index_with_mask(0, &mask)
1623 .expect("index_with_mask should succeed");
1624 assert_eq!(result2.shape().dims(), &[3]);
1625 assert_eq!(result2.get(&[0]).expect("data access should succeed"), 10.0);
1626 assert_eq!(result2.get(&[1]).expect("data access should succeed"), 30.0);
1627 assert_eq!(result2.get(&[2]).expect("data access should succeed"), 50.0);
1628 }
1629
1630 #[test]
1631 fn test_where_condition() {
1632 use crate::creation::tensor_1d;
1633
1634 let tensor = tensor_1d(&[1.0, 2.0, 3.0, 4.0, 5.0]).expect("tensor creation should succeed");
1635
1636 let mask = tensor
1638 .where_condition(|&x| x > 3.0)
1639 .expect("where_condition should succeed");
1640
1641 {
1642 let mask_data = mask.data().expect("data access should succeed");
1643 assert!(!mask_data[0]); assert!(!mask_data[1]); assert!(!mask_data[2]); assert!(mask_data[3]); assert!(mask_data[4]); } let selected = tensor
1652 .mask_select(&mask)
1653 .expect("mask_select should succeed");
1654 assert_eq!(selected.shape().dims(), &[2]);
1655 assert_eq!(selected.get(&[0]).expect("data access should succeed"), 4.0);
1656 assert_eq!(selected.get(&[1]).expect("data access should succeed"), 5.0);
1657 }
1658
1659 #[test]
1660 fn test_newaxis_indexing() {
1661 use crate::creation::tensor_1d;
1662
1663 let tensor = tensor_1d(&[1.0, 2.0, 3.0]).expect("tensor creation should succeed");
1664
1665 let indices = vec![TensorIndex::NewAxis, TensorIndex::All];
1667 let result = tensor.index(&indices).expect("indexing should succeed");
1668 assert_eq!(result.shape().dims(), &[1, 3]);
1669
1670 let indices = vec![TensorIndex::All, TensorIndex::NewAxis];
1672 let result = tensor.index(&indices).expect("indexing should succeed");
1673 assert_eq!(result.shape().dims(), &[3, 1]);
1674
1675 let indices = vec![
1677 TensorIndex::NewAxis,
1678 TensorIndex::All,
1679 TensorIndex::NewAxis,
1680 TensorIndex::NewAxis,
1681 ];
1682 let result = tensor.index(&indices).expect("indexing should succeed");
1683 assert_eq!(result.shape().dims(), &[1, 3, 1, 1]);
1684 }
1685
1686 #[test]
1687 fn test_ellipsis_indexing() {
1688 let tensor =
1690 crate::creation::zeros::<f32>(&[2, 3, 4]).expect("tensor creation should succeed");
1691
1692 let indices = vec![TensorIndex::Index(0), TensorIndex::Ellipsis];
1694 let result = tensor.index(&indices).expect("indexing should succeed");
1695 assert_eq!(result.shape().dims(), &[3, 4]);
1696
1697 let indices = vec![TensorIndex::Index(1), TensorIndex::Ellipsis];
1699 let result = tensor.index(&indices).expect("indexing should succeed");
1700 assert_eq!(result.shape().dims(), &[3, 4]);
1701 }
1702
1703 #[test]
1704 fn test_complex_indexing() {
1705 let tensor = tensor_2d(&[
1707 &[1.0, 2.0, 3.0, 4.0],
1708 &[5.0, 6.0, 7.0, 8.0],
1709 &[9.0, 10.0, 11.0, 12.0],
1710 &[13.0, 14.0, 15.0, 16.0],
1711 ])
1712 .expect("operation should succeed");
1713
1714 let indices = vec![
1716 TensorIndex::List(vec![0, 2, 3]),
1717 TensorIndex::Range(Some(1), Some(4), None),
1718 ];
1719 let result = tensor.index(&indices).expect("indexing should succeed");
1720
1721 assert_eq!(result.shape().dims(), &[3, 3]);
1722 assert_eq!(
1723 result.get(&[0, 0]).expect("data access should succeed"),
1724 2.0
1725 ); assert_eq!(
1727 result.get(&[1, 0]).expect("data access should succeed"),
1728 10.0
1729 ); assert_eq!(
1731 result.get(&[2, 2]).expect("data access should succeed"),
1732 16.0
1733 ); }
1735
1736 #[test]
1737 fn test_negative_indexing() {
1738 use crate::creation::tensor_1d;
1739
1740 let tensor = tensor_1d(&[1.0, 2.0, 3.0, 4.0, 5.0]).expect("tensor creation should succeed");
1741
1742 let indices = vec![TensorIndex::Index(-1)];
1744 let result = tensor.index(&indices).expect("indexing should succeed");
1745 assert_eq!(result.numel(), 1);
1746 assert_eq!(result.item().expect("item extraction should succeed"), 5.0);
1747
1748 let indices = vec![TensorIndex::Range(Some(-3), Some(-1), None)];
1750 let result = tensor.index(&indices).expect("indexing should succeed");
1751 assert_eq!(result.shape().dims(), &[2]);
1752 assert_eq!(result.get(&[0]).expect("data access should succeed"), 3.0);
1753 assert_eq!(result.get(&[1]).expect("data access should succeed"), 4.0);
1754
1755 let indices = vec![TensorIndex::List(vec![-1, -2, 0])];
1757 let result = tensor.index(&indices).expect("indexing should succeed");
1758 assert_eq!(result.shape().dims(), &[3]);
1759 assert_eq!(result.get(&[0]).expect("data access should succeed"), 5.0); assert_eq!(result.get(&[1]).expect("data access should succeed"), 4.0); assert_eq!(result.get(&[2]).expect("data access should succeed"), 1.0); }
1763
1764 #[test]
1770 fn narrow_records_a_geometric_view() {
1771 let source = tensor_2d(&[
1772 &[0.0f32, 1.0, 2.0, 3.0],
1773 &[4.0, 5.0, 6.0, 7.0],
1774 &[8.0, 9.0, 10.0, 11.0],
1775 ])
1776 .expect("tensor creation should succeed")
1777 .requires_grad_(true);
1778
1779 let narrowed = source.narrow(1, 1, 2).expect("narrow should succeed");
1780 assert!(matches!(
1781 narrowed.operation,
1782 Operation::View {
1783 kind: ViewKind::Narrow { dim: 1, start: 1 },
1784 ..
1785 }
1786 ));
1787
1788 let whole = source
1790 .index(&[TensorIndex::All, TensorIndex::All])
1791 .expect("indexing should succeed");
1792 assert!(matches!(
1793 whole.operation,
1794 Operation::View {
1795 kind: ViewKind::Narrow { dim: 0, start: 0 },
1796 ..
1797 }
1798 ));
1799 }
1800
1801 #[test]
1802 fn non_geometric_indexing_keeps_the_gather() {
1803 let source = tensor_2d(&[
1804 &[0.0f32, 1.0, 2.0, 3.0],
1805 &[4.0, 5.0, 6.0, 7.0],
1806 &[8.0, 9.0, 10.0, 11.0],
1807 ])
1808 .expect("tensor creation should succeed")
1809 .requires_grad_(true);
1810
1811 let stepped = source
1813 .slice_with_step(1, Some(0), Some(4), Some(2))
1814 .expect("stepped slice should succeed");
1815 assert!(matches!(stepped.operation, Operation::Gather { .. }));
1816
1817 let corner = source
1819 .index(&[
1820 TensorIndex::Range(Some(1), Some(3), None),
1821 TensorIndex::Range(Some(1), Some(3), None),
1822 ])
1823 .expect("indexing should succeed");
1824 assert!(matches!(corner.operation, Operation::Gather { .. }));
1825
1826 let listed = source
1828 .index(&[TensorIndex::All, TensorIndex::List(vec![0, 0, 3])])
1829 .expect("indexing should succeed");
1830 assert!(matches!(listed.operation, Operation::Gather { .. }));
1831
1832 let row = source
1834 .index(&[TensorIndex::Index(1), TensorIndex::All])
1835 .expect("indexing should succeed");
1836 assert!(matches!(row.operation, Operation::Gather { .. }));
1837 }
1838
1839 #[test]
1840 fn geometric_slices_of_a_detached_tensor_stay_leaves() {
1841 let source = tensor_2d(&[&[0.0f32, 1.0, 2.0], &[3.0, 4.0, 5.0]])
1842 .expect("tensor creation should succeed");
1843 let narrowed = source.narrow(1, 1, 2).expect("narrow should succeed");
1844 assert!(!narrowed.requires_grad());
1845 assert!(matches!(narrowed.operation, Operation::Leaf));
1846 }
1847}