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<()>
24 where
25 T: Copy,
26 {
27 for i in 0..self.numel() {
28 self.storage.set(i, value)?;
29 }
30 Ok(())
31 }
32 pub fn zero_(&mut self) -> Result<()>
34 where
35 T: Copy,
36 {
37 self.fill_(T::zero())
38 }
39 pub fn ones_(&mut self) -> Result<()>
41 where
42 T: Copy,
43 {
44 self.fill_(T::one())
45 }
46 pub fn copy_(&mut self, other: &Self) -> Result<()>
48 where
49 T: Copy,
50 {
51 if self.shape() != other.shape() {
52 return Err(TorshError::ShapeMismatch {
53 expected: self.shape().dims().to_vec(),
54 got: other.shape().dims().to_vec(),
55 });
56 }
57 let other_data = other.to_vec()?;
58 for (i, &value) in other_data.iter().enumerate() {
59 self.storage.set(i, value)?;
60 }
61 Ok(())
62 }
63 pub fn get_item(&self, indices: &[usize]) -> Result<T>
65 where
66 T: Copy,
67 {
68 if indices.len() != self.ndim() {
69 return Err(TorshError::InvalidArgument(format!(
70 "Expected {} indices, got {}",
71 self.ndim(),
72 indices.len()
73 )));
74 }
75 let binding = self.shape();
76 let shape = binding.dims();
77 for (i, &idx) in indices.iter().enumerate() {
78 if idx >= shape[i] {
79 return Err(TorshError::IndexOutOfBounds {
80 index: idx,
81 size: shape[i],
82 });
83 }
84 }
85 let flat_index = self.multi_to_flat_index(indices)?;
86 self.get_item_flat(flat_index)
87 }
88 pub fn set_item(&mut self, indices: &[usize], value: T) -> Result<()>
90 where
91 T: Copy,
92 {
93 if indices.len() != self.ndim() {
94 return Err(TorshError::InvalidArgument(format!(
95 "Expected {} indices, got {}",
96 self.ndim(),
97 indices.len()
98 )));
99 }
100 let binding = self.shape();
101 let shape = binding.dims();
102 for (i, &idx) in indices.iter().enumerate() {
103 if idx >= shape[i] {
104 return Err(TorshError::IndexOutOfBounds {
105 index: idx,
106 size: shape[i],
107 });
108 }
109 }
110 let flat_index = self.multi_to_flat_index(indices)?;
111 self.set_item_flat(flat_index, value)
112 }
113 pub fn get_item_flat(&self, index: usize) -> Result<T>
115 where
116 T: Copy,
117 {
118 if index >= self.numel() {
119 return Err(TorshError::IndexOutOfBounds {
120 index,
121 size: self.numel(),
122 });
123 }
124 let shape = self.shape();
126 let dims = shape.dims();
127 let ndim = dims.len();
128 let mut multi_dim = vec![0usize; ndim];
129 let mut remaining = index;
130 for i in (0..ndim).rev() {
131 if dims[i] > 0 {
132 multi_dim[i] = remaining % dims[i];
133 remaining /= dims[i];
134 }
135 }
136 let strides = self.strides();
137 let storage_idx = self.storage_offset
138 + multi_dim
139 .iter()
140 .zip(strides.iter())
141 .map(|(&m, &s)| m * s)
142 .sum::<usize>();
143 self.storage.get(storage_idx)
144 }
145 pub fn set_item_flat(&mut self, index: usize, value: T) -> Result<()>
147 where
148 T: Copy,
149 {
150 if index >= self.numel() {
151 return Err(TorshError::IndexOutOfBounds {
152 index,
153 size: self.numel(),
154 });
155 }
156 let shape = self.shape();
158 let dims = shape.dims();
159 let ndim = dims.len();
160 let mut multi_dim = vec![0usize; ndim];
161 let mut remaining = index;
162 for i in (0..ndim).rev() {
163 if dims[i] > 0 {
164 multi_dim[i] = remaining % dims[i];
165 remaining /= dims[i];
166 }
167 }
168 let strides = self.strides();
169 let storage_idx = self.storage_offset
170 + multi_dim
171 .iter()
172 .zip(strides.iter())
173 .map(|(&m, &s)| m * s)
174 .sum::<usize>();
175 self.storage.set(storage_idx, value)
176 }
177 pub fn multi_to_flat_index(&self, indices: &[usize]) -> Result<usize> {
179 let binding = self.shape();
180 let shape = binding.dims();
181 if indices.len() != shape.len() {
182 return Err(TorshError::InvalidArgument(format!(
183 "Expected {} indices, got {}",
184 shape.len(),
185 indices.len()
186 )));
187 }
188 let mut flat_index = 0;
189 let mut stride = 1;
190 for i in (0..indices.len()).rev() {
191 flat_index += indices[i] * stride;
192 stride *= shape[i];
193 }
194 Ok(flat_index)
195 }
196 pub fn gather(&self, dim: usize, indices: &Tensor<i64>) -> Result<Self> {
198 if dim >= self.ndim() {
199 return Err(TorshError::InvalidArgument(format!(
200 "Dimension {} out of range for tensor with {} dimensions",
201 dim,
202 self.ndim()
203 )));
204 }
205 let self_data = self.to_vec()?;
206 let indices_data = indices.to_vec()?;
207 let mut result_data = Vec::new();
208 let result_shape = indices.shape().dims().to_vec();
209 if self.ndim() == 1 {
210 for &index in &indices_data {
211 let idx = if index < 0 {
212 (self.shape().dims()[0] as i64 + index) as usize
213 } else {
214 index as usize
215 };
216 if idx >= self.shape().dims()[0] {
217 return Err(TorshError::InvalidArgument(format!(
218 "Index {} out of range for tensor with size {}",
219 index,
220 self.shape().dims()[0]
221 )));
222 }
223 result_data.push(self_data[idx]);
224 }
225 } else {
226 let self_shape_ref = self.shape();
227 let self_shape = self_shape_ref.dims();
228 let indices_shape_ref = indices.shape();
229 let indices_shape = indices_shape_ref.dims();
230 let dim_size = self_shape[dim];
231 let mut self_strides = vec![1; self_shape.len()];
232 let mut indices_strides = vec![1; indices_shape.len()];
233 for i in (0..self_shape.len() - 1).rev() {
234 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
235 }
236 for i in (0..indices_shape.len() - 1).rev() {
237 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
238 }
239 let total_elements = indices_data.len();
240 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
241 let mut indices_coords = vec![0; indices_shape.len()];
242 let mut temp_i = i;
243 for j in 0..indices_shape.len() {
244 indices_coords[j] = temp_i / indices_strides[j];
245 temp_i %= indices_strides[j];
246 }
247 let idx = if index_value < 0 {
248 (dim_size as i64 + index_value) as usize
249 } else {
250 index_value as usize
251 };
252 if idx >= dim_size {
253 return Err(TorshError::InvalidArgument(format!(
254 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
255 )));
256 }
257 let mut self_coords = indices_coords.clone();
258 if dim < self_coords.len() {
259 self_coords[dim] = idx;
260 }
261 let mut flat_idx = 0;
262 for j in 0..self_coords.len() {
263 flat_idx += self_coords[j] * self_strides[j];
264 }
265 result_data.push(self_data[flat_idx]);
266 }
267 }
268 Self::from_data(result_data, result_shape, self.device)
269 }
270 pub fn scatter(&self, dim: usize, indices: &Tensor<i64>, src: &Tensor<T>) -> Result<Self> {
272 if dim >= self.ndim() {
273 return Err(TorshError::InvalidArgument(format!(
274 "Dimension {} out of range for tensor with {} dimensions",
275 dim,
276 self.ndim()
277 )));
278 }
279 let mut result_data = self.to_vec()?;
280 let indices_data = indices.to_vec()?;
281 let src_data = src.to_vec()?;
282 if indices_data.len() != src_data.len() {
283 return Err(TorshError::InvalidArgument(
284 "Indices and source tensor must have the same number of elements".to_string(),
285 ));
286 }
287 if self.ndim() == 1 {
288 for (i, &index) in indices_data.iter().enumerate() {
289 let idx = if index < 0 {
290 (self.shape().dims()[0] as i64 + index) as usize
291 } else {
292 index as usize
293 };
294 if idx >= self.shape().dims()[0] {
295 return Err(TorshError::InvalidArgument(format!(
296 "Index {} out of range for tensor with size {}",
297 index,
298 self.shape().dims()[0]
299 )));
300 }
301 result_data[idx] = src_data[i];
302 }
303 } else {
304 let self_shape_ref = self.shape();
305 let self_shape = self_shape_ref.dims();
306 let indices_shape_ref = indices.shape();
307 let indices_shape = indices_shape_ref.dims();
308 let dim_size = self_shape[dim];
309 let mut self_strides = vec![1; self_shape.len()];
310 let mut indices_strides = vec![1; indices_shape.len()];
311 for i in (0..self_shape.len() - 1).rev() {
312 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
313 }
314 for i in (0..indices_shape.len() - 1).rev() {
315 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
316 }
317 let total_elements = indices_data.len();
318 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
319 let mut indices_coords = vec![0; indices_shape.len()];
320 let mut temp_i = i;
321 for j in 0..indices_shape.len() {
322 indices_coords[j] = temp_i / indices_strides[j];
323 temp_i %= indices_strides[j];
324 }
325 let idx = if index_value < 0 {
326 (dim_size as i64 + index_value) as usize
327 } else {
328 index_value as usize
329 };
330 if idx >= dim_size {
331 return Err(TorshError::InvalidArgument(format!(
332 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
333 )));
334 }
335 let mut self_coords = indices_coords.clone();
336 if dim < self_coords.len() {
337 self_coords[dim] = idx;
338 }
339 let mut flat_idx = 0;
340 for j in 0..self_coords.len() {
341 flat_idx += self_coords[j] * self_strides[j];
342 }
343 result_data[flat_idx] = src_data[i];
344 }
345 }
346 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
347 }
348 pub fn scatter_add(&self, dim: usize, indices: &Tensor<i64>, src: &Tensor<T>) -> Result<Self>
369 where
370 T: std::ops::Add<Output = T>,
371 {
372 if dim >= self.ndim() {
373 return Err(TorshError::InvalidArgument(format!(
374 "Dimension {} out of range for tensor with {} dimensions",
375 dim,
376 self.ndim()
377 )));
378 }
379 let mut result_data = self.to_vec()?;
380 let indices_data = indices.to_vec()?;
381 let src_data = src.to_vec()?;
382 if indices_data.len() != src_data.len() {
383 return Err(TorshError::InvalidArgument(
384 "Indices and source tensor must have the same number of elements".to_string(),
385 ));
386 }
387 if self.ndim() == 1 {
388 for (i, &index) in indices_data.iter().enumerate() {
389 let idx = if index < 0 {
390 (self.shape().dims()[0] as i64 + index) as usize
391 } else {
392 index as usize
393 };
394 if idx >= self.shape().dims()[0] {
395 return Err(TorshError::InvalidArgument(format!(
396 "Index {} out of range for tensor with size {}",
397 index,
398 self.shape().dims()[0]
399 )));
400 }
401 result_data[idx] = result_data[idx] + src_data[i];
402 }
403 } else {
404 let self_shape_ref = self.shape();
405 let self_shape = self_shape_ref.dims();
406 let indices_shape_ref = indices.shape();
407 let indices_shape = indices_shape_ref.dims();
408 let dim_size = self_shape[dim];
409 let mut self_strides = vec![1; self_shape.len()];
410 let mut indices_strides = vec![1; indices_shape.len()];
411 for i in (0..self_shape.len() - 1).rev() {
412 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
413 }
414 for i in (0..indices_shape.len() - 1).rev() {
415 indices_strides[i] = indices_strides[i + 1] * indices_shape[i + 1];
416 }
417 let total_elements = indices_data.len();
418 for (i, &index_value) in indices_data.iter().enumerate().take(total_elements) {
419 let mut indices_coords = vec![0; indices_shape.len()];
420 let mut temp_i = i;
421 for j in 0..indices_shape.len() {
422 indices_coords[j] = temp_i / indices_strides[j];
423 temp_i %= indices_strides[j];
424 }
425 let idx = if index_value < 0 {
426 (dim_size as i64 + index_value) as usize
427 } else {
428 index_value as usize
429 };
430 if idx >= dim_size {
431 return Err(TorshError::InvalidArgument(format!(
432 "Index {index_value} out of range for dimension {dim} with size {dim_size}"
433 )));
434 }
435 let mut self_coords = indices_coords.clone();
436 if dim < self_coords.len() {
437 self_coords[dim] = idx;
438 }
439 let mut flat_idx = 0;
440 for j in 0..self_coords.len() {
441 flat_idx += self_coords[j] * self_strides[j];
442 }
443 result_data[flat_idx] = result_data[flat_idx] + src_data[i];
444 }
445 }
446 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
447 }
448 pub fn repeat(&self, repeats: &[usize]) -> Result<Self> {
450 if repeats.len() != self.ndim() {
451 return Err(TorshError::InvalidArgument(format!(
452 "Number of repeats {} must match tensor dimensions {}",
453 repeats.len(),
454 self.ndim()
455 )));
456 }
457 let self_data = self.to_vec()?;
458 let shape_binding = self.shape();
459 let self_shape = shape_binding.dims();
460 let new_shape: Vec<usize> = self_shape
461 .iter()
462 .zip(repeats.iter())
463 .map(|(&dim, &repeat)| dim * repeat)
464 .collect();
465 let new_numel = new_shape.iter().product();
466 let mut result_data = Vec::with_capacity(new_numel);
467 for result_idx in 0..new_numel {
468 let mut result_coords = vec![0; new_shape.len()];
469 let mut temp_idx = result_idx;
470 for i in (0..new_shape.len()).rev() {
471 result_coords[i] = temp_idx % new_shape[i];
472 temp_idx /= new_shape[i];
473 }
474 let source_coords: Vec<usize> = result_coords
475 .iter()
476 .zip(self_shape.iter())
477 .map(|(&result_coord, &dim_size)| result_coord % dim_size)
478 .collect();
479 let mut source_idx = 0;
480 let mut stride = 1;
481 for i in (0..self_shape.len()).rev() {
482 source_idx += source_coords[i] * stride;
483 stride *= self_shape[i];
484 }
485 result_data.push(self_data[source_idx]);
486 }
487 Self::from_data(result_data, new_shape, self.device)
488 }
489 pub fn index_add(&self, dim: isize, index: &Tensor<i64>, source: &Self) -> Result<Self>
507 where
508 T: std::ops::Add<Output = T>,
509 {
510 let ndim = self.ndim();
511 let dim = if dim < 0 {
512 (ndim as isize + dim) as usize
513 } else {
514 dim as usize
515 };
516 if dim >= ndim {
517 return Err(TorshError::InvalidArgument(format!(
518 "Dimension {} out of range for {}-D tensor",
519 dim, ndim
520 )));
521 }
522 if index.ndim() != 1 {
523 return Err(TorshError::InvalidArgument(
524 "index must be 1D tensor".to_string(),
525 ));
526 }
527 let index_size = index.shape().dims()[0];
528 let self_shape = self.shape().to_vec();
529 let source_shape = source.shape().to_vec();
530 if source_shape.len() != self_shape.len() {
531 return Err(TorshError::ShapeMismatch {
532 expected: self_shape.clone(),
533 got: source_shape.clone(),
534 });
535 }
536 for (i, (&s, &src_s)) in self_shape.iter().zip(source_shape.iter()).enumerate() {
537 if i == dim {
538 if src_s != index_size {
539 return Err(TorshError::InvalidArgument(format!(
540 "source dimension {} size {} must match index size {}",
541 i, src_s, index_size
542 )));
543 }
544 } else if s != src_s {
545 return Err(TorshError::ShapeMismatch {
546 expected: self_shape.clone(),
547 got: source_shape.clone(),
548 });
549 }
550 }
551 let mut result_data = self.to_vec()?;
552 let source_data = source.to_vec()?;
553 let index_data = index.to_vec()?;
554 let dim_size = self_shape[dim];
555 let outer_size: usize = self_shape[..dim].iter().product();
556 let inner_size: usize = self_shape[dim + 1..].iter().product();
557 for (src_idx_in_dim, &target_idx) in index_data.iter().enumerate() {
558 if target_idx < 0 || target_idx as usize >= dim_size {
559 return Err(TorshError::InvalidArgument(format!(
560 "Index {} out of range for dimension size {}",
561 target_idx, dim_size
562 )));
563 }
564 let target_idx = target_idx as usize;
565 for outer in 0..outer_size {
566 for inner in 0..inner_size {
567 let result_idx =
568 outer * dim_size * inner_size + target_idx * inner_size + inner;
569 let source_idx =
570 outer * index_size * inner_size + src_idx_in_dim * inner_size + inner;
571 result_data[result_idx] = result_data[result_idx] + source_data[source_idx];
572 }
573 }
574 }
575 Self::from_data(result_data, self_shape, self.device)
576 }
577 pub fn index_copy(&self, dim: isize, index: &Tensor<i64>, source: &Self) -> Result<Self> {
595 let ndim = self.ndim();
596 let dim = if dim < 0 {
597 (ndim as isize + dim) as usize
598 } else {
599 dim as usize
600 };
601 if dim >= ndim {
602 return Err(TorshError::InvalidArgument(format!(
603 "Dimension {} out of range for {}-D tensor",
604 dim, ndim
605 )));
606 }
607 if index.ndim() != 1 {
608 return Err(TorshError::InvalidArgument(
609 "index must be 1D tensor".to_string(),
610 ));
611 }
612 let index_size = index.shape().dims()[0];
613 let self_shape = self.shape().to_vec();
614 let source_shape = source.shape().to_vec();
615 if source_shape.len() != self_shape.len() {
616 return Err(TorshError::ShapeMismatch {
617 expected: self_shape.clone(),
618 got: source_shape.clone(),
619 });
620 }
621 for (i, (&s, &src_s)) in self_shape.iter().zip(source_shape.iter()).enumerate() {
622 if i == dim {
623 if src_s != index_size {
624 return Err(TorshError::InvalidArgument(format!(
625 "source dimension {} size {} must match index size {}",
626 i, src_s, index_size
627 )));
628 }
629 } else if s != src_s {
630 return Err(TorshError::ShapeMismatch {
631 expected: self_shape.clone(),
632 got: source_shape.clone(),
633 });
634 }
635 }
636 let mut result_data = self.to_vec()?;
637 let source_data = source.to_vec()?;
638 let index_data = index.to_vec()?;
639 let dim_size = self_shape[dim];
640 let outer_size: usize = self_shape[..dim].iter().product();
641 let inner_size: usize = self_shape[dim + 1..].iter().product();
642 for (src_idx_in_dim, &target_idx) in index_data.iter().enumerate() {
643 if target_idx < 0 || target_idx as usize >= dim_size {
644 return Err(TorshError::InvalidArgument(format!(
645 "Index {} out of range for dimension size {}",
646 target_idx, dim_size
647 )));
648 }
649 let target_idx = target_idx as usize;
650 for outer in 0..outer_size {
651 for inner in 0..inner_size {
652 let result_idx =
653 outer * dim_size * inner_size + target_idx * inner_size + inner;
654 let source_idx =
655 outer * index_size * inner_size + src_idx_in_dim * inner_size + inner;
656 result_data[result_idx] = source_data[source_idx];
657 }
658 }
659 }
660 Self::from_data(result_data, self_shape, self.device)
661 }
662 pub fn index_fill(&self, dim: isize, index: &Tensor<i64>, value: T) -> Result<Self> {
679 let ndim = self.ndim();
680 let dim = if dim < 0 {
681 (ndim as isize + dim) as usize
682 } else {
683 dim as usize
684 };
685 if dim >= ndim {
686 return Err(TorshError::InvalidArgument(format!(
687 "Dimension {} out of range for {}-D tensor",
688 dim, ndim
689 )));
690 }
691 if index.ndim() != 1 {
692 return Err(TorshError::InvalidArgument(
693 "index must be 1D tensor".to_string(),
694 ));
695 }
696 let mut result_data = self.to_vec()?;
697 let index_data = index.to_vec()?;
698 let self_shape = self.shape().to_vec();
699 let dim_size = self_shape[dim];
700 let outer_size: usize = self_shape[..dim].iter().product();
701 let inner_size: usize = self_shape[dim + 1..].iter().product();
702 for &target_idx in index_data.iter() {
703 if target_idx < 0 || target_idx as usize >= dim_size {
704 return Err(TorshError::InvalidArgument(format!(
705 "Index {} out of range for dimension size {}",
706 target_idx, dim_size
707 )));
708 }
709 let target_idx = target_idx as usize;
710 for outer in 0..outer_size {
711 for inner in 0..inner_size {
712 let result_idx =
713 outer * dim_size * inner_size + target_idx * inner_size + inner;
714 result_data[result_idx] = value;
715 }
716 }
717 }
718 Self::from_data(result_data, self_shape, self.device)
719 }
720 pub fn put_(&self, indices: &Tensor<i64>, values: &Tensor<T>) -> Result<Self> {
737 if indices.ndim() != 1 {
738 return Err(TorshError::InvalidArgument(
739 "indices must be 1D tensor".to_string(),
740 ));
741 }
742 if values.ndim() != 1 {
743 return Err(TorshError::InvalidArgument(
744 "values must be 1D tensor".to_string(),
745 ));
746 }
747 let indices_data = indices.to_vec()?;
748 let values_data = values.to_vec()?;
749 if indices_data.len() != values_data.len() {
750 return Err(TorshError::InvalidArgument(format!(
751 "Number of values {} must match number of indices {}",
752 values_data.len(),
753 indices_data.len()
754 )));
755 }
756 let mut result_data = self.to_vec()?;
757 let numel = self.numel();
758 for (i, &index) in indices_data.iter().enumerate() {
759 let idx = if index < 0 {
760 ((numel as i64) + index) as usize
761 } else {
762 index as usize
763 };
764 if idx >= numel {
765 return Err(TorshError::InvalidArgument(format!(
766 "Index {} out of range for tensor with {} elements",
767 index, numel
768 )));
769 }
770 result_data[idx] = values_data[i];
771 }
772 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
773 }
774 pub fn masked_scatter(&self, mask: &Tensor<bool>, source: &Tensor<T>) -> Result<Self> {
799 if self.shape() != mask.shape() {
800 return Err(TorshError::ShapeMismatch {
801 expected: self.shape().dims().to_vec(),
802 got: mask.shape().dims().to_vec(),
803 });
804 }
805 let mask_data = mask.to_vec()?;
806 let true_count = mask_data.iter().filter(|&&x| x).count();
807 if source.numel() < true_count {
808 return Err(TorshError::InvalidArgument(format!(
809 "Source tensor has {} elements but need {} for scatter (mask has {} true values)",
810 source.numel(),
811 true_count,
812 true_count
813 )));
814 }
815 let self_data = self.to_vec()?;
816 let source_data = source.to_vec()?;
817 let mut result_data = Vec::with_capacity(self_data.len());
818 let mut source_idx = 0;
819 for (i, &self_val) in self_data.iter().enumerate() {
820 if i < mask_data.len() && mask_data[i] {
821 result_data.push(source_data[source_idx]);
822 source_idx += 1;
823 } else {
824 result_data.push(self_val);
825 }
826 }
827 Self::from_data(result_data, self.shape().dims().to_vec(), self.device)
828 }
829 pub fn index_put(&self, indices: &[Tensor<i64>], values: &Tensor<T>) -> Result<Self> {
853 if indices.is_empty() {
854 return Err(TorshError::InvalidArgument(
855 "indices cannot be empty".to_string(),
856 ));
857 }
858 if indices.len() > self.ndim() {
859 return Err(TorshError::InvalidArgument(format!(
860 "Too many indices ({}) for tensor with {} dimensions",
861 indices.len(),
862 self.ndim()
863 )));
864 }
865 let index_shape_ref = indices[0].shape();
866 let index_shape = index_shape_ref.dims();
867 let num_indices = indices[0].numel();
868 for idx_tensor in indices.iter() {
869 if idx_tensor.shape().dims() != index_shape {
870 return Err(TorshError::ShapeMismatch {
871 expected: index_shape.to_vec(),
872 got: idx_tensor.shape().dims().to_vec(),
873 });
874 }
875 }
876 if values.numel() != num_indices && values.numel() != 1 {
877 return Err(TorshError::InvalidArgument(format!(
878 "Values tensor has {} elements but need {} (or 1 for broadcasting)",
879 values.numel(),
880 num_indices
881 )));
882 }
883 let mut result_data = self.to_vec()?;
884 let self_shape_ref = self.shape();
885 let self_shape = self_shape_ref.dims();
886 let values_data = values.to_vec()?;
887 let index_data: Result<Vec<Vec<i64>>> = indices.iter().map(|idx| idx.to_vec()).collect();
888 let index_data = index_data?;
889 let mut strides = vec![1; self_shape.len()];
890 for i in (0..self_shape.len() - 1).rev() {
891 strides[i] = strides[i + 1] * self_shape[i + 1];
892 }
893 for i in 0..num_indices {
894 let value = if values_data.len() == 1 {
895 values_data[0]
896 } else {
897 values_data[i]
898 };
899 let mut flat_idx = 0;
900 for (dim, idx_vec) in index_data.iter().enumerate() {
901 let mut idx = idx_vec[i];
902 if idx < 0 {
903 idx += self_shape[dim] as i64;
904 }
905 if idx < 0 || idx >= self_shape[dim] as i64 {
906 return Err(TorshError::InvalidArgument(format!(
907 "Index {} out of bounds for dimension {} with size {}",
908 idx_vec[i], dim, self_shape[dim]
909 )));
910 }
911 flat_idx += (idx as usize) * strides[dim];
912 }
913 result_data[flat_idx] = value;
914 }
915 Self::from_data(result_data, self_shape.to_vec(), self.device)
916 }
917 pub fn scatter_reduce(
941 &self,
942 dim: usize,
943 indices: &Tensor<i64>,
944 src: &Tensor<T>,
945 reduce: &str,
946 ) -> Result<Self>
947 where
948 T: std::ops::Add<Output = T>
949 + std::ops::Mul<Output = T>
950 + std::ops::Div<Output = T>
951 + PartialOrd
952 + num_traits::FromPrimitive,
953 {
954 if dim >= self.ndim() {
955 return Err(TorshError::InvalidArgument(format!(
956 "Dimension {} out of range for {}-dimensional tensor",
957 dim,
958 self.ndim()
959 )));
960 }
961 if indices.shape() != src.shape() {
962 return Err(TorshError::ShapeMismatch {
963 expected: indices.shape().dims().to_vec(),
964 got: src.shape().dims().to_vec(),
965 });
966 }
967 let indices_data = indices.to_vec()?;
968 let src_data = src.to_vec()?;
969 let mut result_data = self.to_vec()?;
970 let self_shape_ref = self.shape();
971 let self_shape = self_shape_ref.dims();
972 let mut counts = if reduce == "mean" {
973 vec![0usize; result_data.len()]
974 } else {
975 vec![]
976 };
977 if self.ndim() == 1 {
978 for (i, &index) in indices_data.iter().enumerate() {
979 let idx = if index < 0 {
980 (self_shape[0] as i64 + index) as usize
981 } else {
982 index as usize
983 };
984 if idx >= self_shape[0] {
985 return Err(TorshError::InvalidArgument(format!(
986 "Index {} out of bounds for dimension size {}",
987 index, self_shape[0]
988 )));
989 }
990 result_data[idx] = match reduce {
991 "sum" => result_data[idx] + src_data[i],
992 "prod" => result_data[idx] * src_data[i],
993 "mean" => {
994 counts[idx] += 1;
995 result_data[idx] + src_data[i]
996 }
997 "amax" => {
998 if src_data[i] > result_data[idx] {
999 src_data[i]
1000 } else {
1001 result_data[idx]
1002 }
1003 }
1004 "amin" => {
1005 if src_data[i] < result_data[idx] {
1006 src_data[i]
1007 } else {
1008 result_data[idx]
1009 }
1010 }
1011 _ => {
1012 return Err(TorshError::InvalidArgument(format!(
1013 "Unknown reduce operation: {}. Supported: sum, prod, mean, amax, amin",
1014 reduce
1015 )));
1016 }
1017 };
1018 }
1019 if reduce == "mean" {
1020 for (i, count) in counts.iter().enumerate() {
1021 if *count > 0 {
1022 result_data[i] = T::from_usize(*count)
1023 .and_then(|c| Some(result_data[i] / c))
1024 .unwrap_or(result_data[i]);
1025 }
1026 }
1027 }
1028 } else {
1029 let dim_size = self_shape[dim];
1030 let _outer_size: usize = self_shape[..dim].iter().product();
1031 let _inner_size: usize = self_shape[dim + 1..].iter().product();
1032 let mut self_strides = vec![1; self_shape.len()];
1033 for i in (0..self_shape.len() - 1).rev() {
1034 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1035 }
1036 let src_shape_ref = src.shape();
1037 let src_shape = src_shape_ref.dims();
1038 let mut src_strides = vec![1; src_shape.len()];
1039 for i in (0..src_shape.len() - 1).rev() {
1040 src_strides[i] = src_strides[i + 1] * src_shape[i + 1];
1041 }
1042 for i in 0..indices_data.len() {
1043 let index = indices_data[i];
1044 let idx = if index < 0 {
1045 (dim_size as i64 + index) as usize
1046 } else {
1047 index as usize
1048 };
1049 if idx >= dim_size {
1050 return Err(TorshError::InvalidArgument(format!(
1051 "Index {} out of bounds for dimension {} size {}",
1052 index, dim, dim_size
1053 )));
1054 }
1055 let mut coords = vec![0; self_shape.len()];
1056 let mut remainder = i;
1057 for (d, &stride) in src_strides.iter().enumerate() {
1058 coords[d] = remainder / stride;
1059 remainder %= stride;
1060 }
1061 coords[dim] = idx;
1062 let flat_idx = coords
1063 .iter()
1064 .zip(self_strides.iter())
1065 .map(|(c, s)| c * s)
1066 .sum::<usize>();
1067 result_data[flat_idx] = match reduce {
1068 "sum" => result_data[flat_idx] + src_data[i],
1069 "prod" => result_data[flat_idx] * src_data[i],
1070 "mean" => {
1071 counts[flat_idx] += 1;
1072 result_data[flat_idx] + src_data[i]
1073 }
1074 "amax" => {
1075 if src_data[i] > result_data[flat_idx] {
1076 src_data[i]
1077 } else {
1078 result_data[flat_idx]
1079 }
1080 }
1081 "amin" => {
1082 if src_data[i] < result_data[flat_idx] {
1083 src_data[i]
1084 } else {
1085 result_data[flat_idx]
1086 }
1087 }
1088 _ => {
1089 return Err(TorshError::InvalidArgument(format!(
1090 "Unknown reduce operation: {}",
1091 reduce
1092 )));
1093 }
1094 };
1095 }
1096 if reduce == "mean" {
1097 for (i, count) in counts.iter().enumerate() {
1098 if *count > 0 {
1099 result_data[i] = T::from_usize(*count)
1100 .and_then(|c| Some(result_data[i] / c))
1101 .unwrap_or(result_data[i]);
1102 }
1103 }
1104 }
1105 }
1106 Self::from_data(result_data, self_shape.to_vec(), self.device)
1107 }
1108 pub fn diagonal_scatter(
1130 &self,
1131 src: &Tensor<T>,
1132 offset: isize,
1133 dim1: usize,
1134 dim2: usize,
1135 ) -> Result<Self> {
1136 if dim1 >= self.ndim() || dim2 >= self.ndim() {
1137 return Err(TorshError::InvalidArgument(format!(
1138 "Dimensions ({}, {}) out of range for {}-dimensional tensor",
1139 dim1,
1140 dim2,
1141 self.ndim()
1142 )));
1143 }
1144 if dim1 == dim2 {
1145 return Err(TorshError::InvalidArgument(
1146 "dim1 and dim2 must be different".to_string(),
1147 ));
1148 }
1149 let self_shape_ref = self.shape();
1150 let self_shape = self_shape_ref.dims();
1151 let dim1_size = self_shape[dim1];
1152 let dim2_size = self_shape[dim2];
1153 let diag_len = if offset >= 0 {
1154 let offset_u = offset as usize;
1155 if offset_u >= dim2_size {
1156 0
1157 } else {
1158 std::cmp::min(dim1_size, dim2_size - offset_u)
1159 }
1160 } else {
1161 let offset_u = (-offset) as usize;
1162 if offset_u >= dim1_size {
1163 0
1164 } else {
1165 std::cmp::min(dim1_size - offset_u, dim2_size)
1166 }
1167 };
1168 if src.numel() != diag_len {
1169 return Err(TorshError::ShapeMismatch {
1170 expected: vec![diag_len],
1171 got: vec![src.numel()],
1172 });
1173 }
1174 let mut result_data = self.to_vec()?;
1175 let src_data = src.to_vec()?;
1176 let mut strides = vec![1; self_shape.len()];
1177 for i in (0..self_shape.len() - 1).rev() {
1178 strides[i] = strides[i + 1] * self_shape[i + 1];
1179 }
1180 for i in 0..diag_len {
1181 let mut indices = vec![0; self_shape.len()];
1182 if offset >= 0 {
1183 indices[dim1] = i;
1184 indices[dim2] = i + offset as usize;
1185 } else {
1186 indices[dim1] = i + (-offset) as usize;
1187 indices[dim2] = i;
1188 }
1189 let mut flat_idx = 0;
1190 for (d, &idx) in indices.iter().enumerate() {
1191 flat_idx += idx * strides[d];
1192 }
1193 result_data[flat_idx] = src_data[i];
1194 }
1195 Self::from_data(result_data, self_shape.to_vec(), self.device)
1196 }
1197 pub fn select_scatter(&self, src: &Tensor<T>, dim: isize, index: isize) -> Result<Self> {
1218 let ndim = self.ndim() as isize;
1219 let dim_normalized = if dim < 0 { ndim + dim } else { dim };
1220 if dim_normalized < 0 || dim_normalized >= ndim {
1221 return Err(TorshError::InvalidArgument(format!(
1222 "Dimension {} out of range for {}-dimensional tensor",
1223 dim,
1224 self.ndim()
1225 )));
1226 }
1227 let dim_u = dim_normalized as usize;
1228 let self_shape_ref = self.shape();
1229 let self_shape = self_shape_ref.dims();
1230 let index_normalized = if index < 0 {
1231 (self_shape[dim_u] as isize) + index
1232 } else {
1233 index
1234 };
1235 if index_normalized < 0 || index_normalized >= self_shape[dim_u] as isize {
1236 return Err(TorshError::InvalidArgument(format!(
1237 "Index {} out of bounds for dimension {} with size {}",
1238 index, dim_u, self_shape[dim_u]
1239 )));
1240 }
1241 let index_u = index_normalized as usize;
1242 let expected_src_shape: Vec<usize> = self_shape
1243 .iter()
1244 .enumerate()
1245 .filter(|(i, _)| *i != dim_u)
1246 .map(|(_, &s)| s)
1247 .collect();
1248 let src_shape_ref = src.shape();
1249 let src_shape = src_shape_ref.dims();
1250 if src_shape != expected_src_shape.as_slice() {
1251 return Err(TorshError::ShapeMismatch {
1252 expected: expected_src_shape,
1253 got: src_shape.to_vec(),
1254 });
1255 }
1256 let mut result_data = self.to_vec()?;
1257 let src_data = src.to_vec()?;
1258 let mut self_strides = vec![1; self_shape.len()];
1259 for i in (0..self_shape.len() - 1).rev() {
1260 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1261 }
1262 let outer_size: usize = self_shape[..dim_u].iter().product();
1263 let inner_size: usize = self_shape[dim_u + 1..].iter().product();
1264 for outer in 0..outer_size {
1265 for inner in 0..inner_size {
1266 let self_idx =
1267 outer * (self_shape[dim_u] * inner_size) + index_u * inner_size + inner;
1268 let src_idx = outer * inner_size + inner;
1269 result_data[self_idx] = src_data[src_idx];
1270 }
1271 }
1272 Self::from_data(result_data, self_shape.to_vec(), self.device)
1273 }
1274 pub fn slice_scatter(
1297 &self,
1298 src: &Tensor<T>,
1299 dim: isize,
1300 start: Option<isize>,
1301 end: Option<isize>,
1302 step: usize,
1303 ) -> Result<Self> {
1304 if step == 0 {
1305 return Err(TorshError::InvalidArgument(
1306 "Step must be greater than 0".to_string(),
1307 ));
1308 }
1309 let ndim = self.ndim() as isize;
1310 let dim_normalized = if dim < 0 { ndim + dim } else { dim };
1311 if dim_normalized < 0 || dim_normalized >= ndim {
1312 return Err(TorshError::InvalidArgument(format!(
1313 "Dimension {} out of range for {}-dimensional tensor",
1314 dim,
1315 self.ndim()
1316 )));
1317 }
1318 let dim_u = dim_normalized as usize;
1319 let self_shape_ref = self.shape();
1320 let self_shape = self_shape_ref.dims();
1321 let dim_size = self_shape[dim_u] as isize;
1322 let start_normalized = start.unwrap_or(0);
1323 let start_normalized = if start_normalized < 0 {
1324 dim_size + start_normalized
1325 } else {
1326 start_normalized
1327 };
1328 let start_normalized = std::cmp::max(0, std::cmp::min(start_normalized, dim_size)) as usize;
1329 let end_normalized = end.unwrap_or(dim_size);
1330 let end_normalized = if end_normalized < 0 {
1331 dim_size + end_normalized
1332 } else {
1333 end_normalized
1334 };
1335 let end_normalized = std::cmp::max(0, std::cmp::min(end_normalized, dim_size)) as usize;
1336 let slice_len = if end_normalized > start_normalized {
1337 (end_normalized - start_normalized + step - 1) / step
1338 } else {
1339 0
1340 };
1341 let mut expected_src_shape = self_shape.to_vec();
1342 expected_src_shape[dim_u] = slice_len;
1343 let src_shape_ref = src.shape();
1344 let src_shape = src_shape_ref.dims();
1345 if src_shape != expected_src_shape.as_slice() {
1346 return Err(TorshError::ShapeMismatch {
1347 expected: expected_src_shape,
1348 got: src_shape.to_vec(),
1349 });
1350 }
1351 let mut result_data = self.to_vec()?;
1352 let src_data = src.to_vec()?;
1353 let mut self_strides = vec![1; self_shape.len()];
1354 for i in (0..self_shape.len() - 1).rev() {
1355 self_strides[i] = self_strides[i + 1] * self_shape[i + 1];
1356 }
1357 let outer_size: usize = self_shape[..dim_u].iter().product();
1358 let inner_size: usize = self_shape[dim_u + 1..].iter().product();
1359 for outer in 0..outer_size {
1360 for slice_idx in 0..slice_len {
1361 let self_dim_idx = start_normalized + slice_idx * step;
1362 for inner in 0..inner_size {
1363 let self_idx = outer * (self_shape[dim_u] * inner_size)
1364 + self_dim_idx * inner_size
1365 + inner;
1366 let src_idx = outer * (slice_len * inner_size) + slice_idx * inner_size + inner;
1367 result_data[self_idx] = src_data[src_idx];
1368 }
1369 }
1370 }
1371 Self::from_data(result_data, self_shape.to_vec(), self.device)
1372 }
1373}