1#![allow(clippy::redundant_closure_call)]
3use crate::backend::{BackendDevice, BackendStorage};
4use crate::op::{BackpropOp, BinaryOp, CmpOp, Op, ReduceOp, UnaryOp};
5use crate::scalar::TensorOrScalar;
6use crate::shape::{Dim, Dims, ShapeWithOneHole};
7use crate::storage::{StorageMutRef, StorageRef};
8use crate::{bail, storage::Storage, DType, Device, Error, Layout, Result, Shape};
9use parking_lot::RwLock;
10use std::sync::Arc;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct TensorId(usize);
15
16impl TensorId {
17 fn new() -> Self {
18 use std::sync::atomic;
20 static COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(1);
21 Self(COUNTER.fetch_add(1, atomic::Ordering::Relaxed))
22 }
23}
24
25pub struct Tensor_ {
26 id: TensorId,
27 storage: Arc<RwLock<Storage>>,
40 layout: Layout,
41 op: BackpropOp,
42 is_variable: bool,
43 dtype: DType,
44 device: Device,
45}
46
47impl AsRef<Tensor> for Tensor {
48 fn as_ref(&self) -> &Tensor {
49 self
50 }
51}
52
53#[derive(Clone)]
57pub struct Tensor(Arc<Tensor_>);
71
72impl std::ops::Deref for Tensor {
73 type Target = Tensor_;
74
75 fn deref(&self) -> &Self::Target {
76 self.0.as_ref()
77 }
78}
79
80macro_rules! unary_op {
81 ($fn_name:ident, $op_name:ident) => {
82 pub fn $fn_name(&self) -> Result<Self> {
83 let shape = self.shape();
84 if shape.elem_count() == 0 {
85 return Ok(self.clone());
86 }
87 let storage = self
88 .storage()
89 .unary_impl::<crate::op::$op_name>(self.layout())?;
90 let op = BackpropOp::new1(self, |s| Op::Unary(s, UnaryOp::$op_name));
91 Ok(from_storage(storage, shape.clone(), op, false))
92 }
93 };
94}
95
96macro_rules! binary_op {
97 ($fn_name:ident, $op_name:ident) => {
98 pub fn $fn_name(&self, rhs: &Self) -> Result<Self> {
99 let shape = self.same_shape_binary_op(rhs, stringify!($fn_name))?;
100 if shape.elem_count() == 0 {
101 return Ok(self.clone());
102 }
103 let storage = self.storage().binary_impl::<crate::op::$op_name>(
104 &*rhs.storage(),
105 self.layout(),
106 rhs.layout(),
107 )?;
108 let op = BackpropOp::new2(self, rhs, |t1, t2| Op::Binary(t1, t2, BinaryOp::$op_name));
109 Ok(from_storage(storage, shape.clone(), op, false))
110 }
111 };
112}
113
114macro_rules! binary_op_scalar {
115 ($fn_name:ident, $op_name:ident) => {
116 pub fn $fn_name<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
117 let rhs = match rhs.to_tensor_scalar()? {
118 crate::scalar::TensorScalar::Tensor(rhs) => rhs,
119 crate::scalar::TensorScalar::Scalar(rhs) => rhs
120 .to_dtype(self.dtype())?
121 .to_device(self.device())?
122 .broadcast_as(self.shape())?,
123 };
124 let shape = self.same_shape_binary_op(&rhs, stringify!($fn_name))?;
125 if self.elem_count() == 0 {
126 return Ok(self.clone());
127 }
128 let storage = self.storage().binary_impl::<crate::op::$op_name>(
129 &*rhs.storage(),
130 self.layout(),
131 rhs.layout(),
132 )?;
133 let op = BackpropOp::new2(self, &rhs, |t1, t2| Op::Binary(t1, t2, BinaryOp::$op_name));
134 Ok(from_storage(storage, shape.clone(), op, false))
135 }
136 };
137}
138
139macro_rules! broadcast_binary_op {
140 ($fn_name:ident, $inner_fn_name:ident) => {
141 pub fn $fn_name(&self, rhs: &Self) -> Result<Self> {
142 let lhs = self;
143 let shape = lhs
144 .shape()
145 .broadcast_shape_binary_op(rhs.shape(), stringify!($fn_name))?;
146 let l_broadcast = shape != *lhs.shape();
147 let r_broadcast = shape != *rhs.shape();
148 match (l_broadcast, r_broadcast) {
149 (true, true) => lhs
150 .broadcast_as(&shape)?
151 .$inner_fn_name(&rhs.broadcast_as(&shape)?),
152 (false, true) => lhs.$inner_fn_name(&rhs.broadcast_as(&shape)?),
153 (true, false) => lhs.broadcast_as(&shape)?.$inner_fn_name(rhs),
154 (false, false) => lhs.$inner_fn_name(rhs),
155 }
156 }
157 };
158}
159
160pub(crate) fn from_storage<S: Into<Shape>>(
162 storage: Storage,
163 shape: S,
164 op: BackpropOp,
165 is_variable: bool,
166) -> Tensor {
167 let dtype = storage.dtype();
168 let device = storage.device();
169 let tensor_ = Tensor_ {
170 id: TensorId::new(),
171 storage: Arc::new(RwLock::new(storage)),
172 layout: Layout::contiguous(shape),
173 op,
174 is_variable,
175 dtype,
176 device,
177 };
178 Tensor(Arc::new(tensor_))
179}
180
181impl Tensor {
182 pub(crate) fn ones_impl<S: Into<Shape>>(
183 shape: S,
184 dtype: DType,
185 device: &Device,
186 is_variable: bool,
187 ) -> Result<Self> {
188 let none = BackpropOp::none();
189 let shape = shape.into();
190 let mut storage = unsafe { device.alloc_uninit(&shape, dtype)? };
191 let layout = Layout::contiguous(shape.clone());
192 storage.const_set(crate::scalar::Scalar::one(dtype), &layout)?;
193 Ok(from_storage(storage, shape, none, is_variable))
194 }
195
196 pub fn ones<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
206 Self::ones_impl(shape, dtype, device, false)
207 }
208
209 pub fn const_set(&self, value: crate::scalar::Scalar) -> Result<()> {
210 self.storage_mut().const_set(value, self.layout())
211 }
212
213 pub fn zero_set(&self) -> Result<()> {
214 self.const_set(crate::scalar::Scalar::zero(self.dtype()))
215 }
216
217 pub fn one_set(&self) -> Result<()> {
218 self.const_set(crate::scalar::Scalar::one(self.dtype()))
219 }
220
221 pub fn ones_like(&self) -> Result<Self> {
231 Tensor::ones(self.shape(), self.dtype(), self.device())
232 }
233
234 pub(crate) fn zeros_impl<S: Into<Shape>>(
237 shape: S,
238 dtype: DType,
239 device: &Device,
240 is_variable: bool,
241 ) -> Result<Self> {
242 let none = BackpropOp::none();
243 let shape = shape.into();
244 let storage = device.zeros(&shape, dtype)?;
245 Ok(from_storage(storage, shape, none, is_variable))
246 }
247
248 pub fn zeros<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
258 Self::zeros_impl(shape, dtype, device, false)
259 }
260
261 pub fn zeros_like(&self) -> Result<Self> {
272 Tensor::zeros(self.shape(), self.dtype(), self.device())
273 }
274
275 pub(crate) unsafe fn empty_impl<S: Into<Shape>>(
278 shape: S,
279 dtype: DType,
280 device: &Device,
281 is_variable: bool,
282 ) -> Result<Self> {
283 let none = BackpropOp::none();
284 let shape = shape.into();
285 let storage = device.alloc_uninit(&shape, dtype)?;
286 Ok(from_storage(storage, shape, none, is_variable))
287 }
288
289 pub unsafe fn empty<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
301 Self::empty_impl(shape, dtype, device, false)
302 }
303
304 pub unsafe fn empty_like(&self) -> Result<Self> {
317 Tensor::empty(self.shape(), self.dtype(), self.device())
318 }
319
320 pub(crate) fn rand_impl<S: Into<Shape>, T: crate::FloatDType>(
321 lo: T,
322 up: T,
323 s: S,
324 device: &Device,
325 is_variable: bool,
326 ) -> Result<Self> {
327 let s = s.into();
328 let storage = device.rand_uniform(lo, up, &s)?;
329 let none = BackpropOp::none();
330 Ok(from_storage(storage, s, none, is_variable))
331 }
332
333 pub(crate) fn rand_f64_impl<S: Into<Shape>>(
334 lo: f64,
335 up: f64,
336 s: S,
337 dtype: DType,
338 device: &Device,
339 is_variable: bool,
340 ) -> Result<Self> {
341 let s = s.into();
342 let storage = device.rand_uniform_f64(lo, up, &s, dtype)?;
343 let none = BackpropOp::none();
344 Ok(from_storage(storage, s, none, is_variable))
345 }
346
347 pub fn rand<S: Into<Shape>, T: crate::FloatDType>(
349 lo: T,
350 up: T,
351 s: S,
352 device: &Device,
353 ) -> Result<Self> {
354 Self::rand_impl(lo, up, s, device, false)
355 }
356
357 pub fn rand_like(&self, lo: f64, up: f64) -> Result<Self> {
358 Tensor::rand_f64_impl(lo, up, self.shape(), self.dtype(), self.device(), false)
359 }
360
361 pub(crate) fn randn_impl<S: Into<Shape>, T: crate::FloatDType>(
362 mean: T,
363 std: T,
364 s: S,
365 device: &Device,
366 is_variable: bool,
367 ) -> Result<Self> {
368 let s = s.into();
369 let storage = device.rand_normal(mean, std, &s)?;
370 let none = BackpropOp::none();
371 Ok(from_storage(storage, s, none, is_variable))
372 }
373
374 pub(crate) fn randn_f64_impl<S: Into<Shape>>(
375 mean: f64,
376 std: f64,
377 s: S,
378 dtype: DType,
379 device: &Device,
380 is_variable: bool,
381 ) -> Result<Self> {
382 let s = s.into();
383 let storage = device.rand_normal_f64(mean, std, &s, dtype)?;
384 let none = BackpropOp::none();
385 Ok(from_storage(storage, s, none, is_variable))
386 }
387
388 pub fn randn_like(&self, mean: f64, stdev: f64) -> Result<Self> {
389 Tensor::randn_f64_impl(
390 mean,
391 stdev,
392 self.shape(),
393 self.dtype(),
394 self.device(),
395 false,
396 )
397 }
398
399 pub fn randn<S: Into<Shape>, T: crate::FloatDType>(
402 mean: T,
403 std: T,
404 s: S,
405 device: &Device,
406 ) -> Result<Self> {
407 Self::randn_impl(mean, std, s, device, false)
408 }
409
410 pub(crate) fn new_impl<A: crate::device::NdArray>(
411 array: A,
412 shape: Shape,
413 device: &Device,
414 is_variable: bool,
415 ) -> Result<Self> {
416 let n: usize = shape.elem_count();
417 let buffer_size: usize = array.shape()?.elem_count();
418 if buffer_size != n {
419 return Err(Error::ShapeMismatch { buffer_size, shape }.bt());
420 }
421 let storage = device.storage(array)?;
422 let none = BackpropOp::none();
423 Ok(from_storage(storage, shape, none, is_variable))
424 }
425
426 pub fn new<A: crate::device::NdArray>(array: A, device: &Device) -> Result<Self> {
428 let shape = array.shape()?;
429 Self::new_impl(array, shape, device, false)
430 }
431
432 pub fn full<D: crate::WithDType, S: Into<Shape>>(
443 value: D,
444 shape: S,
445 device: &Device,
446 ) -> Result<Self> {
447 let none = BackpropOp::none();
448 let shape = shape.into();
449 let mut storage = unsafe { device.alloc_uninit(&shape, D::DTYPE)? };
450 let layout = Layout::contiguous(shape.clone());
451 storage.const_set(value.to_scalar(), &layout)?;
452 Ok(from_storage(storage, shape, none, false))
453 }
454
455 pub fn from_iter<D: crate::WithDType>(
464 iter: impl IntoIterator<Item = D>,
465 device: &Device,
466 ) -> Result<Self> {
467 let data = iter.into_iter().collect::<Vec<_>>();
468 let len = data.len();
469 Self::from_vec_impl(data, len, device, false)
470 }
471
472 pub fn arange<D: crate::WithDType>(start: D, end: D, device: &Device) -> Result<Self> {
482 Self::arange_step(start, end, D::one(), device)
483 }
484
485 pub fn arange_step<D: crate::WithDType>(
495 start: D,
496 end: D,
497 step: D,
498 device: &Device,
499 ) -> Result<Self> {
500 if D::is_zero(&step) {
501 bail!("step cannot be zero")
502 }
503 let mut data = vec![];
504 let mut current = start;
505 if step >= D::zero() {
506 while current < end {
507 data.push(current);
508 current += step;
509 }
510 } else {
511 while current > end {
512 data.push(current);
513 current += step;
514 }
515 }
516 let len = data.len();
517 Self::from_vec_impl(data, len, device, false)
518 }
519
520 pub(crate) fn from_vec_impl<S: ShapeWithOneHole, D: crate::WithDType>(
521 data: Vec<D>,
522 shape: S,
523 device: &Device,
524 is_variable: bool,
525 ) -> Result<Self> {
526 let shape = shape.into_shape(data.len())?;
527 let storage = device.storage_owned(data)?;
528 let none = BackpropOp::none();
529 Ok(from_storage(storage, shape, none, is_variable))
530 }
531
532 pub fn from_vec<S: ShapeWithOneHole, D: crate::WithDType>(
546 data: Vec<D>,
547 shape: S,
548 device: &Device,
549 ) -> Result<Self> {
550 Self::from_vec_impl(data, shape, device, false)
551 }
552
553 pub fn from_slice<S: ShapeWithOneHole, D: crate::WithDType>(
567 array: &[D],
568 shape: S,
569 device: &Device,
570 ) -> Result<Self> {
571 let shape = shape.into_shape(array.len())?;
572 let storage = device.storage_from_slice(array)?;
573 let none = BackpropOp::none();
574 Ok(from_storage(storage, shape, none, false))
575 }
576
577 pub(crate) fn same_shape_binary_op(&self, rhs: &Self, op: &'static str) -> Result<&Shape> {
578 let lhs = self.shape();
579 let rhs = rhs.shape();
580 if lhs != rhs {
581 Err(Error::ShapeMismatchBinaryOp {
582 lhs: lhs.clone(),
583 rhs: rhs.clone(),
584 op,
585 }
586 .bt())
587 } else {
588 Ok(lhs)
589 }
590 }
591
592 pub fn track_op(&self) -> bool {
595 self.is_variable || self.op.is_some()
596 }
597
598 pub fn from_storage<S: Into<Shape>>(
604 storage: Storage,
605 shape: S,
606 op: BackpropOp,
607 is_variable: bool,
608 ) -> Tensor {
609 from_storage(storage, shape, op, is_variable)
610 }
611
612 binary_op!(add, Add);
615 binary_op!(mul, Mul);
616 binary_op!(sub, Sub);
617 binary_op!(div, Div);
618 binary_op_scalar!(maximum, Maximum);
619 binary_op_scalar!(minimum, Minimum);
620 broadcast_binary_op!(broadcast_add, add);
621 broadcast_binary_op!(broadcast_mul, mul);
622 broadcast_binary_op!(broadcast_sub, sub);
623 broadcast_binary_op!(broadcast_div, div);
624 broadcast_binary_op!(broadcast_maximum, maximum);
625 broadcast_binary_op!(broadcast_minimum, minimum);
626 broadcast_binary_op!(broadcast_eq, eq);
627 broadcast_binary_op!(broadcast_ne, ne);
628 broadcast_binary_op!(broadcast_lt, lt);
629 broadcast_binary_op!(broadcast_le, le);
630 broadcast_binary_op!(broadcast_gt, gt);
631 broadcast_binary_op!(broadcast_ge, ge);
632
633 unary_op!(recip, Recip);
634 unary_op!(neg, Neg);
635 unary_op!(exp, Exp);
636 unary_op!(log, Log);
637 unary_op!(sin, Sin);
638 unary_op!(cos, Cos);
639 unary_op!(tanh, Tanh);
640 unary_op!(abs, Abs);
641 unary_op!(sqr, Sqr);
642 unary_op!(sqrt, Sqrt);
643 unary_op!(gelu, Gelu);
644 unary_op!(gelu_erf, GeluErf);
645 unary_op!(erf, Erf);
646 unary_op!(relu, Relu);
647 unary_op!(silu, Silu);
648 unary_op!(ceil, Ceil);
649 unary_op!(floor, Floor);
650 unary_op!(round, Round);
651 unary_op!(sign, Sign);
652
653 pub fn round_to(&self, decimals: i32) -> Result<Self> {
658 let mult = 10f64.powi(decimals);
659 (self * mult)?.round()? * (1f64 / mult)
660 }
661
662 pub fn to_scalar<S: crate::WithDType>(&self) -> Result<S> {
665 if self.rank() != 0 {
666 Err(Error::UnexpectedNumberOfDims {
667 expected: 0,
668 got: self.rank(),
669 shape: self.shape().clone(),
670 }
671 .bt())?
672 }
673 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
674 let data = S::cpu_storage_as_slice(cpu_storage)?;
675 Ok::<_, Error>(data[self.layout().start_offset()])
676 };
677 match &*self.storage() {
678 Storage::Cpu(cpu_storage) => from_cpu_storage(cpu_storage),
679 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
680 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
681 #[cfg(feature = "rocm")]
682 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
683 #[cfg(feature = "vulkan")]
684 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
685 #[cfg(feature = "wgpu")]
686 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
687 }
688 }
689
690 pub fn to_vec0<S: crate::WithDType>(&self) -> Result<S> {
692 self.to_scalar::<S>()
693 }
694
695 pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Tensor> {
697 let repeats = shape.into();
699 let repeats = repeats.dims();
700 let mut inp = if self.rank() < repeats.len() {
701 let shape = [vec![1; repeats.len() - self.rank()], self.dims().to_vec()].concat();
702 self.reshape(shape)?
703 } else {
704 self.clone()
705 };
706 for (idx, &repeat) in repeats.iter().enumerate() {
707 inp = match repeat {
708 0 => inp.narrow(idx, 0, 0)?,
709 1 => inp,
710 repeat => Tensor::cat(&vec![&inp; repeat], idx)?,
711 };
712 }
713 Ok(inp)
714 }
715
716 pub fn meshgrid<A: AsRef<Tensor>>(args: &[A], xy_indexing: bool) -> Result<Vec<Self>> {
753 if args.len() <= 1 {
754 Err(Error::OpRequiresAtLeastTwoTensors { op: "meshgrid" }.bt())?
755 }
756 let args: Vec<_> = if xy_indexing {
757 args.iter().rev().collect()
758 } else {
759 args.iter().collect()
760 };
761
762 let mut shape = Vec::with_capacity(args.len());
763 for arg in args.iter() {
764 shape.push(arg.as_ref().dims1()?)
765 }
766
767 let mut grids = Vec::with_capacity(args.len());
768 for idx in 0..args.len() {
769 let mut ones = vec![1usize; args.len()];
770 ones[idx] = shape[idx];
771 let arg = args[idx].as_ref().reshape(ones)?;
772 let mut repeats = shape.clone();
773 repeats[idx] = 1;
774 let repeated_tensor = arg.repeat(repeats)?;
775 grids.push(repeated_tensor);
776 }
777 if xy_indexing {
778 grids.reverse();
779 }
780 Ok(grids)
781 }
782
783 pub fn affine(&self, mul: f64, add: f64) -> Result<Self> {
795 if self.elem_count() == 0 {
796 return Ok(self.clone());
797 }
798 let storage = self.storage().affine(self.layout(), mul, add)?;
799 let op = BackpropOp::new1(self, |arg| Op::Affine { arg, mul, add });
800 Ok(from_storage(storage, self.shape(), op, false))
801 }
802
803 pub fn elu(&self, alpha: f64) -> Result<Self> {
805 if self.elem_count() == 0 {
806 return Ok(self.clone());
807 }
808 let storage = self.storage().elu(self.layout(), alpha)?;
809 let op = BackpropOp::new1(self, |t| Op::Elu(t, alpha));
810 Ok(from_storage(storage, self.shape(), op, false))
811 }
812
813 pub fn powf(&self, e: f64) -> Result<Self> {
815 if self.elem_count() == 0 {
816 return Ok(self.clone());
817 }
818 let storage = self.storage().powf(self.layout(), e)?;
819 let op = BackpropOp::new1(self, |t| Op::Powf(t, e));
820 Ok(from_storage(storage, self.shape(), op, false))
821 }
822
823 pub(crate) fn check_dim(&self, dim: usize, op: &'static str) -> Result<()> {
824 if dim >= self.dims().len() {
825 Err(Error::DimOutOfRange {
826 shape: self.shape().clone(),
827 dim: dim as i32,
828 op,
829 }
830 .bt())?
831 } else {
832 Ok(())
833 }
834 }
835
836 pub fn chunk<D: Dim>(&self, chunks: usize, dim: D) -> Result<Vec<Self>> {
839 let dim = dim.to_index(self.shape(), "chunk")?;
840 let size = self.dim(dim)?;
841 if size < chunks {
842 (0..size).map(|i| self.narrow(dim, i, 1)).collect()
843 } else {
844 let chunk_size = size / chunks;
845 let cnt_additional = size % chunks;
846 let mut tensors = vec![];
847 let mut sum_chunk_size = 0;
848 for i in 0..chunks {
849 let chunk_size = if i < cnt_additional {
850 chunk_size + 1
851 } else {
852 chunk_size
853 };
854 let tensor = self.narrow(dim, sum_chunk_size, chunk_size)?;
855 tensors.push(tensor);
856 sum_chunk_size += chunk_size
857 }
858 Ok(tensors)
859 }
860 }
861
862 pub fn narrow<D: Dim>(&self, dim: D, start: usize, len: usize) -> Result<Self> {
889 let dims = self.dims();
890 let dim = dim.to_index(self.shape(), "narrow")?;
891 let err = |msg| {
892 Err::<(), _>(
893 Error::NarrowInvalidArgs {
894 shape: self.shape().clone(),
895 dim,
896 start,
897 len,
898 msg,
899 }
900 .bt(),
901 )
902 };
903 if start > dims[dim] {
904 err("start > dim_len")?
905 }
906 if start.saturating_add(len) > dims[dim] {
907 err("start + len > dim_len")?
908 }
909 if start == 0 && dims[dim] == len {
910 Ok(self.clone())
911 } else {
912 let op = BackpropOp::new1(self, |t| Op::Narrow(t, dim, start, len));
913 let layout = self.layout().narrow(dim, start, len)?;
914 let tensor_ = Tensor_ {
915 id: TensorId::new(),
916 storage: self.storage.clone(),
917 layout,
918 op,
919 is_variable: false,
920 dtype: self.dtype,
921 device: self.device.clone(),
922 };
923 Ok(Tensor(Arc::new(tensor_)))
924 }
925 }
926
927 fn squeeze_dims(self, dims: &[usize]) -> Result<Self> {
928 match dims {
929 [] => Ok(self),
930 [i] => self.squeeze(*i),
931 dims => {
932 let dims = self
933 .dims()
934 .iter()
935 .enumerate()
936 .filter_map(|(dim_idx, &v)| {
937 if dims.contains(&dim_idx) {
938 None
939 } else {
940 Some(v)
941 }
942 })
943 .collect::<Vec<_>>();
944 self.reshape(dims)
945 }
946 }
947 }
948
949 fn reduce_impl<D: Dim>(&self, dim: D, keepdim: bool, op: ReduceOp) -> Result<Self> {
950 let dim = dim.to_index(self.shape(), op.name())?;
951 let storage = self.storage().reduce_op(op, self.layout(), &[dim])?;
952 let mut dims = self.dims().to_vec();
953 dims[dim] = 1;
954 let op = match op {
955 ReduceOp::Sum | ReduceOp::Min | ReduceOp::Max => {
956 BackpropOp::new1(self, |arg| Op::Reduce(arg, op, dims.to_vec()))
957 }
958 ReduceOp::ArgMin | ReduceOp::ArgMax => BackpropOp::none(),
959 };
960 let res = from_storage(storage, dims, op, false);
961 if keepdim {
962 Ok(res)
963 } else {
964 res.squeeze_dims(&[dim])
965 }
966 }
967
968 fn sum_impl<D: Dims>(&self, sum_dims: D, keepdim: bool) -> Result<Self> {
969 let sum_dims = sum_dims.to_indexes(self.shape(), "sum")?;
970 let storage = self
971 .storage()
972 .reduce_op(ReduceOp::Sum, self.layout(), &sum_dims)?;
973 let mut dims = self.dims().to_vec();
974 for &sum_dim in sum_dims.iter() {
975 dims[sum_dim] = 1
976 }
977 let op = BackpropOp::new1(self, |a| Op::Reduce(a, ReduceOp::Sum, dims.to_vec()));
978 let sum = from_storage(storage, dims, op, false);
979 if keepdim {
980 Ok(sum)
981 } else {
982 sum.squeeze_dims(&sum_dims)
983 }
984 }
985
986 pub fn roll<D>(&self, shift: i32, dim: D) -> Result<Self>
1000 where
1001 D: Dim + Clone,
1002 {
1003 let dim = dim.to_index(self.shape(), "roll")?;
1004 let dim_size = self.dim(dim)?;
1005 let shift = shift.rem_euclid(dim_size as i32) as usize;
1006 if shift == 0 {
1007 Ok(self.clone())
1008 } else {
1009 let a = self.narrow(dim, 0, dim_size - shift)?;
1010 let b = self.narrow(dim, dim_size - shift, shift)?;
1011 Tensor::cat(&[&b, &a], dim)
1012 }
1013 }
1014
1015 pub fn sum_keepdim<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1033 self.sum_impl(sum_dims, true)
1034 }
1035
1036 pub fn sum<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1040 self.sum_impl(sum_dims, false)
1041 }
1042
1043 pub fn mean_keepdim<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1061 let mean_dims = mean_dims.to_indexes(self.shape(), "mean-keepdim")?;
1062 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1063 let scale = 1f64 / (reduced_dim as f64);
1064 self.sum_impl(mean_dims, true)? * scale
1065 }
1066
1067 pub fn mean<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1071 let mean_dims = mean_dims.to_indexes(self.shape(), "mean")?;
1072 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1073 let scale = 1f64 / (reduced_dim as f64);
1074 self.sum_impl(mean_dims, false)? * scale
1075 }
1076
1077 pub fn var_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1079 let dim = dim.to_index(self.shape(), "var")?;
1080 let mean = self.mean_keepdim(dim)?;
1081 let squares = self.broadcast_sub(&mean)?.sqr()?;
1082 squares.sum_impl(dim, true)? / (self.dim(dim)? - 1) as f64
1083 }
1084
1085 pub fn var<D: Dim>(&self, dim: D) -> Result<Self> {
1087 let dim = dim.to_index(self.shape(), "var")?;
1088 self.var_keepdim(dim)?.squeeze(dim)
1089 }
1090
1091 pub fn max_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1094 self.reduce_impl(dim, true, ReduceOp::Max)
1095 }
1096
1097 pub fn max<D: Dim>(&self, dim: D) -> Result<Self> {
1099 self.reduce_impl(dim, false, ReduceOp::Max)
1100 }
1101
1102 pub fn min_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1105 self.reduce_impl(dim, true, ReduceOp::Min)
1106 }
1107
1108 pub fn min<D: Dim>(&self, dim: D) -> Result<Self> {
1110 self.reduce_impl(dim, false, ReduceOp::Min)
1111 }
1112
1113 pub fn argmax_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1114 self.reduce_impl(dim, true, ReduceOp::ArgMax)
1115 }
1116
1117 pub fn argmax<D: Dim>(&self, dim: D) -> Result<Self> {
1119 self.reduce_impl(dim, false, ReduceOp::ArgMax)
1120 }
1121
1122 pub fn argmin_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1123 self.reduce_impl(dim, true, ReduceOp::ArgMin)
1124 }
1125
1126 pub fn argmin<D: Dim>(&self, dim: D) -> Result<Self> {
1128 self.reduce_impl(dim, false, ReduceOp::ArgMin)
1129 }
1130
1131 pub fn cmp<T: TensorOrScalar>(&self, rhs: T, op: CmpOp) -> Result<Self> {
1136 let rhs = match rhs.to_tensor_scalar()? {
1137 crate::scalar::TensorScalar::Tensor(rhs) => rhs,
1138 crate::scalar::TensorScalar::Scalar(rhs) => rhs
1139 .to_dtype(self.dtype())?
1140 .to_device(self.device())?
1141 .broadcast_as(self.shape())?,
1142 };
1143 let shape = self.same_shape_binary_op(&rhs, "cmp")?;
1144 let storage = self
1145 .storage()
1146 .cmp(op, &rhs.storage(), self.layout(), rhs.layout())?;
1147 let op = BackpropOp::new1(self, |a| Op::Cmp(a, op));
1148 Ok(from_storage(storage, shape.dims(), op, false))
1149 }
1150
1151 pub fn eq<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1153 self.cmp(rhs, CmpOp::Eq)
1154 }
1155
1156 pub fn ne<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1158 self.cmp(rhs, CmpOp::Ne)
1159 }
1160
1161 pub fn lt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1164 self.cmp(rhs, CmpOp::Lt)
1165 }
1166
1167 pub fn gt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1170 self.cmp(rhs, CmpOp::Gt)
1171 }
1172
1173 pub fn ge<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1176 self.cmp(rhs, CmpOp::Ge)
1177 }
1178
1179 pub fn le<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1182 self.cmp(rhs, CmpOp::Le)
1183 }
1184
1185 pub fn clamp<T1: TensorOrScalar, T2: TensorOrScalar>(&self, min: T1, max: T2) -> Result<Self> {
1187 self.maximum(min)?.minimum(max)
1188 }
1189
1190 pub fn interpolate1d(&self, target_size: usize) -> Result<Self> {
1195 let (n, c, _l) = self.dims3()?;
1196 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest1D { arg, target_size });
1197 let storage = self
1198 .storage()
1199 .upsample_nearest1d(self.layout(), target_size)?;
1200 Ok(from_storage(storage, (n, c, target_size), op, false))
1201 }
1202
1203 pub fn upsample_nearest1d(&self, target_size: usize) -> Result<Self> {
1205 self.interpolate1d(target_size)
1206 }
1207
1208 pub fn interpolate2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1214 let (n, c, _h, _w) = self.dims4()?;
1215 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest2D {
1216 arg,
1217 target_h,
1218 target_w,
1219 });
1220 let storage = self
1221 .storage()
1222 .upsample_nearest2d(self.layout(), target_h, target_w)?;
1223 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1224 }
1225
1226 pub fn upsample_nearest2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1228 self.interpolate2d(target_h, target_w)
1229 }
1230
1231 pub fn upsample_bilinear2d(
1255 &self,
1256 target_h: usize,
1257 target_w: usize,
1258 align_corners: bool,
1259 ) -> Result<Self> {
1260 let (n, c, _h, _w) = self.dims4()?;
1261 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1262 arg,
1263 target_h,
1264 target_w,
1265 align_corners,
1266 });
1267 let storage = self.storage().upsample_bilinear2d(
1269 self.layout(),
1270 target_h,
1271 target_w,
1272 align_corners,
1273 None,
1274 None,
1275 )?;
1276 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1277 }
1278
1279 pub fn upsample_bilinear2d_with_scale(
1303 &self,
1304 scale_h: f64,
1305 scale_w: f64,
1306 align_corners: bool,
1307 ) -> Result<Self> {
1308 let (n, c, height_in, width_in) = self.dims4()?;
1309
1310 let height_out = (height_in as f64 * scale_h).floor() as usize;
1312 let width_out = (width_in as f64 * scale_w).floor() as usize;
1313
1314 if height_in == height_out && width_in == width_out {
1316 return Ok(self.clone());
1317 }
1318
1319 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1320 arg,
1321 target_h: height_out,
1322 target_w: width_out,
1323 align_corners,
1324 });
1325
1326 let storage = self.storage().upsample_bilinear2d(
1329 self.layout(),
1330 height_out,
1331 width_out,
1332 align_corners,
1333 Some(scale_h),
1334 Some(scale_w),
1335 )?;
1336 Ok(from_storage(
1337 storage,
1338 (n, c, height_out, width_out),
1339 op,
1340 false,
1341 ))
1342 }
1343
1344 pub fn avg_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1351 let sz = sz.to_usize2();
1352 self.avg_pool2d_with_stride(sz, sz)
1353 }
1354
1355 pub fn avg_pool2d_with_stride<T: crate::ToUsize2>(
1358 &self,
1359 kernel_size: T,
1360 stride: T,
1361 ) -> Result<Self> {
1362 let kernel_size = kernel_size.to_usize2();
1363 let stride = stride.to_usize2();
1364 let (n, c, h, w) = self.dims4()?;
1365 if h < kernel_size.0 || w < kernel_size.1 {
1366 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1367 }
1368 let h_out = (h - kernel_size.0) / stride.0 + 1;
1370 let w_out = (w - kernel_size.1) / stride.1 + 1;
1371 let op = BackpropOp::new1(self, |arg| Op::AvgPool2D {
1372 arg,
1373 kernel_size,
1374 stride,
1375 });
1376 let storage = self
1377 .storage()
1378 .avg_pool2d(self.layout(), kernel_size, stride)?;
1379 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1380 }
1381
1382 pub fn max_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1389 let sz = sz.to_usize2();
1390 self.max_pool2d_with_stride(sz, sz)
1391 }
1392
1393 pub fn max_pool2d_with_stride<T: crate::ToUsize2>(
1396 &self,
1397 kernel_size: T,
1398 stride: T,
1399 ) -> Result<Self> {
1400 let kernel_size = kernel_size.to_usize2();
1401 let stride = stride.to_usize2();
1402 let (n, c, h, w) = self.dims4()?;
1403 if h < kernel_size.0 || w < kernel_size.1 {
1404 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1405 }
1406 let h_out = (h - kernel_size.0) / stride.0 + 1;
1408 let w_out = (w - kernel_size.1) / stride.1 + 1;
1409 let op = BackpropOp::new1(self, |arg| Op::MaxPool2D {
1410 arg,
1411 kernel_size,
1412 stride,
1413 });
1414 let storage = self
1415 .storage()
1416 .max_pool2d(self.layout(), kernel_size, stride)?;
1417 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1418 }
1419
1420 pub fn dot(&self, rhs: &Self) -> Result<Self> {
1436 if self.dims().len() != 1 || rhs.dims().len() != 1 {
1437 return Err(Error::ShapeMismatchBinaryOp {
1438 lhs: self.shape().clone(),
1439 rhs: rhs.shape().clone(),
1440 op: "dot",
1441 });
1442 }
1443
1444 (self * rhs).and_then(|ret| ret.sum_all())
1445 }
1446
1447 pub fn norm(&self) -> Result<Self> {
1460 if self.dtype().is_int() {
1461 bail!("norm not supported for integer dtypes");
1462 }
1463
1464 self.sqr().and_then(|x| x.sum_all()).and_then(|x| x.sqrt())
1465 }
1466
1467 pub fn mv(&self, rhs: &Self) -> Result<Self> {
1482 let lhs_dims = self.dims();
1484 let rhs_dims = rhs.dims();
1485 if lhs_dims.len() != 2 || rhs_dims.len() != 1 || lhs_dims[1] != rhs_dims[0] {
1486 return Err(Error::ShapeMismatchBinaryOp {
1487 lhs: self.shape().clone(),
1488 rhs: rhs.shape().clone(),
1489 op: "mv",
1490 });
1491 }
1492
1493 self.matmul(&rhs.unsqueeze(1)?)?.squeeze(1)
1495 }
1496
1497 pub fn matmul(&self, rhs: &Self) -> Result<Self> {
1506 let a_dims = self.shape().dims();
1507 let b_dims = rhs.shape().dims();
1508
1509 let dim = a_dims.len();
1510
1511 if dim < 2 || b_dims.len() != dim {
1512 Err(Error::ShapeMismatchBinaryOp {
1513 lhs: self.shape().clone(),
1514 rhs: rhs.shape().clone(),
1515 op: "matmul",
1516 }
1517 .bt())?
1518 }
1519
1520 let m = a_dims[dim - 2];
1521 let k = a_dims[dim - 1];
1522 let k2 = b_dims[dim - 2];
1523 let n = b_dims[dim - 1];
1524
1525 let c_shape = Shape::from(&a_dims[..dim - 2]).extend(&[m, n]);
1526 let batching: usize = a_dims[..dim - 2].iter().product();
1527 let batching_b: usize = b_dims[..dim - 2].iter().product();
1528 if k != k2 || batching != batching_b {
1529 Err(Error::ShapeMismatchBinaryOp {
1530 lhs: self.shape().clone(),
1531 rhs: rhs.shape().clone(),
1532 op: "matmul",
1533 }
1534 .bt())?
1535 }
1536 if c_shape.elem_count() == 0 || k == 0 {
1537 {
1538 let lhs_storage = self.storage();
1539 let rhs_storage = rhs.storage();
1540 lhs_storage.same_device(&rhs_storage, "matmul")?;
1541 lhs_storage.same_dtype(&rhs_storage, "matmul")?;
1542 }
1543
1544 let storage = self.device().zeros(&c_shape, self.dtype())?;
1545 let op = BackpropOp::new2(self, rhs, Op::Matmul);
1546 return Ok(from_storage(storage, c_shape, op, false));
1547 }
1548
1549 let storage = self.storage().matmul(
1550 &rhs.storage(),
1551 (batching, m, n, k),
1552 self.layout(),
1553 rhs.layout(),
1554 )?;
1555 let op = BackpropOp::new2(self, rhs, Op::Matmul);
1556 Ok(from_storage(storage, c_shape, op, false))
1557 }
1558
1559 pub fn broadcast_matmul(&self, rhs: &Self) -> Result<Self> {
1565 let lhs = self;
1566 let (l_shape, r_shape) = lhs.shape().broadcast_shape_matmul(rhs.shape())?;
1567 let l_broadcast = l_shape != *lhs.shape();
1568 let r_broadcast = r_shape != *rhs.shape();
1569 match (l_broadcast, r_broadcast) {
1571 (true, true) => lhs
1572 .broadcast_as(&l_shape)?
1573 .contiguous()?
1574 .matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1575 (false, true) if rhs.rank() == 2 && lhs.is_contiguous() => {
1581 let (lhs_dims, rhs_dims) = (lhs.dims(), rhs.dims());
1582 let (m, k) = (lhs_dims[lhs.rank() - 2], lhs_dims[lhs.rank() - 1]);
1583 let n = rhs_dims[1];
1584 let batch: usize = lhs_dims[..lhs.rank() - 2].iter().product();
1585 let mut out_dims = lhs_dims.to_vec();
1586 out_dims.pop();
1587 out_dims.push(n);
1588 lhs.reshape((batch * m, k))?.matmul(rhs)?.reshape(out_dims)
1589 }
1590 (false, true) => lhs.matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1591 (true, false) => lhs.broadcast_as(&l_shape)?.contiguous()?.matmul(rhs),
1592 (false, false) => lhs.matmul(rhs),
1593 }
1594 }
1595
1596 pub fn where_cond(&self, on_true: &Self, on_false: &Self) -> Result<Self> {
1600 let _shap = self.same_shape_binary_op(on_true, "where_cond")?;
1601 let shape = self.same_shape_binary_op(on_false, "where_cond")?;
1602 let storage = self.storage().where_cond(
1603 self.layout(),
1604 &on_true.storage(),
1605 on_true.layout(),
1606 &on_false.storage(),
1607 on_false.layout(),
1608 )?;
1609 let op = BackpropOp::new3(self, on_true, on_false, Op::WhereCond);
1610 Ok(from_storage(storage, shape, op, false))
1611 }
1612
1613 pub fn embedding(&self, ids: &Self) -> Result<Self> {
1633 if self.rank() != 2 || ids.rank() != 1 {
1634 Err(Error::ShapeMismatchBinaryOp {
1635 lhs: self.shape().clone(),
1636 rhs: ids.shape().clone(),
1637 op: "embedding",
1638 }
1639 .bt())?
1640 }
1641 self.index_select(ids, 0)
1642 }
1643
1644 fn scatter_checks(&self, indexes: &Self, source: &Self, dim: usize) -> Result<()> {
1645 let source_dims = source.dims();
1646 let self_dims = self.dims();
1647 let mismatch = if source_dims.len() != self_dims.len() {
1648 true
1649 } else {
1650 let mut mismatch = false;
1651 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1652 if i != dim && d1 != d2 {
1653 mismatch = true;
1654 break;
1655 }
1656 }
1657 mismatch
1658 };
1659 if mismatch {
1660 Err(Error::ShapeMismatchBinaryOp {
1661 op: "scatter (self, src)",
1662 lhs: self.shape().clone(),
1663 rhs: source.shape().clone(),
1664 }
1665 .bt())?
1666 }
1667 if indexes.dims() != source.dims() {
1668 Err(Error::ShapeMismatchBinaryOp {
1669 op: "scatter (indexes, src)",
1670 lhs: indexes.shape().clone(),
1671 rhs: source.shape().clone(),
1672 }
1673 .bt())?
1674 }
1675 Ok(())
1676 }
1677
1678 pub fn scatter<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1679 let dim = dim.to_index(self.shape(), "scatter")?;
1680 self.scatter_checks(indexes, source, dim)?;
1681 let shape = self.shape();
1682 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1683 self.storage()
1684 .copy_strided_src(&mut storage, 0, self.layout())?;
1685 let layout = Layout::contiguous(shape);
1686 storage.scatter_set(
1687 &layout,
1688 &indexes.storage(),
1689 indexes.layout(),
1690 &source.storage(),
1691 source.layout(),
1692 dim,
1693 )?;
1694 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1695 Op::Scatter(t1, t2, t3, dim)
1696 });
1697 Ok(from_storage(storage, self.shape(), op, false))
1698 }
1699
1700 pub fn scatter_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1701 if self.same_storage(source) {
1702 crate::bail!("cannot use slice_set when self and src share their storage")
1703 }
1704 let dim = dim.to_index(self.shape(), "scatter-set")?;
1705 self.scatter_checks(indexes, source, dim)?;
1706 self.storage_mut().scatter_set(
1707 self.layout(),
1708 &indexes.storage(),
1709 indexes.layout(),
1710 &source.storage(),
1711 source.layout(),
1712 dim,
1713 )?;
1714 Ok(())
1715 }
1716
1717 pub fn scatter_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1718 let dim = dim.to_index(self.shape(), "scatter-add")?;
1719 self.scatter_checks(indexes, source, dim)?;
1720 let shape = self.shape();
1721 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1722 self.storage()
1723 .copy_strided_src(&mut storage, 0, self.layout())?;
1724 let layout = Layout::contiguous(shape);
1725 storage.scatter_add(
1726 &layout,
1727 &indexes.storage(),
1728 indexes.layout(),
1729 &source.storage(),
1730 source.layout(),
1731 dim,
1732 )?;
1733 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1734 Op::ScatterAdd(t1, t2, t3, dim)
1735 });
1736 Ok(from_storage(storage, self.shape(), op, false))
1737 }
1738
1739 pub fn scatter_add_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1740 if self.same_storage(source) {
1741 crate::bail!("cannot use slice_set when self and src share their storage")
1742 }
1743 let dim = dim.to_index(self.shape(), "scatter-add-set")?;
1744 self.scatter_checks(indexes, source, dim)?;
1745 self.storage_mut().scatter_add(
1746 self.layout(),
1747 &indexes.storage(),
1748 indexes.layout(),
1749 &source.storage(),
1750 source.layout(),
1751 dim,
1752 )?;
1753 Ok(())
1754 }
1755
1756 pub fn slice_scatter<D: Dim>(&self, src: &Self, dim: D, start: usize) -> Result<Self> {
1758 let dim = dim.to_index(self.shape(), "slice-scatter")?;
1759 if dim == 0 {
1760 self.slice_scatter0(src, start)
1761 } else {
1762 self.transpose(0, dim)?
1764 .slice_scatter0(&src.transpose(0, dim)?, start)?
1765 .transpose(0, dim)
1766 }
1767 }
1768
1769 pub fn slice_scatter0(&self, src: &Self, start: usize) -> Result<Self> {
1771 if self.dtype() != src.dtype() {
1772 Err(Error::DTypeMismatchBinaryOp {
1773 lhs: self.dtype(),
1774 rhs: src.dtype(),
1775 op: "slice-scatter",
1776 }
1777 .bt())?
1778 }
1779 if self.device().location() != src.device.location() {
1780 Err(Error::DeviceMismatchBinaryOp {
1781 lhs: self.device().location(),
1782 rhs: src.device().location(),
1783 op: "slice-scatter",
1784 }
1785 .bt())?
1786 }
1787 if self.rank() != src.rank() {
1788 Err(Error::UnexpectedNumberOfDims {
1789 expected: self.rank(),
1790 got: src.rank(),
1791 shape: src.shape().clone(),
1792 }
1793 .bt())?
1794 }
1795 let shape_ok =
1796 self.dims()
1797 .iter()
1798 .zip(src.dims().iter())
1799 .enumerate()
1800 .all(|(dim_idx, (&d1, &d2))| {
1801 if 0 == dim_idx {
1802 d2 + start <= d1
1803 } else {
1804 d1 == d2
1805 }
1806 });
1807 if !shape_ok {
1808 Err(Error::ShapeMismatchBinaryOp {
1809 op: "slice-scatter (self, src)",
1810 lhs: self.shape().clone(),
1811 rhs: src.shape().clone(),
1812 }
1813 .bt())?
1814 }
1815 let mut storage = unsafe { self.device().alloc_uninit(self.shape(), self.dtype())? };
1816 self.storage()
1817 .copy_strided_src(&mut storage, 0, self.layout())?;
1818 let offset = start * src.dims()[1..].iter().product::<usize>();
1819 src.storage()
1820 .copy_strided_src(&mut storage, offset, src.layout())?;
1821 let op = BackpropOp::new2(self, src, |t1, t2| Op::SliceScatter0(t1, t2, start));
1822 Ok(from_storage(storage, self.shape(), op, false))
1823 }
1824
1825 pub fn index_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1827 let dim = dim.to_index(self.shape(), "index-add")?;
1828 let source_dims = source.dims();
1829 let self_dims = self.dims();
1830 let mismatch = if source_dims.len() != self_dims.len() {
1831 true
1832 } else {
1833 let mut mismatch = false;
1834 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1835 if i != dim && d1 != d2 {
1836 mismatch = true;
1837 break;
1838 }
1839 }
1840 mismatch
1841 };
1842 if mismatch {
1843 Err(Error::ShapeMismatchBinaryOp {
1844 op: "index-add (self, source)",
1845 lhs: self.shape().clone(),
1846 rhs: source.shape().clone(),
1847 }
1848 .bt())?
1849 }
1850 let indexes_len = indexes.dims1()?;
1854 if source_dims[dim] != indexes_len {
1855 Err(Error::ShapeMismatchBinaryOp {
1856 op: "index-add (ids, source))",
1857 lhs: indexes.shape().clone(),
1858 rhs: source.shape().clone(),
1859 }
1860 .bt())?
1861 }
1862 let storage = self.storage().index_add(
1863 self.layout(),
1864 &indexes.storage(),
1865 indexes.layout(),
1866 &source.storage(),
1867 source.layout(),
1868 dim,
1869 )?;
1870 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1871 Op::IndexAdd(t1, t2, t3, dim)
1872 });
1873 Ok(from_storage(storage, self.shape(), op, false))
1874 }
1875
1876 pub fn gather<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1888 let dim = dim.to_index(self.shape(), "gather")?;
1889
1890 let self_dims = self.dims();
1891 let indexes_dims = indexes.dims();
1892 let mismatch = if indexes_dims.len() != self_dims.len() {
1893 true
1894 } else {
1895 let mut mismatch = false;
1896 for (i, (&d1, &d2)) in self_dims.iter().zip(indexes_dims.iter()).enumerate() {
1897 if i != dim && d1 < d2 {
1898 mismatch = true;
1899 break;
1900 }
1901 }
1902 mismatch
1903 };
1904 if mismatch {
1905 Err(Error::ShapeMismatchBinaryOp {
1906 op: "gather",
1907 lhs: self.shape().clone(),
1908 rhs: indexes.shape().clone(),
1909 }
1910 .bt())?
1911 }
1912 let storage =
1913 self.storage()
1914 .gather(self.layout(), &indexes.storage(), indexes.layout(), dim)?;
1915 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::Gather(t1, t2, dim));
1916 Ok(from_storage(storage, indexes.shape(), op, false))
1917 }
1918
1919 pub fn index_select<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1927 let dim = dim.to_index(self.shape(), "index-select")?;
1928 let indexes_len = match indexes.dims() {
1929 [l] => *l,
1930 _ => Err(Error::ShapeMismatchBinaryOp {
1931 lhs: self.shape().clone(),
1932 rhs: indexes.shape().clone(),
1933 op: "index-select",
1934 }
1935 .bt())?,
1936 };
1937 let storage = self.storage().index_select(
1938 &indexes.storage(),
1939 self.layout(),
1940 indexes.layout(),
1941 dim,
1942 )?;
1943 let mut dims = self.dims().to_vec();
1944 dims[dim] = indexes_len;
1945 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::IndexSelect(t1, t2, dim));
1946 Ok(from_storage(storage, dims, op, false))
1947 }
1948
1949 pub fn strided_index(&self) -> crate::StridedIndex<'_> {
1952 self.layout.strided_index()
1953 }
1954
1955 pub fn strided_blocks(&self) -> crate::StridedBlocks<'_> {
1960 self.layout.strided_blocks()
1961 }
1962
1963 pub fn to_vec1<S: crate::WithDType>(&self) -> Result<Vec<S>> {
1965 if self.rank() != 1 {
1966 Err(Error::UnexpectedNumberOfDims {
1967 expected: 1,
1968 got: self.rank(),
1969 shape: self.shape().clone(),
1970 }
1971 .bt())?
1972 }
1973 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1974 let data = S::cpu_storage_as_slice(cpu_storage)?;
1975 let data = match self.layout.contiguous_offsets() {
1976 Some((o1, o2)) => data[o1..o2].to_vec(),
1977 None => self.strided_index().map(|i| data[i]).collect(),
1978 };
1979 Ok::<Vec<_>, Error>(data)
1980 };
1981 match &*self.storage() {
1982 Storage::Cpu(storage) => from_cpu_storage(storage),
1983 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1984 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1985 #[cfg(feature = "rocm")]
1986 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1987 #[cfg(feature = "vulkan")]
1988 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1989 #[cfg(feature = "wgpu")]
1990 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1991 }
1992 }
1993
1994 pub fn to_vec2<S: crate::WithDType>(&self) -> Result<Vec<Vec<S>>> {
1996 let (dim1, dim2) = self.dims2()?;
1997 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1998 let data = S::cpu_storage_as_slice(cpu_storage)?;
1999 let mut rows = vec![];
2000 match self.layout.contiguous_offsets() {
2001 Some((o1, o2)) => {
2002 let data = &data[o1..o2];
2003 for idx_row in 0..dim1 {
2004 rows.push(data[idx_row * dim2..(idx_row + 1) * dim2].to_vec())
2005 }
2006 }
2007 None => {
2008 let mut src_index = self.strided_index();
2009 for _idx_row in 0..dim1 {
2010 let row = (0..dim2).map(|_| data[src_index.next().unwrap()]).collect();
2011 rows.push(row)
2012 }
2013 assert!(src_index.next().is_none());
2014 }
2015 }
2016 Ok(rows)
2017 };
2018 match &*self.storage() {
2019 Storage::Cpu(storage) => from_cpu_storage(storage),
2020 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2021 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2022 #[cfg(feature = "rocm")]
2023 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2024 #[cfg(feature = "vulkan")]
2025 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2026 #[cfg(feature = "wgpu")]
2027 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2028 }
2029 }
2030
2031 pub fn to_vec3<S: crate::WithDType>(&self) -> Result<Vec<Vec<Vec<S>>>> {
2033 let (dim1, dim2, dim3) = self.dims3()?;
2034 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
2035 let data = S::cpu_storage_as_slice(cpu_storage)?;
2036 let mut top_rows = vec![];
2037 match self.layout.contiguous_offsets() {
2038 Some((o1, o2)) => {
2039 let data = &data[o1..o2];
2040 let dim23 = dim2 * dim3;
2041 for idx1 in 0..dim1 {
2042 let data = &data[idx1 * dim23..(idx1 + 1) * dim23];
2043 let mut rows = vec![];
2044 for idx2 in 0..dim2 {
2045 rows.push(data[idx2 * dim3..(idx2 + 1) * dim3].to_vec())
2046 }
2047 top_rows.push(rows);
2048 }
2049 }
2050 None => {
2051 let mut src_index = self.strided_index();
2052 for _idx in 0..dim1 {
2053 let mut rows = vec![];
2054 for _jdx in 0..dim2 {
2055 let row = (0..dim3).map(|_| data[src_index.next().unwrap()]).collect();
2056 rows.push(row)
2057 }
2058 top_rows.push(rows);
2059 }
2060 assert!(src_index.next().is_none());
2061 }
2062 }
2063 Ok(top_rows)
2064 };
2065 match &*self.storage() {
2066 Storage::Cpu(storage) => from_cpu_storage(storage),
2067 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2068 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2069 #[cfg(feature = "rocm")]
2070 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2071 #[cfg(feature = "vulkan")]
2072 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2073 #[cfg(feature = "wgpu")]
2074 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2075 }
2076 }
2077
2078 pub fn dtype(&self) -> DType {
2080 self.dtype
2081 }
2082
2083 pub fn device(&self) -> &Device {
2085 &self.device
2086 }
2087
2088 pub fn shape(&self) -> &Shape {
2090 self.layout().shape()
2091 }
2092
2093 pub fn dims(&self) -> &[usize] {
2095 self.shape().dims()
2096 }
2097
2098 pub fn dim<D: Dim>(&self, dim: D) -> Result<usize> {
2100 let dim = dim.to_index(self.shape(), "dim")?;
2101 Ok(self.dims()[dim])
2102 }
2103
2104 pub fn layout(&self) -> &Layout {
2107 &self.layout
2108 }
2109
2110 pub fn stride(&self) -> &[usize] {
2111 self.layout.stride()
2112 }
2113
2114 pub fn rank(&self) -> usize {
2116 self.shape().rank()
2117 }
2118
2119 pub fn elem_count(&self) -> usize {
2121 self.shape().elem_count()
2122 }
2123
2124 pub fn id(&self) -> TensorId {
2126 self.id
2127 }
2128
2129 pub fn is_variable(&self) -> bool {
2132 self.is_variable
2133 }
2134
2135 pub(crate) fn op(&self) -> &Option<Op> {
2136 &self.op
2137 }
2138
2139 pub fn max_all(&self) -> Result<Tensor> {
2150 if self.rank() == 0 {
2151 Ok(self.clone())
2152 } else {
2153 self.flatten_all()?.max(0)
2154 }
2155 }
2156
2157 pub fn min_all(&self) -> Result<Tensor> {
2168 if self.rank() == 0 {
2169 Ok(self.clone())
2170 } else {
2171 self.flatten_all()?.min(0)
2172 }
2173 }
2174
2175 pub fn sum_all(&self) -> Result<Tensor> {
2186 let dims: Vec<_> = (0..self.rank()).collect();
2187 self.sum(dims)
2188 }
2189
2190 pub fn mean_all(&self) -> Result<Tensor> {
2191 self.sum_all()? / self.elem_count() as f64
2192 }
2193
2194 fn flatten_<D1: Dim, D2: Dim>(
2195 &self,
2196 start_dim: Option<D1>,
2197 end_dim: Option<D2>,
2198 ) -> Result<Tensor> {
2199 if self.rank() == 0 {
2200 self.reshape(1)
2201 } else {
2202 let start_dim = match start_dim {
2203 None => 0,
2204 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2205 };
2206 let end_dim = match end_dim {
2207 None => self.rank() - 1,
2208 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2209 };
2210 if start_dim < end_dim {
2211 let dims = self.dims();
2212 let mut dst_dims = dims[..start_dim].to_vec();
2213 dst_dims.push(dims[start_dim..end_dim + 1].iter().product::<usize>());
2214 if end_dim + 1 < dims.len() {
2215 dst_dims.extend(&dims[end_dim + 1..]);
2216 }
2217 self.reshape(dst_dims)
2218 } else {
2219 Ok(self.clone())
2220 }
2221 }
2222 }
2223
2224 pub fn flatten<D1: Dim, D2: Dim>(&self, start_dim: D1, end_dim: D2) -> Result<Tensor> {
2227 self.flatten_(Some(start_dim), Some(end_dim))
2228 }
2229
2230 pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Tensor> {
2232 self.flatten_(None::<usize>, Some(end_dim))
2233 }
2234
2235 pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Tensor> {
2238 self.flatten_(Some(start_dim), None::<usize>)
2239 }
2240
2241 pub fn flatten_all(&self) -> Result<Tensor> {
2251 self.flatten_(None::<usize>, None::<usize>)
2252 }
2253
2254 pub fn get(&self, i: usize) -> Result<Tensor> {
2266 let dims = self.dims();
2267 if dims.is_empty() {
2268 Ok(self.clone())
2269 } else {
2270 self.narrow(0, i, 1)?.reshape(&dims[1..])
2271 }
2272 }
2273
2274 pub fn get_on_dim<D: Dim>(&self, dim: D, index: usize) -> Result<Tensor> {
2288 let dim = dim.to_index(self.shape(), "get_on_dim")?;
2289 self.narrow(dim, index, 1)?.squeeze(dim)
2290 }
2291
2292 pub fn t(&self) -> Result<Tensor> {
2303 let rank = self.rank();
2304 if rank < 2 {
2305 Err(Error::UnexpectedNumberOfDims {
2306 expected: 2,
2307 got: rank,
2308 shape: self.shape().clone(),
2309 }
2310 .bt())?
2311 }
2312 self.transpose(rank - 2, rank - 1)
2313 }
2314
2315 pub fn transpose<D1: Dim, D2: Dim>(&self, dim1: D1, dim2: D2) -> Result<Tensor> {
2318 let dim1 = dim1.to_index(self.shape(), "transpose")?;
2319 let dim2 = dim2.to_index(self.shape(), "transpose")?;
2320 if dim1 == dim2 {
2321 return Ok(self.clone());
2322 }
2323 let op = BackpropOp::new1(self, |t| Op::Transpose(t, dim1, dim2));
2324 let tensor_ = Tensor_ {
2325 id: TensorId::new(),
2326 storage: self.storage.clone(),
2327 layout: self.layout.transpose(dim1, dim2)?,
2328 op,
2329 is_variable: false,
2330 dtype: self.dtype,
2331 device: self.device.clone(),
2332 };
2333 Ok(Tensor(Arc::new(tensor_)))
2334 }
2335
2336 pub fn permute<D: Dims>(&self, dims: D) -> Result<Tensor> {
2348 let dims = dims.to_indexes(self.shape(), "permute")?;
2349 let is_permutation =
2351 dims.len() == self.rank() && (0..dims.len()).all(|i| dims.contains(&i));
2352 if !is_permutation {
2353 bail!(
2354 "dimension mismatch in permute, tensor {:?}, dims: {:?}",
2355 self.dims(),
2356 dims
2357 )
2358 }
2359 let op = BackpropOp::new1(self, |t| Op::Permute(t, dims.clone()));
2360 let tensor_ = Tensor_ {
2361 id: TensorId::new(),
2362 storage: self.storage.clone(),
2363 layout: self.layout.permute(&dims)?,
2364 op,
2365 is_variable: false,
2366 dtype: self.dtype,
2367 device: self.device.clone(),
2368 };
2369 Ok(Tensor(Arc::new(tensor_)))
2370 }
2371
2372 pub fn is_contiguous(&self) -> bool {
2374 self.layout.is_contiguous()
2375 }
2376
2377 pub fn is_fortran_contiguous(&self) -> bool {
2379 self.layout.is_fortran_contiguous()
2380 }
2381
2382 pub fn copy(&self) -> Result<Tensor> {
2385 let op = BackpropOp::new1(self, Op::Copy);
2386 let tensor_ = Tensor_ {
2387 id: TensorId::new(),
2388 storage: Arc::new(RwLock::new(self.storage().try_clone(self.layout())?)),
2389 layout: self.layout.clone(),
2390 op,
2391 is_variable: false,
2392 dtype: self.dtype,
2393 device: self.device.clone(),
2394 };
2395 Ok(Tensor(Arc::new(tensor_)))
2396 }
2397
2398 pub fn detach(&self) -> Tensor {
2403 if self.op.is_none() && !self.is_variable {
2404 self.clone()
2405 } else {
2406 let tensor_ = Tensor_ {
2407 id: TensorId::new(),
2408 storage: self.storage.clone(),
2409 layout: self.layout.clone(),
2410 op: BackpropOp::none(),
2411 is_variable: false,
2412 dtype: self.dtype,
2413 device: self.device.clone(),
2414 };
2415 Tensor(Arc::new(tensor_))
2416 }
2417 }
2418
2419 pub fn to_device(&self, device: &Device) -> Result<Tensor> {
2421 if self.device().same_device(device) {
2422 Ok(self.clone())
2423 } else {
2424 let storage = match (&*self.storage(), device) {
2425 (Storage::Cpu(storage), Device::Cuda(cuda)) => {
2426 Storage::Cuda(cuda.storage_from_cpu_storage(storage)?)
2427 }
2428 (Storage::Cpu(storage), Device::Metal(metal)) => {
2429 Storage::Metal(metal.storage_from_cpu_storage(storage)?)
2430 }
2431 (Storage::Cuda(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2432 (Storage::Metal(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2433 #[cfg(feature = "rocm")]
2434 (Storage::Rocm(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2435 #[cfg(feature = "vulkan")]
2436 (Storage::Vulkan(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2437 #[cfg(feature = "wgpu")]
2438 (Storage::Wgpu(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2439 #[cfg(feature = "rocm")]
2440 (Storage::Cpu(storage), Device::Rocm(rocm)) => {
2441 Storage::Rocm(rocm.storage_from_cpu_storage(storage)?)
2442 }
2443 #[cfg(feature = "vulkan")]
2444 (Storage::Cpu(storage), Device::Vulkan(vulkan)) => {
2445 Storage::Vulkan(vulkan.storage_from_cpu_storage(storage)?)
2446 }
2447 #[cfg(feature = "wgpu")]
2448 (Storage::Cpu(storage), Device::Wgpu(wgpu)) => {
2449 Storage::Wgpu(wgpu.storage_from_cpu_storage(storage)?)
2450 }
2451 #[cfg(feature = "rocm")]
2452 (Storage::Rocm(storage), Device::Rocm(rocm)) => {
2453 let cpu_storage = storage.to_cpu_storage()?;
2454 Storage::Rocm(rocm.storage_from_cpu_storage(&cpu_storage)?)
2455 }
2456 #[cfg(feature = "vulkan")]
2457 (Storage::Vulkan(storage), Device::Vulkan(vulkan)) => {
2458 let cpu_storage = storage.to_cpu_storage()?;
2459 Storage::Vulkan(vulkan.storage_from_cpu_storage(&cpu_storage)?)
2460 }
2461 #[cfg(feature = "wgpu")]
2462 (Storage::Wgpu(storage), Device::Wgpu(wgpu)) => {
2463 let cpu_storage = storage.to_cpu_storage()?;
2464 Storage::Wgpu(wgpu.storage_from_cpu_storage(&cpu_storage)?)
2465 }
2466 (Storage::Cuda(storage), Device::Cuda(cuda)) => {
2467 let cpu_storage = storage.to_cpu_storage()?;
2470 Storage::Cuda(cuda.storage_from_cpu_storage(&cpu_storage)?)
2471 }
2472 (Storage::Cpu(storage), Device::Cpu) => Storage::Cpu(storage.clone()),
2473 _ => {
2474 bail!(
2475 "not implemented yet, self.device: {:?}, device: {:?}",
2476 self.device(),
2477 device
2478 )
2479 }
2480 };
2481 let op = BackpropOp::new1(self, Op::ToDevice);
2482 let tensor_ = Tensor_ {
2483 id: TensorId::new(),
2484 storage: Arc::new(RwLock::new(storage)),
2485 layout: self.layout.clone(),
2486 op,
2487 is_variable: false,
2488 dtype: self.dtype,
2489 device: device.clone(),
2490 };
2491 Ok(Tensor(Arc::new(tensor_)))
2492 }
2493 }
2494
2495 pub fn broadcast_left<S: Into<Shape>>(&self, left_shape: S) -> Result<Self> {
2498 let left_shape = left_shape.into();
2499 let mut dims = left_shape.into_dims();
2500 dims.extend(self.dims());
2501 self.broadcast_as(dims)
2502 }
2503
2504 pub fn broadcast_as<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2512 let tensor_ = Tensor_ {
2513 id: TensorId::new(),
2514 storage: self.storage.clone(),
2515 layout: self.layout.broadcast_as(shape)?,
2516 op: BackpropOp::new1(self, Op::Broadcast),
2517 is_variable: false,
2518 dtype: self.dtype,
2519 device: self.device.clone(),
2520 };
2521 Ok(Tensor(Arc::new(tensor_)))
2522 }
2523
2524 pub fn expand<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2526 self.broadcast_as(shape)
2527 }
2528
2529 pub fn to_dtype(&self, dtype: DType) -> Result<Self> {
2540 if self.dtype() == dtype {
2541 Ok(self.clone())
2542 } else {
2543 let shape = self.shape();
2544 let storage = self.storage().to_dtype(self.layout(), dtype)?;
2545 let op = BackpropOp::new1(self, Op::ToDType);
2546 Ok(from_storage(storage, shape.clone(), op, false))
2547 }
2548 }
2549
2550 pub fn contiguous(&self) -> Result<Tensor> {
2553 if self.is_contiguous() {
2554 Ok(self.clone())
2555 } else {
2556 let shape = self.shape();
2557 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2558 self.storage()
2559 .copy_strided_src(&mut storage, 0, self.layout())?;
2560 let op = BackpropOp::new1(self, Op::Copy);
2561 Ok(from_storage(storage, shape.clone(), op, false))
2562 }
2563 }
2564
2565 pub fn force_contiguous(&self) -> Result<Tensor> {
2567 let shape = self.shape();
2568 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2569 self.storage()
2570 .copy_strided_src(&mut storage, 0, self.layout())?;
2571 let op = BackpropOp::new1(self, Op::Copy);
2572 Ok(from_storage(storage, shape.clone(), op, false))
2573 }
2574
2575 pub(crate) fn make_var(&self) -> Result<Tensor> {
2578 let shape = self.shape().clone();
2579 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2580 self.storage()
2581 .copy_strided_src(&mut storage, 0, self.layout())?;
2582 Ok(from_storage(storage, shape, BackpropOp::none(), true))
2583 }
2584
2585 pub fn reshape<S: ShapeWithOneHole>(&self, s: S) -> Result<Tensor> {
2610 let shape = s.into_shape(self.elem_count())?;
2611 if shape.elem_count() != self.elem_count() {
2612 return Err(Error::ShapeMismatchBinaryOp {
2613 lhs: self.shape().clone(),
2614 rhs: shape,
2615 op: "reshape",
2616 }
2617 .bt());
2618 }
2619 let op = BackpropOp::new1(self, Op::Reshape);
2620 if self.is_contiguous() {
2621 let tensor_ = Tensor_ {
2622 id: TensorId::new(),
2623 storage: self.storage.clone(),
2624 layout: Layout::contiguous_with_offset(shape, self.layout.start_offset()),
2625 op,
2626 is_variable: false,
2627 dtype: self.dtype,
2628 device: self.device.clone(),
2629 };
2630 Ok(Tensor(Arc::new(tensor_)))
2631 } else {
2632 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2633 self.storage()
2634 .copy_strided_src(&mut storage, 0, self.layout())?;
2635 Ok(from_storage(storage, shape, op, false))
2636 }
2637 }
2638
2639 pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
2653 let dims = self.dims();
2656 let dim = dim.to_index(self.shape(), "squeeze")?;
2657 if dims[dim] == 1 {
2658 let mut dims = dims.to_vec();
2659 let mut strides = self.stride().to_vec();
2660 dims.remove(dim);
2661 strides.remove(dim);
2662 let tensor_ = Tensor_ {
2663 id: TensorId::new(),
2664 storage: self.storage.clone(),
2665 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2666 op: BackpropOp::new1(self, Op::Reshape),
2667 is_variable: false,
2668 dtype: self.dtype,
2669 device: self.device.clone(),
2670 };
2671 Ok(Tensor(Arc::new(tensor_)))
2672 } else {
2673 Ok(self.clone())
2674 }
2675 }
2676
2677 pub fn unsqueeze<D: Dim>(&self, dim: D) -> Result<Self> {
2691 let mut dims = self.dims().to_vec();
2692 let mut strides = self.stride().to_vec();
2693 let dim = dim.to_index_plus_one(self.shape(), "unsqueeze")?;
2694 dims.insert(dim, 1);
2696 let stride = if dim < strides.len() { strides[dim] } else { 1 };
2699 strides.insert(dim, stride);
2700 let tensor_ = Tensor_ {
2701 id: TensorId::new(),
2702 storage: self.storage.clone(),
2703 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2704 op: BackpropOp::new1(self, Op::Reshape),
2705 is_variable: false,
2706 dtype: self.dtype,
2707 device: self.device.clone(),
2708 };
2709 Ok(Tensor(Arc::new(tensor_)))
2710 }
2711
2712 pub fn stack<A: AsRef<Tensor>, D: Dim>(args: &[A], dim: D) -> Result<Self> {
2729 if args.is_empty() {
2730 Err(Error::OpRequiresAtLeastOneTensor { op: "stack" }.bt())?
2731 }
2732 let dim = dim.to_index_plus_one(args[0].as_ref().shape(), "stack")?;
2733 let args = args
2734 .iter()
2735 .map(|t| t.as_ref().unsqueeze(dim))
2736 .collect::<Result<Vec<_>>>()?;
2737 Self::cat(&args, dim)
2738 }
2739
2740 pub fn pad_with_zeros<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2743 if left == 0 && right == 0 {
2744 Ok(self.clone())
2745 } else if left == 0 {
2746 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2747 let mut dims = self.dims().to_vec();
2748 dims[dim] = right;
2749 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2750 Tensor::cat(&[self, &right], dim)
2751 } else if right == 0 {
2752 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2753 let mut dims = self.dims().to_vec();
2754 dims[dim] = left;
2755 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2756 Tensor::cat(&[&left, self], dim)
2757 } else {
2758 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2759 let mut dims = self.dims().to_vec();
2760 dims[dim] = left;
2761 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2762 dims[dim] = right;
2763 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2764 Tensor::cat(&[&left, self, &right], dim)
2765 }
2766 }
2767
2768 pub fn pad_with_same<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2771 if left == 0 && right == 0 {
2772 Ok(self.clone())
2773 } else if self.elem_count() == 0 {
2774 bail!("cannot use pad_with_same on an empty tensor")
2775 } else if left == 0 {
2776 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2777 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2778 let mut v = vec![self];
2779 for _ in 0..right {
2780 v.push(&r)
2781 }
2782 Tensor::cat(&v, dim)
2783 } else if right == 0 {
2784 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2785 let l = self.narrow(dim, 0, 1)?;
2786 let mut v = vec![];
2787 for _ in 0..left {
2788 v.push(&l)
2789 }
2790 v.push(self);
2791 Tensor::cat(&v, dim)
2792 } else {
2793 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2794 let l = self.narrow(dim, 0, 1)?;
2795 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2796 let mut v = vec![];
2797 for _ in 0..left {
2798 v.push(&l)
2799 }
2800 v.push(self);
2801 for _ in 0..right {
2802 v.push(&r)
2803 }
2804 Tensor::cat(&v, dim)
2805 }
2806 }
2807
2808 pub fn apply<M: crate::Module>(&self, m: &M) -> Result<Self> {
2810 m.forward(self)
2811 }
2812
2813 pub fn apply_t<M: crate::ModuleT>(&self, m: &M, train: bool) -> Result<Self> {
2815 m.forward_t(self, train)
2816 }
2817
2818 pub(crate) fn storage(&self) -> StorageRef<'_> {
2821 self.storage.read_recursive()
2822 }
2823
2824 pub(crate) fn storage_mut(&self) -> StorageMutRef<'_> {
2826 self.storage.write()
2827 }
2828
2829 pub(crate) fn storage_mut_and_layout(&self) -> (StorageMutRef<'_>, &Layout) {
2832 let storage = self.storage.write();
2833 (storage, &self.layout)
2834 }
2835
2836 pub fn storage_and_layout(&self) -> (StorageRef<'_>, &Layout) {
2838 let storage = self.storage.read();
2839 (storage, &self.layout)
2840 }
2841
2842 #[inline]
2844 pub(crate) fn storage_key(&self) -> usize {
2845 let lock: &RwLock<Storage> = self.storage.as_ref();
2846 std::ptr::from_ref(lock).addr()
2847 }
2848
2849 #[inline]
2851 pub(crate) fn same_storage(&self, rhs: &Self) -> bool {
2852 self.storage_key() == rhs.storage_key()
2853 }
2854
2855 pub fn normalize_axis(&self, axis: i64) -> Result<usize> {
2858 let rank = self.rank() as i64;
2859 if rank <= axis {
2860 bail!("axis {axis} is too large, tensor rank {rank}")
2861 } else if 0 <= axis {
2862 Ok(axis as usize)
2863 } else {
2864 let naxis = rank + axis;
2865 if naxis < 0 {
2866 bail!("axis {axis} is too small, tensor rank {rank}")
2867 }
2868 Ok(naxis as usize)
2869 }
2870 }
2871
2872 pub fn tril2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2874 let t = Tensor::arange(0u32, n as u32, device)?;
2875 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2876 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2877 t1.le(&t2)?.to_dtype(dtype)
2878 }
2879
2880 pub fn triu2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2882 let t = Tensor::arange(0u32, n as u32, device)?;
2883 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2884 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2885 t1.ge(&t2)?.to_dtype(dtype)
2886 }
2887
2888 pub fn eye(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2890 let t = Tensor::arange(0u32, n as u32, device)?;
2891 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2892 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2893 t1.eq(&t2)?.to_dtype(dtype)
2894 }
2895
2896 pub fn cumsum<D: Dim>(&self, dim: D) -> Result<Self> {
2901 let dim = dim.to_index(self.shape(), "cumsum")?;
2902 let rank = self.rank();
2903 if rank == 0 {
2904 return Ok(self.clone());
2905 }
2906 let n_axis = self.dim(dim)?;
2907 let triu = Tensor::triu2(n_axis, self.dtype(), self.device())?;
2908 if rank == 1 {
2909 self.unsqueeze(0)?.matmul(&triu)?.squeeze(0)
2910 } else {
2911 let last = rank - 1;
2912 let t = self.transpose(dim, last)?;
2913 let t = t.broadcast_matmul(&triu)?;
2914 t.transpose(dim, last)
2915 }
2916 }
2917
2918 pub fn slice_assign<D: std::ops::RangeBounds<usize>>(
2921 &self,
2922 ranges: &[D],
2923 src: &Tensor,
2924 ) -> Result<Self> {
2925 let src_dims = src.dims();
2926 let self_dims = self.dims();
2927 if self_dims.len() != src_dims.len() {
2928 bail!(
2929 "slice-assign requires input with the same rank {} <> {}",
2930 self_dims.len(),
2931 src_dims.len()
2932 )
2933 }
2934 if self_dims.len() != ranges.len() {
2935 bail!(
2936 "slice-assign requires input with the same rank as there are ranges {} <> {}",
2937 self_dims.len(),
2938 ranges.len()
2939 )
2940 }
2941 let mut src = src.clone();
2942 let mut mask = Self::ones(src.shape(), DType::U8, src.device())?;
2943 for (i, range) in ranges.iter().enumerate() {
2944 let start_included = match range.start_bound() {
2945 std::ops::Bound::Unbounded => 0,
2946 std::ops::Bound::Included(v) => *v,
2947 std::ops::Bound::Excluded(v) => *v + 1,
2948 };
2949 let end_excluded = match range.end_bound() {
2950 std::ops::Bound::Unbounded => self_dims[i],
2951 std::ops::Bound::Included(v) => *v + 1,
2952 std::ops::Bound::Excluded(v) => *v,
2953 };
2954 if end_excluded <= start_included {
2955 bail!("slice-assign: empty range for dim {i}, {start_included} {end_excluded}")
2956 }
2957 if self_dims[i] < end_excluded {
2958 bail!(
2959 "slice-assign: upper bound is out of range for dim {i}, {end_excluded} {}",
2960 self_dims[i]
2961 )
2962 }
2963 if end_excluded - start_included != src_dims[i] {
2964 bail!(
2965 "slice-assign: the range for dim {i} ({start_included}..{end_excluded}) does not match the size of src {}", src_dims[i]
2966 )
2967 }
2968 src = src.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?;
2969 mask = mask.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?
2970 }
2971 mask.where_cond(&src, self)
2972 }
2973
2974 pub fn log_sum_exp<D: Dims>(&self, sum_dims: D) -> Result<Self> {
2976 let sum_dims = sum_dims.to_indexes(self.shape(), "log-sum-exp")?;
2977 if sum_dims.is_empty() {
2978 return Ok(self.clone());
2979 }
2980 let max = sum_dims[1..]
2981 .iter()
2982 .try_fold(self.max_keepdim(sum_dims[0])?, |max, &dim| {
2983 max.max_keepdim(dim)
2984 })?;
2985 let exp = self.broadcast_sub(&max)?.exp()?;
2986 let sum = exp.sum(sum_dims.clone())?;
2987
2988 sum.log()? + max.squeeze_dims(&sum_dims)
2989 }
2990
2991 pub fn pow(&self, rhs: &Tensor) -> Result<Self> {
2993 rhs.mul(&self.log()?)?.exp()
2994 }
2995
2996 pub fn broadcast_pow(&self, rhs: &Tensor) -> Result<Self> {
2998 rhs.broadcast_mul(&self.log()?)?.exp()
2999 }
3000
3001 pub fn flip(&self, dims: &[usize]) -> Result<Tensor> {
3013 let mut result = self.clone();
3014 for &dim in dims.iter() {
3015 let size = result.dim(dim)?;
3016 let indices: Vec<i64> = (0..size).rev().map(|x| x as i64).collect();
3017 let indices_tensor = Tensor::from_vec(indices, (size,), result.device())?;
3018 result = result.index_select(&indices_tensor, dim)?;
3019 }
3020 Ok(result)
3021 }
3022
3023 pub fn unfold<D: Dim>(&self, dim: D, size: usize, step: usize) -> Result<Self> {
3026 let mut sizes = self.dims().to_vec();
3028 let mut strides = self.stride().to_vec();
3029
3030 let dim = dim.to_index(self.shape(), "unfold")?;
3031
3032 let max_len = if self.dims().is_empty() {
3033 1
3034 } else {
3035 sizes[dim]
3036 };
3037 if size > max_len {
3038 bail!(
3039 "unsqueeze: maximum size for tensor at dimension {dim} is {max_len} but size is {size}"
3040 )
3041 }
3042 sizes.push(size);
3043 strides.push(if self.dims().is_empty() {
3044 1
3045 } else {
3046 strides[dim]
3047 });
3048
3049 if !self.dims().is_empty() {
3050 sizes[dim] = ((sizes[dim] as f32 - size as f32) / step as f32 + 1.) as usize;
3051 strides[dim] *= step;
3052 }
3053
3054 let tensor_ = Tensor_ {
3055 id: TensorId::new(),
3056 storage: self.storage.clone(),
3057 layout: Layout::new(sizes.into(), strides, self.layout.start_offset()),
3058 op: BackpropOp::new1(self, Op::Reshape),
3059 is_variable: false,
3060 dtype: self.dtype,
3061 device: self.device.clone(),
3062 };
3063 Ok(Tensor(Arc::new(tensor_)))
3064 }
3065}
3066
3067macro_rules! bin_trait {
3068 ($trait:ident, $fn1:ident, $mul:expr, $add:expr) => {
3069 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for Tensor {
3070 type Output = Result<Tensor>;
3071
3072 fn $fn1(self, rhs: B) -> Self::Output {
3073 Tensor::$fn1(&self, rhs.borrow())
3074 }
3075 }
3076
3077 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for &Tensor {
3078 type Output = Result<Tensor>;
3079
3080 fn $fn1(self, rhs: B) -> Self::Output {
3081 Tensor::$fn1(&self, rhs.borrow())
3082 }
3083 }
3084
3085 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Tensor> for Result<B> {
3086 type Output = Result<Tensor>;
3087
3088 fn $fn1(self, rhs: Tensor) -> Self::Output {
3089 Tensor::$fn1(self?.borrow(), &rhs)
3090 }
3091 }
3092
3093 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<&Tensor> for Result<B> {
3094 type Output = Result<Tensor>;
3095
3096 fn $fn1(self, rhs: &Tensor) -> Self::Output {
3097 Tensor::$fn1(self?.borrow(), rhs)
3098 }
3099 }
3100
3101 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for Tensor {
3102 type Output = Result<Tensor>;
3103
3104 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3105 Tensor::$fn1(&self, rhs?.borrow())
3106 }
3107 }
3108
3109 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for &Tensor {
3110 type Output = Result<Tensor>;
3111
3112 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3113 Tensor::$fn1(&self, rhs?.borrow())
3114 }
3115 }
3116
3117 impl std::ops::$trait<f64> for Tensor {
3118 type Output = Result<Tensor>;
3119
3120 fn $fn1(self, rhs: f64) -> Self::Output {
3121 self.affine($mul(rhs), $add(rhs))
3122 }
3123 }
3124
3125 impl std::ops::$trait<f64> for &Tensor {
3126 type Output = Result<Tensor>;
3127
3128 fn $fn1(self, rhs: f64) -> Self::Output {
3129 self.affine($mul(rhs), $add(rhs))
3130 }
3131 }
3132 };
3133}
3134
3135bin_trait!(Add, add, |_| 1., |v| v);
3136bin_trait!(Sub, sub, |_| 1., |v: f64| -v);
3137bin_trait!(Mul, mul, |v| v, |_| 0.);
3138bin_trait!(Div, div, |v| 1. / v, |_| 0.);
3139
3140impl std::ops::Add<Tensor> for f64 {
3141 type Output = Result<Tensor>;
3142
3143 fn add(self, rhs: Tensor) -> Self::Output {
3144 rhs + self
3145 }
3146}
3147
3148impl std::ops::Add<&Tensor> for f64 {
3149 type Output = Result<Tensor>;
3150
3151 fn add(self, rhs: &Tensor) -> Self::Output {
3152 rhs + self
3153 }
3154}
3155
3156impl std::ops::Mul<Tensor> for f64 {
3157 type Output = Result<Tensor>;
3158
3159 fn mul(self, rhs: Tensor) -> Self::Output {
3160 rhs * self
3161 }
3162}
3163
3164impl std::ops::Mul<&Tensor> for f64 {
3165 type Output = Result<Tensor>;
3166
3167 fn mul(self, rhs: &Tensor) -> Self::Output {
3168 rhs * self
3169 }
3170}
3171
3172impl std::ops::Sub<Tensor> for f64 {
3173 type Output = Result<Tensor>;
3174
3175 fn sub(self, rhs: Tensor) -> Self::Output {
3176 rhs.affine(-1., self)
3177 }
3178}
3179
3180impl std::ops::Sub<&Tensor> for f64 {
3181 type Output = Result<Tensor>;
3182
3183 fn sub(self, rhs: &Tensor) -> Self::Output {
3184 rhs.affine(-1., self)
3185 }
3186}
3187
3188impl std::ops::Div<Tensor> for f64 {
3189 type Output = Result<Tensor>;
3190
3191 #[allow(clippy::suspicious_arithmetic_impl)]
3192 fn div(self, rhs: Tensor) -> Self::Output {
3193 rhs.recip()? * self
3194 }
3195}
3196
3197impl std::ops::Div<&Tensor> for f64 {
3198 type Output = Result<Tensor>;
3199
3200 #[allow(clippy::suspicious_arithmetic_impl)]
3201 fn div(self, rhs: &Tensor) -> Self::Output {
3202 rhs.recip()? * self
3203 }
3204}
3205
3206impl<S: Into<Shape>> From<(Storage, S)> for Tensor {
3207 fn from((storage, shape): (Storage, S)) -> Self {
3208 from_storage(storage, shape, BackpropOp::none(), false)
3209 }
3210}