1use crate::core_ops::Tensor;
6use torsh_core::{
7 device::DeviceType,
8 dtype::TensorElement,
9 error::{Result, TorshError},
10};
11
12impl<T: TensorElement + Copy> Tensor<T> {
13 pub fn from_scalar(value: T, shape: &[usize], device: DeviceType) -> Result<Self>
15 where
16 T: Copy,
17 {
18 let numel = shape.iter().product::<usize>();
19 let data = vec![value; numel];
20 Self::from_data(data, shape.to_vec(), device)
21 }
22 pub fn fill_(&mut self, value: T) -> Result<()>
34 where
35 T: Copy,
36 {
37 #[cfg(feature = "gpu")]
38 if self.storage.is_device() {
39 self.make_unique()?;
40 }
41 if !self.is_view() {
42 self.make_unique()?;
43 }
44
45 let numel = self.numel();
46 if self.has_default_layout() {
47 let offset = self.storage_offset;
51 for i in 0..numel {
52 self.storage.set(offset + i, value)?;
53 }
54 return Ok(());
55 }
56 for i in 0..numel {
59 self.set_item_flat(i, value)?;
60 }
61 Ok(())
62 }
63 pub fn zero_(&mut self) -> Result<()>
65 where
66 T: Copy,
67 {
68 self.fill_(T::zero())
69 }
70 pub fn ones_(&mut self) -> Result<()>
72 where
73 T: Copy,
74 {
75 self.fill_(T::one())
76 }
77 pub fn copy_(&mut self, other: &Self) -> Result<()>
82 where
83 T: Copy,
84 {
85 self.copy_from(other)
86 }
87
88 pub fn copy_from(&mut self, other: &Self) -> Result<()>
96 where
97 T: Copy,
98 {
99 if self.shape() != other.shape() {
100 return Err(TorshError::ShapeMismatch {
101 expected: self.shape().dims().to_vec(),
102 got: other.shape().dims().to_vec(),
103 });
104 }
105 let other_data = other.to_vec()?;
109 if self.is_view() {
118 return self.set_slice(0, &other_data);
119 }
120 self.make_unique()?;
121 self.set_slice(0, &other_data)
122 }
123
124 pub fn set_data(&mut self, data: &[T]) -> Result<()>
131 where
132 T: Copy,
133 {
134 let numel = self.numel();
135 if data.len() != numel {
136 return Err(TorshError::InvalidArgument(format!(
137 "set_data: slice has {} elements but the tensor holds {}",
138 data.len(),
139 numel
140 )));
141 }
142 if self.is_view() {
146 return self.set_slice(0, data);
147 }
148 self.make_unique()?;
149 self.set_slice(0, data)
150 }
151 pub fn get_item(&self, indices: &[usize]) -> Result<T>
153 where
154 T: Copy,
155 {
156 if indices.len() != self.ndim() {
157 return Err(TorshError::InvalidArgument(format!(
158 "Expected {} indices, got {}",
159 self.ndim(),
160 indices.len()
161 )));
162 }
163 let binding = self.shape();
164 let shape = binding.dims();
165 for (i, &idx) in indices.iter().enumerate() {
166 if idx >= shape[i] {
167 return Err(TorshError::IndexOutOfBounds {
168 index: idx,
169 size: shape[i],
170 });
171 }
172 }
173 let flat_index = self.multi_to_flat_index(indices)?;
174 self.get_item_flat(flat_index)
175 }
176 pub fn set_item(&mut self, indices: &[usize], value: T) -> Result<()>
182 where
183 T: Copy,
184 {
185 if indices.len() != self.ndim() {
186 return Err(TorshError::InvalidArgument(format!(
187 "Expected {} indices, got {}",
188 self.ndim(),
189 indices.len()
190 )));
191 }
192 let binding = self.shape();
193 let shape = binding.dims();
194 for (i, &idx) in indices.iter().enumerate() {
195 if idx >= shape[i] {
196 return Err(TorshError::IndexOutOfBounds {
197 index: idx,
198 size: shape[i],
199 });
200 }
201 }
202 let flat_index = self.multi_to_flat_index(indices)?;
203 self.set_item_flat(flat_index, value)
204 }
205 pub fn get_item_flat(&self, index: usize) -> Result<T>
207 where
208 T: Copy,
209 {
210 if index >= self.numel() {
211 return Err(TorshError::IndexOutOfBounds {
212 index,
213 size: self.numel(),
214 });
215 }
216 let shape = self.shape();
218 let dims = shape.dims();
219 let ndim = dims.len();
220 let mut multi_dim = vec![0usize; ndim];
221 let mut remaining = index;
222 for i in (0..ndim).rev() {
223 if dims[i] > 0 {
224 multi_dim[i] = remaining % dims[i];
225 remaining /= dims[i];
226 }
227 }
228 let strides = self.strides();
229 let storage_idx = self.storage_offset
230 + multi_dim
231 .iter()
232 .zip(strides.iter())
233 .map(|(&m, &s)| m * s)
234 .sum::<usize>();
235 self.storage.get(storage_idx)
236 }
237 pub fn set_item_flat(&mut self, index: usize, value: T) -> Result<()>
263 where
264 T: Copy,
265 {
266 #[cfg(feature = "gpu")]
267 if self.storage.is_device() {
268 self.make_unique()?;
269 }
270 if !self.is_view() {
271 self.make_unique()?;
272 }
273 if index >= self.numel() {
274 return Err(TorshError::IndexOutOfBounds {
275 index,
276 size: self.numel(),
277 });
278 }
279 let shape = self.shape();
281 let dims = shape.dims();
282 let ndim = dims.len();
283 let mut multi_dim = vec![0usize; ndim];
284 let mut remaining = index;
285 for i in (0..ndim).rev() {
286 if dims[i] > 0 {
287 multi_dim[i] = remaining % dims[i];
288 remaining /= dims[i];
289 }
290 }
291 let strides = self.strides();
292 let storage_idx = self.storage_offset
293 + multi_dim
294 .iter()
295 .zip(strides.iter())
296 .map(|(&m, &s)| m * s)
297 .sum::<usize>();
298 self.storage.set(storage_idx, value)
299 }
300 pub fn multi_to_flat_index(&self, indices: &[usize]) -> Result<usize> {
302 let binding = self.shape();
303 let shape = binding.dims();
304 if indices.len() != shape.len() {
305 return Err(TorshError::InvalidArgument(format!(
306 "Expected {} indices, got {}",
307 shape.len(),
308 indices.len()
309 )));
310 }
311 let mut flat_index = 0;
312 let mut stride = 1;
313 for i in (0..indices.len()).rev() {
314 flat_index += indices[i] * stride;
315 stride *= shape[i];
316 }
317 Ok(flat_index)
318 }
319 pub fn gather(&self, dim: usize, indices: &Tensor<i64>) -> Result<Self> {
331 if dim >= self.ndim() {
332 return Err(TorshError::InvalidArgument(format!(
333 "Dimension {} out of range for tensor with {} dimensions",
334 dim,
335 self.ndim()
336 )));
337 }
338 let record = crate::should_record_grad(self.requires_grad);
339 let self_data = self.to_vec()?;
340 let indices_data = indices.to_vec()?;
341 let mut result_data = Vec::new();
342 let mut index_map = Vec::with_capacity(if record { indices_data.len() } else { 0 });
346 let result_shape = indices.shape().dims().to_vec();
347 if self.ndim() == 1 {
348 for &index in &indices_data {
349 let idx = if index < 0 {
350 (self.shape().dims()[0] as i64 + index) as usize
351 } else {
352 index as usize
353 };
354 if idx >= self.shape().dims()[0] {
355 return Err(TorshError::InvalidArgument(format!(
356 "Index {} out of range for tensor with size {}",
357 index,
358 self.shape().dims()[0]
359 )));
360 }
361 result_data.push(self_data[idx]);
362 if record {
363 index_map.push(idx);
364 }
365 }
366 } else {
367 let self_shape_ref = self.shape();
368 let self_shape = self_shape_ref.dims();
369 let indices_shape_ref = indices.shape();
370 let indices_shape = indices_shape_ref.dims();
371 let dim_size = self_shape[dim];
372 let mut self_strides = vec![1; self_shape.len()];
373 let mut indices_strides = vec![1; indices_shape.len()];
374 for i in (0..self_shape.len() - 1).rev() {
375 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
376 }
377 for i in (0..indices_shape.len() - 1).rev() {
378 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
379 }
380 let total_elements = indices_data.len();
381 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
382 let mut indices_coords = vec![0; indices_shape.len()];
383 let mut temp_i = i;
384 for j in 0..indices_shape.len() {
385 indices_coords[j] = temp_i / indices_strides[j];
386 temp_i %= indices_strides[j];
387 }
388 let idx = if index_value < 0 {
389 (dim_size as i64 + index_value) as usize
390 } else {
391 index_value as usize
392 };
393 if idx >= dim_size {
394 return Err(TorshError::InvalidArgument(format!(
395 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
396 )));
397 }
398 let mut self_coords = indices_coords.clone();
399 if dim < self_coords.len() {
400 self_coords[dim] = idx;
401 }
402 let mut flat_idx = 0;
403 for j in 0..self_coords.len() {
404 flat_idx += self_coords[j] * self_strides[j];
405 }
406 result_data.push(self_data[flat_idx]);
407 if record {
408 index_map.push(flat_idx);
411 }
412 }
413 }
414 let mut result = Self::from_data(result_data, result_shape, self.device)?;
415 if record {
420 self.record_gather(&mut result, index_map);
421 }
422 Ok(result)
423 }
424 pub fn scatter(&self, dim: usize, indices: &Tensor<i64>, src: &Tensor<T>) -> Result<Self> {
426 if dim >= self.ndim() {
427 return Err(TorshError::InvalidArgument(format!(
428 "Dimension {} out of range for tensor with {} dimensions",
429 dim,
430 self.ndim()
431 )));
432 }
433 let mut result_data = self.to_vec()?;
434 let indices_data = indices.to_vec()?;
435 let src_data = src.to_vec()?;
436 if indices_data.len() != src_data.len() {
437 return Err(TorshError::InvalidArgument(
438 "Indices and source tensor must have the same number of elements".to_string(),
439 ));
440 }
441 if self.ndim() == 1 {
442 for (i, &index) in indices_data.iter().enumerate() {
443 let idx = if index < 0 {
444 (self.shape().dims()[0] as i64 + index) as usize
445 } else {
446 index as usize
447 };
448 if idx >= self.shape().dims()[0] {
449 return Err(TorshError::InvalidArgument(format!(
450 "Index {} out of range for tensor with size {}",
451 index,
452 self.shape().dims()[0]
453 )));
454 }
455 result_data[idx] = src_data[i];
456 }
457 } else {
458 let self_shape_ref = self.shape();
459 let self_shape = self_shape_ref.dims();
460 let indices_shape_ref = indices.shape();
461 let indices_shape = indices_shape_ref.dims();
462 let dim_size = self_shape[dim];
463 let mut self_strides = vec![1; self_shape.len()];
464 let mut indices_strides = vec![1; indices_shape.len()];
465 for i in (0..self_shape.len() - 1).rev() {
466 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
467 }
468 for i in (0..indices_shape.len() - 1).rev() {
469 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
470 }
471 let total_elements = indices_data.len();
472 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
473 let mut indices_coords = vec![0; indices_shape.len()];
474 let mut temp_i = i;
475 for j in 0..indices_shape.len() {
476 indices_coords[j] = temp_i / indices_strides[j];
477 temp_i %= indices_strides[j];
478 }
479 let idx = if index_value < 0 {
480 (dim_size as i64 + index_value) as usize
481 } else {
482 index_value as usize
483 };
484 if idx >= dim_size {
485 return Err(TorshError::InvalidArgument(format!(
486 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
487 )));
488 }
489 let mut self_coords = indices_coords.clone();
490 if dim < self_coords.len() {
491 self_coords[dim] = idx;
492 }
493 let mut flat_idx = 0;
494 for j in 0..self_coords.len() {
495 flat_idx += self_coords[j] * self_strides[j];
496 }
497 result_data[flat_idx] = src_data[i];
498 }
499 }
500 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
501 }
502 pub fn scatter_add(&self, dim: usize, indices: &Tensor<i64>, src: &Tensor<T>) -> Result<Self>
523 where
524 T: std::ops::Add<Output = T>,
525 {
526 if dim >= self.ndim() {
527 return Err(TorshError::InvalidArgument(format!(
528 "Dimension {} out of range for tensor with {} dimensions",
529 dim,
530 self.ndim()
531 )));
532 }
533 let mut result_data = self.to_vec()?;
534 let indices_data = indices.to_vec()?;
535 let src_data = src.to_vec()?;
536 if indices_data.len() != src_data.len() {
537 return Err(TorshError::InvalidArgument(
538 "Indices and source tensor must have the same number of elements".to_string(),
539 ));
540 }
541 if self.ndim() == 1 {
542 for (i, &index) in indices_data.iter().enumerate() {
543 let idx = if index < 0 {
544 (self.shape().dims()[0] as i64 + index) as usize
545 } else {
546 index as usize
547 };
548 if idx >= self.shape().dims()[0] {
549 return Err(TorshError::InvalidArgument(format!(
550 "Index {} out of range for tensor with size {}",
551 index,
552 self.shape().dims()[0]
553 )));
554 }
555 result_data[idx] = result_data[idx] + src_data[i];
556 }
557 } else {
558 let self_shape_ref = self.shape();
559 let self_shape = self_shape_ref.dims();
560 let indices_shape_ref = indices.shape();
561 let indices_shape = indices_shape_ref.dims();
562 let dim_size = self_shape[dim];
563 let mut self_strides = vec![1; self_shape.len()];
564 let mut indices_strides = vec![1; indices_shape.len()];
565 for i in (0..self_shape.len() - 1).rev() {
566 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
567 }
568 for i in (0..indices_shape.len() - 1).rev() {
569 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
570 }
571 let total_elements = indices_data.len();
572 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
573 let mut indices_coords = vec![0; indices_shape.len()];
574 let mut temp_i = i;
575 for j in 0..indices_shape.len() {
576 indices_coords[j] = temp_i / indices_strides[j];
577 temp_i %= indices_strides[j];
578 }
579 let idx = if index_value < 0 {
580 (dim_size as i64 + index_value) as usize
581 } else {
582 index_value as usize
583 };
584 if idx >= dim_size {
585 return Err(TorshError::InvalidArgument(format!(
586 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
587 )));
588 }
589 let mut self_coords = indices_coords.clone();
590 if dim < self_coords.len() {
591 self_coords[dim] = idx;
592 }
593 let mut flat_idx = 0;
594 for j in 0..self_coords.len() {
595 flat_idx += self_coords[j] * self_strides[j];
596 }
597 result_data[flat_idx] = result_data[flat_idx] + src_data[i];
598 }
599 }
600 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
601 }
602 pub fn repeat(&self, repeats: &[usize]) -> Result<Self> {
604 if repeats.len() != self.ndim() {
605 return Err(TorshError::InvalidArgument(format!(
606 "Number of repeats {} must match tensor dimensions {}",
607 repeats.len(),
608 self.ndim()
609 )));
610 }
611 let self_data = self.to_vec()?;
612 let shape_binding = self.shape();
613 let self_shape = shape_binding.dims();
614 let new_shape: Vec<usize> = self_shape
615 .iter()
616 .zip(repeats.iter())
617 .map(|(&dim, &repeat)| dim * repeat)
618 .collect();
619 let new_numel = new_shape.iter().product();
620 let mut result_data = Vec::with_capacity(new_numel);
621 for result_idx in 0..new_numel {
622 let mut result_coords = vec![0; new_shape.len()];
623 let mut temp_idx = result_idx;
624 for i in (0..new_shape.len()).rev() {
625 result_coords[i] = temp_idx % new_shape[i];
626 temp_idx /= new_shape[i];
627 }
628 let source_coords: Vec<usize> = result_coords
629 .iter()
630 .zip(self_shape.iter())
631 .map(|(&result_coord, &dim_size)| result_coord % dim_size)
632 .collect();
633 let mut source_idx = 0;
634 let mut stride = 1;
635 for i in (0..self_shape.len()).rev() {
636 source_idx += source_coords[i] * stride;
637 stride *= self_shape[i];
638 }
639 result_data.push(self_data[source_idx]);
640 }
641 Self::from_data(result_data, new_shape, self.device)
642 }
643 pub fn index_add(&self, dim: isize, index: &Tensor<i64>, source: &Self) -> Result<Self>
661 where
662 T: std::ops::Add<Output = T>,
663 {
664 let ndim = self.ndim();
665 let dim = if dim < 0 {
666 (ndim as isize + dim) as usize
667 } else {
668 dim as usize
669 };
670 if dim >= ndim {
671 return Err(TorshError::InvalidArgument(format!(
672 "Dimension {} out of range for {}-D tensor",
673 dim, ndim
674 )));
675 }
676 if index.ndim() != 1 {
677 return Err(TorshError::InvalidArgument(
678 "index must be 1D tensor".to_string(),
679 ));
680 }
681 let index_size = index.shape().dims()[0];
682 let self_shape = self.shape().to_vec();
683 let source_shape = source.shape().to_vec();
684 if source_shape.len() != self_shape.len() {
685 return Err(TorshError::ShapeMismatch {
686 expected: self_shape.clone(),
687 got: source_shape.clone(),
688 });
689 }
690 for (i, (&s, &src_s)) in self_shape.iter().zip(source_shape.iter()).enumerate() {
691 if i == dim {
692 if src_s != index_size {
693 return Err(TorshError::InvalidArgument(format!(
694 "source dimension {} size {} must match index size {}",
695 i, src_s, index_size
696 )));
697 }
698 } else if s != src_s {
699 return Err(TorshError::ShapeMismatch {
700 expected: self_shape.clone(),
701 got: source_shape.clone(),
702 });
703 }
704 }
705 let mut result_data = self.to_vec()?;
706 let source_data = source.to_vec()?;
707 let index_data = index.to_vec()?;
708 let dim_size = self_shape[dim];
709 let outer_size: usize = self_shape[..dim].iter().product();
710 let inner_size: usize = self_shape[dim + 1..].iter().product();
711 for (src_idx_in_dim, &target_idx) in index_data.iter().enumerate() {
712 if target_idx < 0 || target_idx as usize >= dim_size {
713 return Err(TorshError::InvalidArgument(format!(
714 "Index {} out of range for dimension size {}",
715 target_idx, dim_size
716 )));
717 }
718 let target_idx = target_idx as usize;
719 for outer in 0..outer_size {
720 for inner in 0..inner_size {
721 let result_idx =
722 outer * dim_size * inner_size + target_idx * inner_size + inner;
723 let source_idx =
724 outer * index_size * inner_size + src_idx_in_dim * inner_size + inner;
725 result_data[result_idx] = result_data[result_idx] + source_data[source_idx];
726 }
727 }
728 }
729 Self::from_data(result_data, self_shape, self.device)
730 }
731 pub fn index_copy(&self, dim: isize, index: &Tensor<i64>, source: &Self) -> Result<Self> {
749 let ndim = self.ndim();
750 let dim = if dim < 0 {
751 (ndim as isize + dim) as usize
752 } else {
753 dim as usize
754 };
755 if dim >= ndim {
756 return Err(TorshError::InvalidArgument(format!(
757 "Dimension {} out of range for {}-D tensor",
758 dim, ndim
759 )));
760 }
761 if index.ndim() != 1 {
762 return Err(TorshError::InvalidArgument(
763 "index must be 1D tensor".to_string(),
764 ));
765 }
766 let index_size = index.shape().dims()[0];
767 let self_shape = self.shape().to_vec();
768 let source_shape = source.shape().to_vec();
769 if source_shape.len() != self_shape.len() {
770 return Err(TorshError::ShapeMismatch {
771 expected: self_shape.clone(),
772 got: source_shape.clone(),
773 });
774 }
775 for (i, (&s, &src_s)) in self_shape.iter().zip(source_shape.iter()).enumerate() {
776 if i == dim {
777 if src_s != index_size {
778 return Err(TorshError::InvalidArgument(format!(
779 "source dimension {} size {} must match index size {}",
780 i, src_s, index_size
781 )));
782 }
783 } else if s != src_s {
784 return Err(TorshError::ShapeMismatch {
785 expected: self_shape.clone(),
786 got: source_shape.clone(),
787 });
788 }
789 }
790 let mut result_data = self.to_vec()?;
791 let source_data = source.to_vec()?;
792 let index_data = index.to_vec()?;
793 let dim_size = self_shape[dim];
794 let outer_size: usize = self_shape[..dim].iter().product();
795 let inner_size: usize = self_shape[dim + 1..].iter().product();
796 for (src_idx_in_dim, &target_idx) in index_data.iter().enumerate() {
797 if target_idx < 0 || target_idx as usize >= dim_size {
798 return Err(TorshError::InvalidArgument(format!(
799 "Index {} out of range for dimension size {}",
800 target_idx, dim_size
801 )));
802 }
803 let target_idx = target_idx as usize;
804 for outer in 0..outer_size {
805 for inner in 0..inner_size {
806 let result_idx =
807 outer * dim_size * inner_size + target_idx * inner_size + inner;
808 let source_idx =
809 outer * index_size * inner_size + src_idx_in_dim * inner_size + inner;
810 result_data[result_idx] = source_data[source_idx];
811 }
812 }
813 }
814 Self::from_data(result_data, self_shape, self.device)
815 }
816 pub fn index_fill(&self, dim: isize, index: &Tensor<i64>, value: T) -> Result<Self> {
833 let ndim = self.ndim();
834 let dim = if dim < 0 {
835 (ndim as isize + dim) as usize
836 } else {
837 dim as usize
838 };
839 if dim >= ndim {
840 return Err(TorshError::InvalidArgument(format!(
841 "Dimension {} out of range for {}-D tensor",
842 dim, ndim
843 )));
844 }
845 if index.ndim() != 1 {
846 return Err(TorshError::InvalidArgument(
847 "index must be 1D tensor".to_string(),
848 ));
849 }
850 let mut result_data = self.to_vec()?;
851 let index_data = index.to_vec()?;
852 let self_shape = self.shape().to_vec();
853 let dim_size = self_shape[dim];
854 let outer_size: usize = self_shape[..dim].iter().product();
855 let inner_size: usize = self_shape[dim + 1..].iter().product();
856 for &target_idx in index_data.iter() {
857 if target_idx < 0 || target_idx as usize >= dim_size {
858 return Err(TorshError::InvalidArgument(format!(
859 "Index {} out of range for dimension size {}",
860 target_idx, dim_size
861 )));
862 }
863 let target_idx = target_idx as usize;
864 for outer in 0..outer_size {
865 for inner in 0..inner_size {
866 let result_idx =
867 outer * dim_size * inner_size + target_idx * inner_size + inner;
868 result_data[result_idx] = value;
869 }
870 }
871 }
872 Self::from_data(result_data, self_shape, self.device)
873 }
874 pub fn put_(&self, indices: &Tensor<i64>, values: &Tensor<T>) -> Result<Self> {
891 if indices.ndim() != 1 {
892 return Err(TorshError::InvalidArgument(
893 "indices must be 1D tensor".to_string(),
894 ));
895 }
896 if values.ndim() != 1 {
897 return Err(TorshError::InvalidArgument(
898 "values must be 1D tensor".to_string(),
899 ));
900 }
901 let indices_data = indices.to_vec()?;
902 let values_data = values.to_vec()?;
903 if indices_data.len() != values_data.len() {
904 return Err(TorshError::InvalidArgument(format!(
905 "Number of values {} must match number of indices {}",
906 values_data.len(),
907 indices_data.len()
908 )));
909 }
910 let mut result_data = self.to_vec()?;
911 let numel = self.numel();
912 for (i, &index) in indices_data.iter().enumerate() {
913 let idx = if index < 0 {
914 ((numel as i64) + index) as usize
915 } else {
916 index as usize
917 };
918 if idx >= numel {
919 return Err(TorshError::InvalidArgument(format!(
920 "Index {} out of range for tensor with {} elements",
921 index, numel
922 )));
923 }
924 result_data[idx] = values_data[i];
925 }
926 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
927 }
928 pub fn masked_scatter(&self, mask: &Tensor<bool>, source: &Tensor<T>) -> Result<Self> {
953 if self.shape() != mask.shape() {
954 return Err(TorshError::ShapeMismatch {
955 expected: self.shape().dims().to_vec(),
956 got: mask.shape().dims().to_vec(),
957 });
958 }
959 let mask_data = mask.to_vec()?;
960 let true_count = mask_data.iter().filter(|&&x| x).count();
961 if source.numel() < true_count {
962 return Err(TorshError::InvalidArgument(format!(
963 "Source tensor has {} elements but need {} for scatter (mask has {} true values)",
964 source.numel(),
965 true_count,
966 true_count
967 )));
968 }
969 let self_data = self.to_vec()?;
970 let source_data = source.to_vec()?;
971 let mut result_data = Vec::with_capacity(self_data.len());
972 let mut source_idx = 0;
973 for (i, &self_val) in self_data.iter().enumerate() {
974 if i < mask_data.len() && mask_data[i] {
975 result_data.push(source_data[source_idx]);
976 source_idx += 1;
977 } else {
978 result_data.push(self_val);
979 }
980 }
981 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
982 }
983 pub fn index_put(&self, indices: &[Tensor<i64>], values: &Tensor<T>) -> Result<Self> {
1007 if indices.is_empty() {
1008 return Err(TorshError::InvalidArgument(
1009 "indices cannot be empty".to_string(),
1010 ));
1011 }
1012 if indices.len() > self.ndim() {
1013 return Err(TorshError::InvalidArgument(format!(
1014 "Too many indices ({}) for tensor with {} dimensions",
1015 indices.len(),
1016 self.ndim()
1017 )));
1018 }
1019 let index_shape_ref = indices[0].shape();
1020 let index_shape = index_shape_ref.dims();
1021 let num_indices = indices[0].numel();
1022 for idx_tensor in indices.iter() {
1023 if idx_tensor.shape().dims() != index_shape {
1024 return Err(TorshError::ShapeMismatch {
1025 expected: index_shape.to_vec(),
1026 got: idx_tensor.shape().dims().to_vec(),
1027 });
1028 }
1029 }
1030 if values.numel() != num_indices && values.numel() != 1 {
1031 return Err(TorshError::InvalidArgument(format!(
1032 "Values tensor has {} elements but need {} (or 1 for broadcasting)",
1033 values.numel(),
1034 num_indices
1035 )));
1036 }
1037 let mut result_data = self.to_vec()?;
1038 let self_shape_ref = self.shape();
1039 let self_shape = self_shape_ref.dims();
1040 let values_data = values.to_vec()?;
1041 let index_data: Result<Vec<Vec<i64>>> = indices.iter().map(|idx| idx.to_vec()).collect();
1042 let index_data = index_data?;
1043 let mut strides = vec![1; self_shape.len()];
1044 for i in (0..self_shape.len() - 1).rev() {
1045 strides[i] = strides[i + 1] * self_shape[i + 1];
1046 }
1047 for i in 0..num_indices {
1048 let value = if values_data.len() == 1 {
1049 values_data[0]
1050 } else {
1051 values_data[i]
1052 };
1053 let mut flat_idx = 0;
1054 for (dim, idx_vec) in index_data.iter().enumerate() {
1055 let mut idx = idx_vec[i];
1056 if idx < 0 {
1057 idx += self_shape[dim] as i64;
1058 }
1059 if idx < 0 || idx >= self_shape[dim] as i64 {
1060 return Err(TorshError::InvalidArgument(format!(
1061 "Index {} out of bounds for dimension {} with size {}",
1062 idx_vec[i], dim, self_shape[dim]
1063 )));
1064 }
1065 flat_idx += (idx as usize) * strides[dim];
1066 }
1067 result_data[flat_idx] = value;
1068 }
1069 Self::from_data(result_data, self_shape.to_vec(), self.device)
1070 }
1071 pub fn scatter_reduce(
1095 &self,
1096 dim: usize,
1097 indices: &Tensor<i64>,
1098 src: &Tensor<T>,
1099 reduce: &str,
1100 ) -> Result<Self>
1101 where
1102 T: std::ops::Add<Output = T>
1103 + std::ops::Mul<Output = T>
1104 + std::ops::Div<Output = T>
1105 + PartialOrd
1106 + num_traits::FromPrimitive,
1107 {
1108 if dim >= self.ndim() {
1109 return Err(TorshError::InvalidArgument(format!(
1110 "Dimension {} out of range for {}-dimensional tensor",
1111 dim,
1112 self.ndim()
1113 )));
1114 }
1115 if indices.shape() != src.shape() {
1116 return Err(TorshError::ShapeMismatch {
1117 expected: indices.shape().dims().to_vec(),
1118 got: src.shape().dims().to_vec(),
1119 });
1120 }
1121 let indices_data = indices.to_vec()?;
1122 let src_data = src.to_vec()?;
1123 let mut result_data = self.to_vec()?;
1124 let self_shape_ref = self.shape();
1125 let self_shape = self_shape_ref.dims();
1126 let mut counts = if reduce == "mean" {
1127 vec![0usize; result_data.len()]
1128 } else {
1129 vec![]
1130 };
1131 if self.ndim() == 1 {
1132 for (i, &index) in indices_data.iter().enumerate() {
1133 let idx = if index < 0 {
1134 (self_shape[0] as i64 + index) as usize
1135 } else {
1136 index as usize
1137 };
1138 if idx >= self_shape[0] {
1139 return Err(TorshError::InvalidArgument(format!(
1140 "Index {} out of bounds for dimension size {}",
1141 index, self_shape[0]
1142 )));
1143 }
1144 result_data[idx] = match reduce {
1145 "sum" => result_data[idx] + src_data[i],
1146 "prod" => result_data[idx] * src_data[i],
1147 "mean" => {
1148 counts[idx] += 1;
1149 result_data[idx] + src_data[i]
1150 }
1151 "amax" => {
1152 if src_data[i] > result_data[idx] {
1153 src_data[i]
1154 } else {
1155 result_data[idx]
1156 }
1157 }
1158 "amin" => {
1159 if src_data[i] < result_data[idx] {
1160 src_data[i]
1161 } else {
1162 result_data[idx]
1163 }
1164 }
1165 _ => {
1166 return Err(TorshError::InvalidArgument(format!(
1167 "Unknown reduce operation: {}. Supported: sum, prod, mean, amax, amin",
1168 reduce
1169 )));
1170 }
1171 };
1172 }
1173 if reduce == "mean" {
1174 for (i, count) in counts.iter().enumerate() {
1175 if *count > 0 {
1176 result_data[i] = T::from_usize(*count)
1177 .and_then(|c| Some(result_data[i] / c))
1178 .unwrap_or(result_data[i]);
1179 }
1180 }
1181 }
1182 } else {
1183 let dim_size = self_shape[dim];
1184 let _outer_size: usize = self_shape[..dim].iter().product();
1185 let _inner_size: usize = self_shape[dim + 1..].iter().product();
1186 let mut self_strides = vec![1; self_shape.len()];
1187 for i in (0..self_shape.len() - 1).rev() {
1188 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1189 }
1190 let src_shape_ref = src.shape();
1191 let src_shape = src_shape_ref.dims();
1192 let mut src_strides = vec![1; src_shape.len()];
1193 for i in (0..src_shape.len() - 1).rev() {
1194 src_strides[i] = src_strides[i + 1] * src_shape[i + 1];
1195 }
1196 for i in 0..indices_data.len() {
1197 let index = indices_data[i];
1198 let idx = if index < 0 {
1199 (dim_size as i64 + index) as usize
1200 } else {
1201 index as usize
1202 };
1203 if idx >= dim_size {
1204 return Err(TorshError::InvalidArgument(format!(
1205 "Index {} out of bounds for dimension {} size {}",
1206 index, dim, dim_size
1207 )));
1208 }
1209 let mut coords = vec![0; self_shape.len()];
1210 let mut remainder = i;
1211 for (d, &stride) in src_strides.iter().enumerate() {
1212 coords[d] = remainder / stride;
1213 remainder %= stride;
1214 }
1215 coords[dim] = idx;
1216 let flat_idx = coords
1217 .iter()
1218 .zip(self_strides.iter())
1219 .map(|(c, s)| c * s)
1220 .sum::<usize>();
1221 result_data[flat_idx] = match reduce {
1222 "sum" => result_data[flat_idx] + src_data[i],
1223 "prod" => result_data[flat_idx] * src_data[i],
1224 "mean" => {
1225 counts[flat_idx] += 1;
1226 result_data[flat_idx] + src_data[i]
1227 }
1228 "amax" => {
1229 if src_data[i] > result_data[flat_idx] {
1230 src_data[i]
1231 } else {
1232 result_data[flat_idx]
1233 }
1234 }
1235 "amin" => {
1236 if src_data[i] < result_data[flat_idx] {
1237 src_data[i]
1238 } else {
1239 result_data[flat_idx]
1240 }
1241 }
1242 _ => {
1243 return Err(TorshError::InvalidArgument(format!(
1244 "Unknown reduce operation: {}",
1245 reduce
1246 )));
1247 }
1248 };
1249 }
1250 if reduce == "mean" {
1251 for (i, count) in counts.iter().enumerate() {
1252 if *count > 0 {
1253 result_data[i] = T::from_usize(*count)
1254 .and_then(|c| Some(result_data[i] / c))
1255 .unwrap_or(result_data[i]);
1256 }
1257 }
1258 }
1259 }
1260 Self::from_data(result_data, self_shape.to_vec(), self.device)
1261 }
1262 pub fn diagonal_scatter(
1284 &self,
1285 src: &Tensor<T>,
1286 offset: isize,
1287 dim1: usize,
1288 dim2: usize,
1289 ) -> Result<Self> {
1290 if dim1 >= self.ndim() || dim2 >= self.ndim() {
1291 return Err(TorshError::InvalidArgument(format!(
1292 "Dimensions ({}, {}) out of range for {}-dimensional tensor",
1293 dim1,
1294 dim2,
1295 self.ndim()
1296 )));
1297 }
1298 if dim1 == dim2 {
1299 return Err(TorshError::InvalidArgument(
1300 "dim1 and dim2 must be different".to_string(),
1301 ));
1302 }
1303 let self_shape_ref = self.shape();
1304 let self_shape = self_shape_ref.dims();
1305 let dim1_size = self_shape[dim1];
1306 let dim2_size = self_shape[dim2];
1307 let diag_len = if offset >= 0 {
1308 let offset_u = offset as usize;
1309 if offset_u >= dim2_size {
1310 0
1311 } else {
1312 std::cmp::min(dim1_size, dim2_size - offset_u)
1313 }
1314 } else {
1315 let offset_u = (-offset) as usize;
1316 if offset_u >= dim1_size {
1317 0
1318 } else {
1319 std::cmp::min(dim1_size - offset_u, dim2_size)
1320 }
1321 };
1322 if src.numel() != diag_len {
1323 return Err(TorshError::ShapeMismatch {
1324 expected: vec![diag_len],
1325 got: vec![src.numel()],
1326 });
1327 }
1328 let mut result_data = self.to_vec()?;
1329 let src_data = src.to_vec()?;
1330 let mut strides = vec![1; self_shape.len()];
1331 for i in (0..self_shape.len() - 1).rev() {
1332 strides[i] = strides[i + 1] * self_shape[i + 1];
1333 }
1334 for i in 0..diag_len {
1335 let mut indices = vec![0; self_shape.len()];
1336 if offset >= 0 {
1337 indices[dim1] = i;
1338 indices[dim2] = i + offset as usize;
1339 } else {
1340 indices[dim1] = i + (-offset) as usize;
1341 indices[dim2] = i;
1342 }
1343 let mut flat_idx = 0;
1344 for (d, &idx) in indices.iter().enumerate() {
1345 flat_idx += idx * strides[d];
1346 }
1347 result_data[flat_idx] = src_data[i];
1348 }
1349 Self::from_data(result_data, self_shape.to_vec(), self.device)
1350 }
1351 pub fn select_scatter(&self, src: &Tensor<T>, dim: isize, index: isize) -> Result<Self> {
1372 let ndim = self.ndim() as isize;
1373 let dim_normalized = if dim < 0 { ndim + dim } else { dim };
1374 if dim_normalized < 0 || dim_normalized >= ndim {
1375 return Err(TorshError::InvalidArgument(format!(
1376 "Dimension {} out of range for {}-dimensional tensor",
1377 dim,
1378 self.ndim()
1379 )));
1380 }
1381 let dim_u = dim_normalized as usize;
1382 let self_shape_ref = self.shape();
1383 let self_shape = self_shape_ref.dims();
1384 let index_normalized = if index < 0 {
1385 (self_shape[dim_u] as isize) + index
1386 } else {
1387 index
1388 };
1389 if index_normalized < 0 || index_normalized >= self_shape[dim_u] as isize {
1390 return Err(TorshError::InvalidArgument(format!(
1391 "Index {} out of bounds for dimension {} with size {}",
1392 index, dim_u, self_shape[dim_u]
1393 )));
1394 }
1395 let index_u = index_normalized as usize;
1396 let expected_src_shape: Vec<usize> = self_shape
1397 .iter()
1398 .enumerate()
1399 .filter(|(i, _)| *i != dim_u)
1400 .map(|(_, &s)| s)
1401 .collect();
1402 let src_shape_ref = src.shape();
1403 let src_shape = src_shape_ref.dims();
1404 if src_shape != expected_src_shape.as_slice() {
1405 return Err(TorshError::ShapeMismatch {
1406 expected: expected_src_shape,
1407 got: src_shape.to_vec(),
1408 });
1409 }
1410 let mut result_data = self.to_vec()?;
1411 let src_data = src.to_vec()?;
1412 let mut self_strides = vec![1; self_shape.len()];
1413 for i in (0..self_shape.len() - 1).rev() {
1414 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1415 }
1416 let outer_size: usize = self_shape[..dim_u].iter().product();
1417 let inner_size: usize = self_shape[dim_u + 1..].iter().product();
1418 for outer in 0..outer_size {
1419 for inner in 0..inner_size {
1420 let self_idx =
1421 outer * (self_shape[dim_u] * inner_size) + index_u * inner_size + inner;
1422 let src_idx = outer * inner_size + inner;
1423 result_data[self_idx] = src_data[src_idx];
1424 }
1425 }
1426 Self::from_data(result_data, self_shape.to_vec(), self.device)
1427 }
1428 pub fn slice_scatter(
1451 &self,
1452 src: &Tensor<T>,
1453 dim: isize,
1454 start: Option<isize>,
1455 end: Option<isize>,
1456 step: usize,
1457 ) -> Result<Self> {
1458 if step == 0 {
1459 return Err(TorshError::InvalidArgument(
1460 "Step must be greater than 0".to_string(),
1461 ));
1462 }
1463 let ndim = self.ndim() as isize;
1464 let dim_normalized = if dim < 0 { ndim + dim } else { dim };
1465 if dim_normalized < 0 || dim_normalized >= ndim {
1466 return Err(TorshError::InvalidArgument(format!(
1467 "Dimension {} out of range for {}-dimensional tensor",
1468 dim,
1469 self.ndim()
1470 )));
1471 }
1472 let dim_u = dim_normalized as usize;
1473 let self_shape_ref = self.shape();
1474 let self_shape = self_shape_ref.dims();
1475 let dim_size = self_shape[dim_u] as isize;
1476 let start_normalized = start.unwrap_or(0);
1477 let start_normalized = if start_normalized < 0 {
1478 dim_size + start_normalized
1479 } else {
1480 start_normalized
1481 };
1482 let start_normalized = std::cmp::max(0, std::cmp::min(start_normalized, dim_size)) as usize;
1483 let end_normalized = end.unwrap_or(dim_size);
1484 let end_normalized = if end_normalized < 0 {
1485 dim_size + end_normalized
1486 } else {
1487 end_normalized
1488 };
1489 let end_normalized = std::cmp::max(0, std::cmp::min(end_normalized, dim_size)) as usize;
1490 let slice_len = if end_normalized > start_normalized {
1491 (end_normalized - start_normalized + step - 1) / step
1492 } else {
1493 0
1494 };
1495 let mut expected_src_shape = self_shape.to_vec();
1496 expected_src_shape[dim_u] = slice_len;
1497 let src_shape_ref = src.shape();
1498 let src_shape = src_shape_ref.dims();
1499 if src_shape != expected_src_shape.as_slice() {
1500 return Err(TorshError::ShapeMismatch {
1501 expected: expected_src_shape,
1502 got: src_shape.to_vec(),
1503 });
1504 }
1505 let mut result_data = self.to_vec()?;
1506 let src_data = src.to_vec()?;
1507 let mut self_strides = vec![1; self_shape.len()];
1508 for i in (0..self_shape.len() - 1).rev() {
1509 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1510 }
1511 let outer_size: usize = self_shape[..dim_u].iter().product();
1512 let inner_size: usize = self_shape[dim_u + 1..].iter().product();
1513 for outer in 0..outer_size {
1514 for slice_idx in 0..slice_len {
1515 let self_dim_idx = start_normalized + slice_idx * step;
1516 for inner in 0..inner_size {
1517 let self_idx = outer * (self_shape[dim_u] * inner_size)
1518 + self_dim_idx * inner_size
1519 + inner;
1520 let src_idx = outer * (slice_len * inner_size) + slice_idx * inner_size + inner;
1521 result_data[self_idx] = src_data[src_idx];
1522 }
1523 }
1524 }
1525 Self::from_data(result_data, self_shape.to_vec(), self.device)
1526 }
1527}