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::{bail, storage::Storage, DType, Device, Error, Layout, Result, Shape};
8use std::sync::{Arc, RwLock};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub struct TensorId(usize);
13
14impl TensorId {
15 fn new() -> Self {
16 use std::sync::atomic;
18 static COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(1);
19 Self(COUNTER.fetch_add(1, atomic::Ordering::Relaxed))
20 }
21}
22
23pub struct Tensor_ {
24 id: TensorId,
25 storage: Arc<RwLock<Storage>>,
38 layout: Layout,
39 op: BackpropOp,
40 is_variable: bool,
41 dtype: DType,
42 device: Device,
43}
44
45impl AsRef<Tensor> for Tensor {
46 fn as_ref(&self) -> &Tensor {
47 self
48 }
49}
50
51#[derive(Clone)]
55pub struct Tensor(Arc<Tensor_>);
69
70impl std::ops::Deref for Tensor {
71 type Target = Tensor_;
72
73 fn deref(&self) -> &Self::Target {
74 self.0.as_ref()
75 }
76}
77
78macro_rules! unary_op {
79 ($fn_name:ident, $op_name:ident) => {
80 pub fn $fn_name(&self) -> Result<Self> {
81 let shape = self.shape();
82 if shape.elem_count() == 0 {
83 return Ok(self.clone());
84 }
85 let storage = self
86 .storage()
87 .unary_impl::<crate::op::$op_name>(self.layout())?;
88 let op = BackpropOp::new1(self, |s| Op::Unary(s, UnaryOp::$op_name));
89 Ok(from_storage(storage, shape.clone(), op, false))
90 }
91 };
92}
93
94macro_rules! binary_op {
95 ($fn_name:ident, $op_name:ident) => {
96 pub fn $fn_name(&self, rhs: &Self) -> Result<Self> {
97 let shape = self.same_shape_binary_op(rhs, stringify!($fn_name))?;
98 if shape.elem_count() == 0 {
99 return Ok(self.clone());
100 }
101 let storage = self.storage().binary_impl::<crate::op::$op_name>(
102 &*rhs.storage(),
103 self.layout(),
104 rhs.layout(),
105 )?;
106 let op = BackpropOp::new2(self, rhs, |t1, t2| Op::Binary(t1, t2, BinaryOp::$op_name));
107 Ok(from_storage(storage, shape.clone(), op, false))
108 }
109 };
110}
111
112macro_rules! binary_op_scalar {
113 ($fn_name:ident, $op_name:ident) => {
114 pub fn $fn_name<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
115 let rhs = match rhs.to_tensor_scalar()? {
116 crate::scalar::TensorScalar::Tensor(rhs) => rhs,
117 crate::scalar::TensorScalar::Scalar(rhs) => rhs
118 .to_dtype(self.dtype())?
119 .to_device(self.device())?
120 .broadcast_as(self.shape())?,
121 };
122 let shape = self.same_shape_binary_op(&rhs, stringify!($fn_name))?;
123 if self.elem_count() == 0 {
124 return Ok(self.clone());
125 }
126 let storage = self.storage().binary_impl::<crate::op::$op_name>(
127 &*rhs.storage(),
128 self.layout(),
129 rhs.layout(),
130 )?;
131 let op = BackpropOp::new2(self, &rhs, |t1, t2| Op::Binary(t1, t2, BinaryOp::$op_name));
132 Ok(from_storage(storage, shape.clone(), op, false))
133 }
134 };
135}
136
137macro_rules! broadcast_binary_op {
138 ($fn_name:ident, $inner_fn_name:ident) => {
139 pub fn $fn_name(&self, rhs: &Self) -> Result<Self> {
140 let lhs = self;
141 let shape = lhs
142 .shape()
143 .broadcast_shape_binary_op(rhs.shape(), stringify!($fn_name))?;
144 let l_broadcast = shape != *lhs.shape();
145 let r_broadcast = shape != *rhs.shape();
146 match (l_broadcast, r_broadcast) {
147 (true, true) => lhs
148 .broadcast_as(&shape)?
149 .$inner_fn_name(&rhs.broadcast_as(&shape)?),
150 (false, true) => lhs.$inner_fn_name(&rhs.broadcast_as(&shape)?),
151 (true, false) => lhs.broadcast_as(&shape)?.$inner_fn_name(rhs),
152 (false, false) => lhs.$inner_fn_name(rhs),
153 }
154 }
155 };
156}
157
158pub(crate) fn from_storage<S: Into<Shape>>(
160 storage: Storage,
161 shape: S,
162 op: BackpropOp,
163 is_variable: bool,
164) -> Tensor {
165 let dtype = storage.dtype();
166 let device = storage.device();
167 let tensor_ = Tensor_ {
168 id: TensorId::new(),
169 storage: Arc::new(RwLock::new(storage)),
170 layout: Layout::contiguous(shape),
171 op,
172 is_variable,
173 dtype,
174 device,
175 };
176 Tensor(Arc::new(tensor_))
177}
178
179impl Tensor {
180 pub(crate) fn ones_impl<S: Into<Shape>>(
181 shape: S,
182 dtype: DType,
183 device: &Device,
184 is_variable: bool,
185 ) -> Result<Self> {
186 let none = BackpropOp::none();
187 let shape = shape.into();
188 let mut storage = unsafe { device.alloc_uninit(&shape, dtype)? };
189 let layout = Layout::contiguous(shape.clone());
190 storage.const_set(crate::scalar::Scalar::one(dtype), &layout)?;
191 Ok(from_storage(storage, shape, none, is_variable))
192 }
193
194 pub fn ones<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
204 Self::ones_impl(shape, dtype, device, false)
205 }
206
207 pub fn const_set(&self, value: crate::scalar::Scalar) -> Result<()> {
208 self.storage_mut().const_set(value, self.layout())
209 }
210
211 pub fn zero_set(&self) -> Result<()> {
212 self.const_set(crate::scalar::Scalar::zero(self.dtype()))
213 }
214
215 pub fn one_set(&self) -> Result<()> {
216 self.const_set(crate::scalar::Scalar::one(self.dtype()))
217 }
218
219 pub fn ones_like(&self) -> Result<Self> {
229 Tensor::ones(self.shape(), self.dtype(), self.device())
230 }
231
232 pub(crate) fn zeros_impl<S: Into<Shape>>(
235 shape: S,
236 dtype: DType,
237 device: &Device,
238 is_variable: bool,
239 ) -> Result<Self> {
240 let none = BackpropOp::none();
241 let shape = shape.into();
242 let storage = device.zeros(&shape, dtype)?;
243 Ok(from_storage(storage, shape, none, is_variable))
244 }
245
246 pub fn zeros<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
256 Self::zeros_impl(shape, dtype, device, false)
257 }
258
259 pub fn zeros_like(&self) -> Result<Self> {
270 Tensor::zeros(self.shape(), self.dtype(), self.device())
271 }
272
273 pub(crate) unsafe fn empty_impl<S: Into<Shape>>(
276 shape: S,
277 dtype: DType,
278 device: &Device,
279 is_variable: bool,
280 ) -> Result<Self> {
281 let none = BackpropOp::none();
282 let shape = shape.into();
283 let storage = device.alloc_uninit(&shape, dtype)?;
284 Ok(from_storage(storage, shape, none, is_variable))
285 }
286
287 pub unsafe fn empty<S: Into<Shape>>(shape: S, dtype: DType, device: &Device) -> Result<Self> {
299 Self::empty_impl(shape, dtype, device, false)
300 }
301
302 pub unsafe fn empty_like(&self) -> Result<Self> {
315 Tensor::empty(self.shape(), self.dtype(), self.device())
316 }
317
318 pub(crate) fn rand_impl<S: Into<Shape>, T: crate::FloatDType>(
319 lo: T,
320 up: T,
321 s: S,
322 device: &Device,
323 is_variable: bool,
324 ) -> Result<Self> {
325 let s = s.into();
326 let storage = device.rand_uniform(lo, up, &s)?;
327 let none = BackpropOp::none();
328 Ok(from_storage(storage, s, none, is_variable))
329 }
330
331 pub(crate) fn rand_f64_impl<S: Into<Shape>>(
332 lo: f64,
333 up: f64,
334 s: S,
335 dtype: DType,
336 device: &Device,
337 is_variable: bool,
338 ) -> Result<Self> {
339 let s = s.into();
340 let storage = device.rand_uniform_f64(lo, up, &s, dtype)?;
341 let none = BackpropOp::none();
342 Ok(from_storage(storage, s, none, is_variable))
343 }
344
345 pub fn rand<S: Into<Shape>, T: crate::FloatDType>(
347 lo: T,
348 up: T,
349 s: S,
350 device: &Device,
351 ) -> Result<Self> {
352 Self::rand_impl(lo, up, s, device, false)
353 }
354
355 pub fn rand_like(&self, lo: f64, up: f64) -> Result<Self> {
356 Tensor::rand_f64_impl(lo, up, self.shape(), self.dtype(), self.device(), false)
357 }
358
359 pub(crate) fn randn_impl<S: Into<Shape>, T: crate::FloatDType>(
360 mean: T,
361 std: T,
362 s: S,
363 device: &Device,
364 is_variable: bool,
365 ) -> Result<Self> {
366 let s = s.into();
367 let storage = device.rand_normal(mean, std, &s)?;
368 let none = BackpropOp::none();
369 Ok(from_storage(storage, s, none, is_variable))
370 }
371
372 pub(crate) fn randn_f64_impl<S: Into<Shape>>(
373 mean: f64,
374 std: f64,
375 s: S,
376 dtype: DType,
377 device: &Device,
378 is_variable: bool,
379 ) -> Result<Self> {
380 let s = s.into();
381 let storage = device.rand_normal_f64(mean, std, &s, dtype)?;
382 let none = BackpropOp::none();
383 Ok(from_storage(storage, s, none, is_variable))
384 }
385
386 pub fn randn_like(&self, mean: f64, stdev: f64) -> Result<Self> {
387 Tensor::randn_f64_impl(
388 mean,
389 stdev,
390 self.shape(),
391 self.dtype(),
392 self.device(),
393 false,
394 )
395 }
396
397 pub fn randn<S: Into<Shape>, T: crate::FloatDType>(
400 mean: T,
401 std: T,
402 s: S,
403 device: &Device,
404 ) -> Result<Self> {
405 Self::randn_impl(mean, std, s, device, false)
406 }
407
408 pub(crate) fn new_impl<A: crate::device::NdArray>(
409 array: A,
410 shape: Shape,
411 device: &Device,
412 is_variable: bool,
413 ) -> Result<Self> {
414 let n: usize = shape.elem_count();
415 let buffer_size: usize = array.shape()?.elem_count();
416 if buffer_size != n {
417 return Err(Error::ShapeMismatch { buffer_size, shape }.bt());
418 }
419 let storage = device.storage(array)?;
420 let none = BackpropOp::none();
421 Ok(from_storage(storage, shape, none, is_variable))
422 }
423
424 pub fn new<A: crate::device::NdArray>(array: A, device: &Device) -> Result<Self> {
426 let shape = array.shape()?;
427 Self::new_impl(array, shape, device, false)
428 }
429
430 pub fn full<D: crate::WithDType, S: Into<Shape>>(
441 value: D,
442 shape: S,
443 device: &Device,
444 ) -> Result<Self> {
445 let none = BackpropOp::none();
446 let shape = shape.into();
447 let mut storage = unsafe { device.alloc_uninit(&shape, D::DTYPE)? };
448 let layout = Layout::contiguous(shape.clone());
449 storage.const_set(value.to_scalar(), &layout)?;
450 Ok(from_storage(storage, shape, none, false))
451 }
452
453 pub fn from_iter<D: crate::WithDType>(
462 iter: impl IntoIterator<Item = D>,
463 device: &Device,
464 ) -> Result<Self> {
465 let data = iter.into_iter().collect::<Vec<_>>();
466 let len = data.len();
467 Self::from_vec_impl(data, len, device, false)
468 }
469
470 pub fn arange<D: crate::WithDType>(start: D, end: D, device: &Device) -> Result<Self> {
480 Self::arange_step(start, end, D::one(), device)
481 }
482
483 pub fn arange_step<D: crate::WithDType>(
493 start: D,
494 end: D,
495 step: D,
496 device: &Device,
497 ) -> Result<Self> {
498 if D::is_zero(&step) {
499 bail!("step cannot be zero")
500 }
501 let mut data = vec![];
502 let mut current = start;
503 if step >= D::zero() {
504 while current < end {
505 data.push(current);
506 current += step;
507 }
508 } else {
509 while current > end {
510 data.push(current);
511 current += step;
512 }
513 }
514 let len = data.len();
515 Self::from_vec_impl(data, len, device, false)
516 }
517
518 pub(crate) fn from_vec_impl<S: ShapeWithOneHole, D: crate::WithDType>(
519 data: Vec<D>,
520 shape: S,
521 device: &Device,
522 is_variable: bool,
523 ) -> Result<Self> {
524 let shape = shape.into_shape(data.len())?;
525 let storage = device.storage_owned(data)?;
526 let none = BackpropOp::none();
527 Ok(from_storage(storage, shape, none, is_variable))
528 }
529
530 pub fn from_vec<S: ShapeWithOneHole, D: crate::WithDType>(
544 data: Vec<D>,
545 shape: S,
546 device: &Device,
547 ) -> Result<Self> {
548 Self::from_vec_impl(data, shape, device, false)
549 }
550
551 pub fn from_slice<S: ShapeWithOneHole, D: crate::WithDType>(
565 array: &[D],
566 shape: S,
567 device: &Device,
568 ) -> Result<Self> {
569 let shape = shape.into_shape(array.len())?;
570 let storage = device.storage_from_slice(array)?;
571 let none = BackpropOp::none();
572 Ok(from_storage(storage, shape, none, false))
573 }
574
575 pub(crate) fn same_shape_binary_op(&self, rhs: &Self, op: &'static str) -> Result<&Shape> {
576 let lhs = self.shape();
577 let rhs = rhs.shape();
578 if lhs != rhs {
579 Err(Error::ShapeMismatchBinaryOp {
580 lhs: lhs.clone(),
581 rhs: rhs.clone(),
582 op,
583 }
584 .bt())
585 } else {
586 Ok(lhs)
587 }
588 }
589
590 pub fn track_op(&self) -> bool {
593 self.is_variable || self.op.is_some()
594 }
595
596 pub fn from_storage<S: Into<Shape>>(
602 storage: Storage,
603 shape: S,
604 op: BackpropOp,
605 is_variable: bool,
606 ) -> Tensor {
607 from_storage(storage, shape, op, is_variable)
608 }
609
610 binary_op!(add, Add);
613 binary_op!(mul, Mul);
614 binary_op!(sub, Sub);
615 binary_op!(div, Div);
616 binary_op_scalar!(maximum, Maximum);
617 binary_op_scalar!(minimum, Minimum);
618 broadcast_binary_op!(broadcast_add, add);
619 broadcast_binary_op!(broadcast_mul, mul);
620 broadcast_binary_op!(broadcast_sub, sub);
621 broadcast_binary_op!(broadcast_div, div);
622 broadcast_binary_op!(broadcast_maximum, maximum);
623 broadcast_binary_op!(broadcast_minimum, minimum);
624 broadcast_binary_op!(broadcast_eq, eq);
625 broadcast_binary_op!(broadcast_ne, ne);
626 broadcast_binary_op!(broadcast_lt, lt);
627 broadcast_binary_op!(broadcast_le, le);
628 broadcast_binary_op!(broadcast_gt, gt);
629 broadcast_binary_op!(broadcast_ge, ge);
630
631 unary_op!(recip, Recip);
632 unary_op!(neg, Neg);
633 unary_op!(exp, Exp);
634 unary_op!(log, Log);
635 unary_op!(sin, Sin);
636 unary_op!(cos, Cos);
637 unary_op!(tanh, Tanh);
638 unary_op!(abs, Abs);
639 unary_op!(sqr, Sqr);
640 unary_op!(sqrt, Sqrt);
641 unary_op!(gelu, Gelu);
642 unary_op!(gelu_erf, GeluErf);
643 unary_op!(erf, Erf);
644 unary_op!(relu, Relu);
645 unary_op!(silu, Silu);
646 unary_op!(ceil, Ceil);
647 unary_op!(floor, Floor);
648 unary_op!(round, Round);
649 unary_op!(sign, Sign);
650
651 pub fn round_to(&self, decimals: i32) -> Result<Self> {
656 let mult = 10f64.powi(decimals);
657 (self * mult)?.round()? * (1f64 / mult)
658 }
659
660 pub fn to_scalar<S: crate::WithDType>(&self) -> Result<S> {
663 if self.rank() != 0 {
664 Err(Error::UnexpectedNumberOfDims {
665 expected: 0,
666 got: self.rank(),
667 shape: self.shape().clone(),
668 }
669 .bt())?
670 }
671 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
672 let data = S::cpu_storage_as_slice(cpu_storage)?;
673 Ok::<_, Error>(data[self.layout().start_offset()])
674 };
675 match &*self.storage() {
676 Storage::Cpu(cpu_storage) => from_cpu_storage(cpu_storage),
677 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
678 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
679 #[cfg(feature = "rocm")]
680 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
681 #[cfg(feature = "vulkan")]
682 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
683 #[cfg(feature = "wgpu")]
684 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
685 }
686 }
687
688 pub fn to_vec0<S: crate::WithDType>(&self) -> Result<S> {
690 self.to_scalar::<S>()
691 }
692
693 pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Tensor> {
695 let repeats = shape.into();
697 let repeats = repeats.dims();
698 let mut inp = if self.rank() < repeats.len() {
699 let shape = [vec![1; repeats.len() - self.rank()], self.dims().to_vec()].concat();
700 self.reshape(shape)?
701 } else {
702 self.clone()
703 };
704 for (idx, &repeat) in repeats.iter().enumerate() {
705 if repeat > 1 {
706 inp = Tensor::cat(&vec![&inp; repeat], idx)?
707 }
708 }
709 Ok(inp)
710 }
711
712 pub fn meshgrid<A: AsRef<Tensor>>(args: &[A], xy_indexing: bool) -> Result<Vec<Self>> {
749 if args.len() <= 1 {
750 Err(Error::OpRequiresAtLeastTwoTensors { op: "meshgrid" }.bt())?
751 }
752 let args: Vec<_> = if xy_indexing {
753 args.iter().rev().collect()
754 } else {
755 args.iter().collect()
756 };
757
758 let mut shape = Vec::with_capacity(args.len());
759 for arg in args.iter() {
760 shape.push(arg.as_ref().dims1()?)
761 }
762
763 let mut grids = Vec::with_capacity(args.len());
764 for idx in 0..args.len() {
765 let mut ones = vec![1usize; args.len()];
766 ones[idx] = shape[idx];
767 let arg = args[idx].as_ref().reshape(ones)?;
768 let mut repeats = shape.clone();
769 repeats[idx] = 1;
770 let repeated_tensor = arg.repeat(repeats)?;
771 grids.push(repeated_tensor);
772 }
773 if xy_indexing {
774 grids.reverse();
775 }
776 Ok(grids)
777 }
778
779 pub fn affine(&self, mul: f64, add: f64) -> Result<Self> {
791 if self.elem_count() == 0 {
792 return Ok(self.clone());
793 }
794 let storage = self.storage().affine(self.layout(), mul, add)?;
795 let op = BackpropOp::new1(self, |arg| Op::Affine { arg, mul, add });
796 Ok(from_storage(storage, self.shape(), op, false))
797 }
798
799 pub fn elu(&self, alpha: f64) -> Result<Self> {
801 if self.elem_count() == 0 {
802 return Ok(self.clone());
803 }
804 let storage = self.storage().elu(self.layout(), alpha)?;
805 let op = BackpropOp::new1(self, |t| Op::Elu(t, alpha));
806 Ok(from_storage(storage, self.shape(), op, false))
807 }
808
809 pub fn powf(&self, e: f64) -> Result<Self> {
811 if self.elem_count() == 0 {
812 return Ok(self.clone());
813 }
814 let storage = self.storage().powf(self.layout(), e)?;
815 let op = BackpropOp::new1(self, |t| Op::Powf(t, e));
816 Ok(from_storage(storage, self.shape(), op, false))
817 }
818
819 pub(crate) fn check_dim(&self, dim: usize, op: &'static str) -> Result<()> {
820 if dim >= self.dims().len() {
821 Err(Error::DimOutOfRange {
822 shape: self.shape().clone(),
823 dim: dim as i32,
824 op,
825 }
826 .bt())?
827 } else {
828 Ok(())
829 }
830 }
831
832 pub fn chunk<D: Dim>(&self, chunks: usize, dim: D) -> Result<Vec<Self>> {
835 let dim = dim.to_index(self.shape(), "chunk")?;
836 let size = self.dim(dim)?;
837 if size < chunks {
838 (0..size).map(|i| self.narrow(dim, i, 1)).collect()
839 } else {
840 let chunk_size = size / chunks;
841 let cnt_additional = size % chunks;
842 let mut tensors = vec![];
843 let mut sum_chunk_size = 0;
844 for i in 0..chunks {
845 let chunk_size = if i < cnt_additional {
846 chunk_size + 1
847 } else {
848 chunk_size
849 };
850 let tensor = self.narrow(dim, sum_chunk_size, chunk_size)?;
851 tensors.push(tensor);
852 sum_chunk_size += chunk_size
853 }
854 Ok(tensors)
855 }
856 }
857
858 pub fn narrow<D: Dim>(&self, dim: D, start: usize, len: usize) -> Result<Self> {
885 let dims = self.dims();
886 let dim = dim.to_index(self.shape(), "narrow")?;
887 let err = |msg| {
888 Err::<(), _>(
889 Error::NarrowInvalidArgs {
890 shape: self.shape().clone(),
891 dim,
892 start,
893 len,
894 msg,
895 }
896 .bt(),
897 )
898 };
899 if start > dims[dim] {
900 err("start > dim_len")?
901 }
902 if start.saturating_add(len) > dims[dim] {
903 err("start + len > dim_len")?
904 }
905 if start == 0 && dims[dim] == len {
906 Ok(self.clone())
907 } else {
908 let op = BackpropOp::new1(self, |t| Op::Narrow(t, dim, start, len));
909 let layout = self.layout().narrow(dim, start, len)?;
910 let tensor_ = Tensor_ {
911 id: TensorId::new(),
912 storage: self.storage.clone(),
913 layout,
914 op,
915 is_variable: false,
916 dtype: self.dtype,
917 device: self.device.clone(),
918 };
919 Ok(Tensor(Arc::new(tensor_)))
920 }
921 }
922
923 fn squeeze_dims(self, dims: &[usize]) -> Result<Self> {
924 match dims {
925 [] => Ok(self),
926 [i] => self.squeeze(*i),
927 dims => {
928 let dims = self
929 .dims()
930 .iter()
931 .enumerate()
932 .filter_map(|(dim_idx, &v)| {
933 if dims.contains(&dim_idx) {
934 None
935 } else {
936 Some(v)
937 }
938 })
939 .collect::<Vec<_>>();
940 self.reshape(dims)
941 }
942 }
943 }
944
945 fn reduce_impl<D: Dim>(&self, dim: D, keepdim: bool, op: ReduceOp) -> Result<Self> {
946 let dim = dim.to_index(self.shape(), op.name())?;
947 let storage = self.storage().reduce_op(op, self.layout(), &[dim])?;
948 let mut dims = self.dims().to_vec();
949 dims[dim] = 1;
950 let op = match op {
951 ReduceOp::Sum | ReduceOp::Min | ReduceOp::Max => {
952 BackpropOp::new1(self, |arg| Op::Reduce(arg, op, dims.to_vec()))
953 }
954 ReduceOp::ArgMin | ReduceOp::ArgMax => BackpropOp::none(),
955 };
956 let res = from_storage(storage, dims, op, false);
957 if keepdim {
958 Ok(res)
959 } else {
960 res.squeeze_dims(&[dim])
961 }
962 }
963
964 fn sum_impl<D: Dims>(&self, sum_dims: D, keepdim: bool) -> Result<Self> {
965 let sum_dims = sum_dims.to_indexes(self.shape(), "sum")?;
966 let storage = self
967 .storage()
968 .reduce_op(ReduceOp::Sum, self.layout(), &sum_dims)?;
969 let mut dims = self.dims().to_vec();
970 for &sum_dim in sum_dims.iter() {
971 dims[sum_dim] = 1
972 }
973 let op = BackpropOp::new1(self, |a| Op::Reduce(a, ReduceOp::Sum, dims.to_vec()));
974 let sum = from_storage(storage, dims, op, false);
975 if keepdim {
976 Ok(sum)
977 } else {
978 sum.squeeze_dims(&sum_dims)
979 }
980 }
981
982 pub fn roll<D>(&self, shift: i32, dim: D) -> Result<Self>
996 where
997 D: Dim + Clone,
998 {
999 let dim = dim.to_index(self.shape(), "roll")?;
1000 let dim_size = self.dim(dim)?;
1001 let shift = shift.rem_euclid(dim_size as i32) as usize;
1002 if shift == 0 {
1003 Ok(self.clone())
1004 } else {
1005 let a = self.narrow(dim, 0, dim_size - shift)?;
1006 let b = self.narrow(dim, dim_size - shift, shift)?;
1007 Tensor::cat(&[&b, &a], dim)
1008 }
1009 }
1010
1011 pub fn sum_keepdim<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1029 self.sum_impl(sum_dims, true)
1030 }
1031
1032 pub fn sum<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1036 self.sum_impl(sum_dims, false)
1037 }
1038
1039 pub fn mean_keepdim<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1057 let mean_dims = mean_dims.to_indexes(self.shape(), "mean-keepdim")?;
1058 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1059 let scale = 1f64 / (reduced_dim as f64);
1060 self.sum_impl(mean_dims, true)? * scale
1061 }
1062
1063 pub fn mean<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1067 let mean_dims = mean_dims.to_indexes(self.shape(), "mean")?;
1068 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1069 let scale = 1f64 / (reduced_dim as f64);
1070 self.sum_impl(mean_dims, false)? * scale
1071 }
1072
1073 pub fn var_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1075 let dim = dim.to_index(self.shape(), "var")?;
1076 let mean = self.mean_keepdim(dim)?;
1077 let squares = self.broadcast_sub(&mean)?.sqr()?;
1078 squares.sum_impl(dim, true)? / (self.dim(dim)? - 1) as f64
1079 }
1080
1081 pub fn var<D: Dim>(&self, dim: D) -> Result<Self> {
1083 let dim = dim.to_index(self.shape(), "var")?;
1084 self.var_keepdim(dim)?.squeeze(dim)
1085 }
1086
1087 pub fn max_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1090 self.reduce_impl(dim, true, ReduceOp::Max)
1091 }
1092
1093 pub fn max<D: Dim>(&self, dim: D) -> Result<Self> {
1095 self.reduce_impl(dim, false, ReduceOp::Max)
1096 }
1097
1098 pub fn min_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1101 self.reduce_impl(dim, true, ReduceOp::Min)
1102 }
1103
1104 pub fn min<D: Dim>(&self, dim: D) -> Result<Self> {
1106 self.reduce_impl(dim, false, ReduceOp::Min)
1107 }
1108
1109 pub fn argmax_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1110 self.reduce_impl(dim, true, ReduceOp::ArgMax)
1111 }
1112
1113 pub fn argmax<D: Dim>(&self, dim: D) -> Result<Self> {
1115 self.reduce_impl(dim, false, ReduceOp::ArgMax)
1116 }
1117
1118 pub fn argmin_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1119 self.reduce_impl(dim, true, ReduceOp::ArgMin)
1120 }
1121
1122 pub fn argmin<D: Dim>(&self, dim: D) -> Result<Self> {
1124 self.reduce_impl(dim, false, ReduceOp::ArgMin)
1125 }
1126
1127 pub fn cmp<T: TensorOrScalar>(&self, rhs: T, op: CmpOp) -> Result<Self> {
1132 let rhs = match rhs.to_tensor_scalar()? {
1133 crate::scalar::TensorScalar::Tensor(rhs) => rhs,
1134 crate::scalar::TensorScalar::Scalar(rhs) => rhs
1135 .to_dtype(self.dtype())?
1136 .to_device(self.device())?
1137 .broadcast_as(self.shape())?,
1138 };
1139 let shape = self.same_shape_binary_op(&rhs, "cmp")?;
1140 let storage = self
1141 .storage()
1142 .cmp(op, &rhs.storage(), self.layout(), rhs.layout())?;
1143 let op = BackpropOp::new1(self, |a| Op::Cmp(a, op));
1144 Ok(from_storage(storage, shape.dims(), op, false))
1145 }
1146
1147 pub fn eq<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1149 self.cmp(rhs, CmpOp::Eq)
1150 }
1151
1152 pub fn ne<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1154 self.cmp(rhs, CmpOp::Ne)
1155 }
1156
1157 pub fn lt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1160 self.cmp(rhs, CmpOp::Lt)
1161 }
1162
1163 pub fn gt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1166 self.cmp(rhs, CmpOp::Gt)
1167 }
1168
1169 pub fn ge<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1172 self.cmp(rhs, CmpOp::Ge)
1173 }
1174
1175 pub fn le<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1178 self.cmp(rhs, CmpOp::Le)
1179 }
1180
1181 pub fn clamp<T1: TensorOrScalar, T2: TensorOrScalar>(&self, min: T1, max: T2) -> Result<Self> {
1183 self.maximum(min)?.minimum(max)
1184 }
1185
1186 pub fn interpolate1d(&self, target_size: usize) -> Result<Self> {
1191 let (n, c, _l) = self.dims3()?;
1192 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest1D { arg, target_size });
1193 let storage = self
1194 .storage()
1195 .upsample_nearest1d(self.layout(), target_size)?;
1196 Ok(from_storage(storage, (n, c, target_size), op, false))
1197 }
1198
1199 pub fn upsample_nearest1d(&self, target_size: usize) -> Result<Self> {
1201 self.interpolate1d(target_size)
1202 }
1203
1204 pub fn interpolate2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1210 let (n, c, _h, _w) = self.dims4()?;
1211 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest2D {
1212 arg,
1213 target_h,
1214 target_w,
1215 });
1216 let storage = self
1217 .storage()
1218 .upsample_nearest2d(self.layout(), target_h, target_w)?;
1219 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1220 }
1221
1222 pub fn upsample_nearest2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1224 self.interpolate2d(target_h, target_w)
1225 }
1226
1227 pub fn upsample_bilinear2d(
1251 &self,
1252 target_h: usize,
1253 target_w: usize,
1254 align_corners: bool,
1255 ) -> Result<Self> {
1256 let (n, c, _h, _w) = self.dims4()?;
1257 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1258 arg,
1259 target_h,
1260 target_w,
1261 align_corners,
1262 });
1263 let storage = self.storage().upsample_bilinear2d(
1265 self.layout(),
1266 target_h,
1267 target_w,
1268 align_corners,
1269 None,
1270 None,
1271 )?;
1272 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1273 }
1274
1275 pub fn upsample_bilinear2d_with_scale(
1299 &self,
1300 scale_h: f64,
1301 scale_w: f64,
1302 align_corners: bool,
1303 ) -> Result<Self> {
1304 let (n, c, height_in, width_in) = self.dims4()?;
1305
1306 let height_out = (height_in as f64 * scale_h).floor() as usize;
1308 let width_out = (width_in as f64 * scale_w).floor() as usize;
1309
1310 if height_in == height_out && width_in == width_out {
1312 return Ok(self.clone());
1313 }
1314
1315 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1316 arg,
1317 target_h: height_out,
1318 target_w: width_out,
1319 align_corners,
1320 });
1321
1322 let storage = self.storage().upsample_bilinear2d(
1325 self.layout(),
1326 height_out,
1327 width_out,
1328 align_corners,
1329 Some(scale_h),
1330 Some(scale_w),
1331 )?;
1332 Ok(from_storage(
1333 storage,
1334 (n, c, height_out, width_out),
1335 op,
1336 false,
1337 ))
1338 }
1339
1340 pub fn avg_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1347 let sz = sz.to_usize2();
1348 self.avg_pool2d_with_stride(sz, sz)
1349 }
1350
1351 pub fn avg_pool2d_with_stride<T: crate::ToUsize2>(
1354 &self,
1355 kernel_size: T,
1356 stride: T,
1357 ) -> Result<Self> {
1358 let kernel_size = kernel_size.to_usize2();
1359 let stride = stride.to_usize2();
1360 let (n, c, h, w) = self.dims4()?;
1361 if h < kernel_size.0 || w < kernel_size.1 {
1362 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1363 }
1364 let h_out = (h - kernel_size.0) / stride.0 + 1;
1366 let w_out = (w - kernel_size.1) / stride.1 + 1;
1367 let op = BackpropOp::new1(self, |arg| Op::AvgPool2D {
1368 arg,
1369 kernel_size,
1370 stride,
1371 });
1372 let storage = self
1373 .storage()
1374 .avg_pool2d(self.layout(), kernel_size, stride)?;
1375 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1376 }
1377
1378 pub fn max_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1385 let sz = sz.to_usize2();
1386 self.max_pool2d_with_stride(sz, sz)
1387 }
1388
1389 pub fn max_pool2d_with_stride<T: crate::ToUsize2>(
1392 &self,
1393 kernel_size: T,
1394 stride: T,
1395 ) -> Result<Self> {
1396 let kernel_size = kernel_size.to_usize2();
1397 let stride = stride.to_usize2();
1398 let (n, c, h, w) = self.dims4()?;
1399 if h < kernel_size.0 || w < kernel_size.1 {
1400 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1401 }
1402 let h_out = (h - kernel_size.0) / stride.0 + 1;
1404 let w_out = (w - kernel_size.1) / stride.1 + 1;
1405 let op = BackpropOp::new1(self, |arg| Op::MaxPool2D {
1406 arg,
1407 kernel_size,
1408 stride,
1409 });
1410 let storage = self
1411 .storage()
1412 .max_pool2d(self.layout(), kernel_size, stride)?;
1413 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1414 }
1415
1416 pub fn dot(&self, rhs: &Self) -> Result<Self> {
1432 if self.dims().len() != 1 || rhs.dims().len() != 1 {
1433 return Err(Error::ShapeMismatchBinaryOp {
1434 lhs: self.shape().clone(),
1435 rhs: rhs.shape().clone(),
1436 op: "dot",
1437 });
1438 }
1439
1440 (self * rhs).and_then(|ret| ret.sum_all())
1441 }
1442
1443 pub fn norm(&self) -> Result<Self> {
1456 if self.dtype().is_int() {
1457 bail!("norm not supported for integer dtypes");
1458 }
1459
1460 self.sqr().and_then(|x| x.sum_all()).and_then(|x| x.sqrt())
1461 }
1462
1463 pub fn mv(&self, rhs: &Self) -> Result<Self> {
1478 let lhs_dims = self.dims();
1480 let rhs_dims = rhs.dims();
1481 if lhs_dims.len() != 2 || rhs_dims.len() != 1 || lhs_dims[1] != rhs_dims[0] {
1482 return Err(Error::ShapeMismatchBinaryOp {
1483 lhs: self.shape().clone(),
1484 rhs: rhs.shape().clone(),
1485 op: "mv",
1486 });
1487 }
1488
1489 self.matmul(&rhs.unsqueeze(1)?)?.squeeze(1)
1491 }
1492
1493 pub fn matmul(&self, rhs: &Self) -> Result<Self> {
1502 let a_dims = self.shape().dims();
1503 let b_dims = rhs.shape().dims();
1504
1505 let dim = a_dims.len();
1506
1507 if dim < 2 || b_dims.len() != dim {
1508 Err(Error::ShapeMismatchBinaryOp {
1509 lhs: self.shape().clone(),
1510 rhs: rhs.shape().clone(),
1511 op: "matmul",
1512 }
1513 .bt())?
1514 }
1515
1516 let m = a_dims[dim - 2];
1517 let k = a_dims[dim - 1];
1518 let k2 = b_dims[dim - 2];
1519 let n = b_dims[dim - 1];
1520
1521 let c_shape = Shape::from(&a_dims[..dim - 2]).extend(&[m, n]);
1522 if c_shape.elem_count() == 0 || k == 0 {
1523 return Tensor::zeros(c_shape, self.dtype(), self.device());
1524 }
1525 let batching: usize = a_dims[..dim - 2].iter().product();
1526 let batching_b: usize = b_dims[..dim - 2].iter().product();
1527 if k != k2 || batching != batching_b {
1528 Err(Error::ShapeMismatchBinaryOp {
1529 lhs: self.shape().clone(),
1530 rhs: rhs.shape().clone(),
1531 op: "matmul",
1532 }
1533 .bt())?
1534 }
1535
1536 let storage = self.storage().matmul(
1537 &rhs.storage(),
1538 (batching, m, n, k),
1539 self.layout(),
1540 rhs.layout(),
1541 )?;
1542 let op = BackpropOp::new2(self, rhs, Op::Matmul);
1543 Ok(from_storage(storage, c_shape, op, false))
1544 }
1545
1546 pub fn broadcast_matmul(&self, rhs: &Self) -> Result<Self> {
1552 let lhs = self;
1553 let (l_shape, r_shape) = lhs.shape().broadcast_shape_matmul(rhs.shape())?;
1554 let l_broadcast = l_shape != *lhs.shape();
1555 let r_broadcast = r_shape != *rhs.shape();
1556 match (l_broadcast, r_broadcast) {
1558 (true, true) => lhs
1559 .broadcast_as(&l_shape)?
1560 .contiguous()?
1561 .matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1562 (false, true) => lhs.matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1563 (true, false) => lhs.broadcast_as(&l_shape)?.contiguous()?.matmul(rhs),
1564 (false, false) => lhs.matmul(rhs),
1565 }
1566 }
1567
1568 pub fn where_cond(&self, on_true: &Self, on_false: &Self) -> Result<Self> {
1572 let _shap = self.same_shape_binary_op(on_true, "where_cond")?;
1573 let shape = self.same_shape_binary_op(on_false, "where_cond")?;
1574 let storage = self.storage().where_cond(
1575 self.layout(),
1576 &on_true.storage(),
1577 on_true.layout(),
1578 &on_false.storage(),
1579 on_false.layout(),
1580 )?;
1581 let op = BackpropOp::new3(self, on_true, on_false, Op::WhereCond);
1582 Ok(from_storage(storage, shape, op, false))
1583 }
1584
1585 pub fn embedding(&self, ids: &Self) -> Result<Self> {
1605 if self.rank() != 2 || ids.rank() != 1 {
1606 Err(Error::ShapeMismatchBinaryOp {
1607 lhs: self.shape().clone(),
1608 rhs: ids.shape().clone(),
1609 op: "embedding",
1610 }
1611 .bt())?
1612 }
1613 self.index_select(ids, 0)
1614 }
1615
1616 fn scatter_checks(&self, indexes: &Self, source: &Self, dim: usize) -> Result<()> {
1617 let source_dims = source.dims();
1618 let self_dims = self.dims();
1619 let mismatch = if source_dims.len() != self_dims.len() {
1620 true
1621 } else {
1622 let mut mismatch = false;
1623 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1624 if i != dim && d1 != d2 {
1625 mismatch = true;
1626 break;
1627 }
1628 }
1629 mismatch
1630 };
1631 if mismatch {
1632 Err(Error::ShapeMismatchBinaryOp {
1633 op: "scatter (self, src)",
1634 lhs: self.shape().clone(),
1635 rhs: source.shape().clone(),
1636 }
1637 .bt())?
1638 }
1639 if indexes.dims() != source.dims() {
1640 Err(Error::ShapeMismatchBinaryOp {
1641 op: "scatter (indexes, src)",
1642 lhs: indexes.shape().clone(),
1643 rhs: source.shape().clone(),
1644 }
1645 .bt())?
1646 }
1647 Ok(())
1648 }
1649
1650 pub fn scatter<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1651 let dim = dim.to_index(self.shape(), "scatter")?;
1652 self.scatter_checks(indexes, source, dim)?;
1653 let shape = self.shape();
1654 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1655 self.storage()
1656 .copy_strided_src(&mut storage, 0, self.layout())?;
1657 let layout = Layout::contiguous(shape);
1658 storage.scatter_set(
1659 &layout,
1660 &indexes.storage(),
1661 indexes.layout(),
1662 &source.storage(),
1663 source.layout(),
1664 dim,
1665 )?;
1666 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1667 Op::Scatter(t1, t2, t3, dim)
1668 });
1669 Ok(from_storage(storage, self.shape(), op, false))
1670 }
1671
1672 pub fn scatter_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1673 if self.same_storage(source) {
1674 crate::bail!("cannot use slice_set when self and src share their storage")
1675 }
1676 let dim = dim.to_index(self.shape(), "scatter-set")?;
1677 self.scatter_checks(indexes, source, dim)?;
1678 self.storage_mut().scatter_set(
1679 self.layout(),
1680 &indexes.storage(),
1681 indexes.layout(),
1682 &source.storage(),
1683 source.layout(),
1684 dim,
1685 )?;
1686 Ok(())
1687 }
1688
1689 pub fn scatter_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1690 let dim = dim.to_index(self.shape(), "scatter-add")?;
1691 self.scatter_checks(indexes, source, dim)?;
1692 let shape = self.shape();
1693 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1694 self.storage()
1695 .copy_strided_src(&mut storage, 0, self.layout())?;
1696 let layout = Layout::contiguous(shape);
1697 storage.scatter_add(
1698 &layout,
1699 &indexes.storage(),
1700 indexes.layout(),
1701 &source.storage(),
1702 source.layout(),
1703 dim,
1704 )?;
1705 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1706 Op::ScatterAdd(t1, t2, t3, dim)
1707 });
1708 Ok(from_storage(storage, self.shape(), op, false))
1709 }
1710
1711 pub fn scatter_add_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1712 if self.same_storage(source) {
1713 crate::bail!("cannot use slice_set when self and src share their storage")
1714 }
1715 let dim = dim.to_index(self.shape(), "scatter-add-set")?;
1716 self.scatter_checks(indexes, source, dim)?;
1717 self.storage_mut().scatter_add(
1718 self.layout(),
1719 &indexes.storage(),
1720 indexes.layout(),
1721 &source.storage(),
1722 source.layout(),
1723 dim,
1724 )?;
1725 Ok(())
1726 }
1727
1728 pub fn slice_scatter<D: Dim>(&self, src: &Self, dim: D, start: usize) -> Result<Self> {
1730 let dim = dim.to_index(self.shape(), "slice-scatter")?;
1731 if dim == 0 {
1732 self.slice_scatter0(src, start)
1733 } else {
1734 self.transpose(0, dim)?
1736 .slice_scatter0(&src.transpose(0, dim)?, start)?
1737 .transpose(0, dim)
1738 }
1739 }
1740
1741 pub fn slice_scatter0(&self, src: &Self, start: usize) -> Result<Self> {
1743 if self.dtype() != src.dtype() {
1744 Err(Error::DTypeMismatchBinaryOp {
1745 lhs: self.dtype(),
1746 rhs: src.dtype(),
1747 op: "slice-scatter",
1748 }
1749 .bt())?
1750 }
1751 if self.device().location() != src.device.location() {
1752 Err(Error::DeviceMismatchBinaryOp {
1753 lhs: self.device().location(),
1754 rhs: src.device().location(),
1755 op: "slice-scatter",
1756 }
1757 .bt())?
1758 }
1759 if self.rank() != src.rank() {
1760 Err(Error::UnexpectedNumberOfDims {
1761 expected: self.rank(),
1762 got: src.rank(),
1763 shape: src.shape().clone(),
1764 }
1765 .bt())?
1766 }
1767 let shape_ok =
1768 self.dims()
1769 .iter()
1770 .zip(src.dims().iter())
1771 .enumerate()
1772 .all(|(dim_idx, (&d1, &d2))| {
1773 if 0 == dim_idx {
1774 d2 + start <= d1
1775 } else {
1776 d1 == d2
1777 }
1778 });
1779 if !shape_ok {
1780 Err(Error::ShapeMismatchBinaryOp {
1781 op: "slice-scatter (self, src)",
1782 lhs: self.shape().clone(),
1783 rhs: src.shape().clone(),
1784 }
1785 .bt())?
1786 }
1787 let mut storage = unsafe { self.device().alloc_uninit(self.shape(), self.dtype())? };
1788 self.storage()
1789 .copy_strided_src(&mut storage, 0, self.layout())?;
1790 let offset = start * src.dims()[1..].iter().product::<usize>();
1791 src.storage()
1792 .copy_strided_src(&mut storage, offset, src.layout())?;
1793 let op = BackpropOp::new2(self, src, |t1, t2| Op::SliceScatter0(t1, t2, start));
1794 Ok(from_storage(storage, self.shape(), op, false))
1795 }
1796
1797 pub fn index_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1799 let dim = dim.to_index(self.shape(), "index-add")?;
1800 let source_dims = source.dims();
1801 let self_dims = self.dims();
1802 let mismatch = if source_dims.len() != self_dims.len() {
1803 true
1804 } else {
1805 let mut mismatch = false;
1806 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1807 if i != dim && d1 != d2 {
1808 mismatch = true;
1809 break;
1810 }
1811 }
1812 mismatch
1813 };
1814 if mismatch {
1815 Err(Error::ShapeMismatchBinaryOp {
1816 op: "index-add (self, source)",
1817 lhs: self.shape().clone(),
1818 rhs: source.shape().clone(),
1819 }
1820 .bt())?
1821 }
1822 let indexes_len = indexes.dims1()?;
1826 if source_dims[dim] != indexes_len {
1827 Err(Error::ShapeMismatchBinaryOp {
1828 op: "index-add (ids, source))",
1829 lhs: indexes.shape().clone(),
1830 rhs: source.shape().clone(),
1831 }
1832 .bt())?
1833 }
1834 let storage = self.storage().index_add(
1835 self.layout(),
1836 &indexes.storage(),
1837 indexes.layout(),
1838 &source.storage(),
1839 source.layout(),
1840 dim,
1841 )?;
1842 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1843 Op::IndexAdd(t1, t2, t3, dim)
1844 });
1845 Ok(from_storage(storage, self.shape(), op, false))
1846 }
1847
1848 pub fn gather<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1860 let dim = dim.to_index(self.shape(), "gather")?;
1861
1862 let self_dims = self.dims();
1863 let indexes_dims = indexes.dims();
1864 let mismatch = if indexes_dims.len() != self_dims.len() {
1865 true
1866 } else {
1867 let mut mismatch = false;
1868 for (i, (&d1, &d2)) in self_dims.iter().zip(indexes_dims.iter()).enumerate() {
1869 if i != dim && d1 < d2 {
1870 mismatch = true;
1871 break;
1872 }
1873 }
1874 mismatch
1875 };
1876 if mismatch {
1877 Err(Error::ShapeMismatchBinaryOp {
1878 op: "gather",
1879 lhs: self.shape().clone(),
1880 rhs: indexes.shape().clone(),
1881 }
1882 .bt())?
1883 }
1884 let storage =
1885 self.storage()
1886 .gather(self.layout(), &indexes.storage(), indexes.layout(), dim)?;
1887 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::Gather(t1, t2, dim));
1888 Ok(from_storage(storage, indexes.shape(), op, false))
1889 }
1890
1891 pub fn index_select<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1899 let dim = dim.to_index(self.shape(), "index-select")?;
1900 let indexes_len = match indexes.dims() {
1901 [l] => *l,
1902 _ => Err(Error::ShapeMismatchBinaryOp {
1903 lhs: self.shape().clone(),
1904 rhs: indexes.shape().clone(),
1905 op: "index-select",
1906 }
1907 .bt())?,
1908 };
1909 let storage = self.storage().index_select(
1910 &indexes.storage(),
1911 self.layout(),
1912 indexes.layout(),
1913 dim,
1914 )?;
1915 let mut dims = self.dims().to_vec();
1916 dims[dim] = indexes_len;
1917 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::IndexSelect(t1, t2, dim));
1918 Ok(from_storage(storage, dims, op, false))
1919 }
1920
1921 pub fn strided_index(&self) -> crate::StridedIndex<'_> {
1924 self.layout.strided_index()
1925 }
1926
1927 pub fn strided_blocks(&self) -> crate::StridedBlocks<'_> {
1932 self.layout.strided_blocks()
1933 }
1934
1935 pub fn to_vec1<S: crate::WithDType>(&self) -> Result<Vec<S>> {
1937 if self.rank() != 1 {
1938 Err(Error::UnexpectedNumberOfDims {
1939 expected: 1,
1940 got: self.rank(),
1941 shape: self.shape().clone(),
1942 }
1943 .bt())?
1944 }
1945 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1946 let data = S::cpu_storage_as_slice(cpu_storage)?;
1947 let data = match self.layout.contiguous_offsets() {
1948 Some((o1, o2)) => data[o1..o2].to_vec(),
1949 None => self.strided_index().map(|i| data[i]).collect(),
1950 };
1951 Ok::<Vec<_>, Error>(data)
1952 };
1953 match &*self.storage() {
1954 Storage::Cpu(storage) => from_cpu_storage(storage),
1955 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1956 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1957 #[cfg(feature = "rocm")]
1958 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1959 #[cfg(feature = "vulkan")]
1960 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1961 #[cfg(feature = "wgpu")]
1962 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1963 }
1964 }
1965
1966 pub fn to_vec2<S: crate::WithDType>(&self) -> Result<Vec<Vec<S>>> {
1968 let (dim1, dim2) = self.dims2()?;
1969 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1970 let data = S::cpu_storage_as_slice(cpu_storage)?;
1971 let mut rows = vec![];
1972 match self.layout.contiguous_offsets() {
1973 Some((o1, o2)) => {
1974 let data = &data[o1..o2];
1975 for idx_row in 0..dim1 {
1976 rows.push(data[idx_row * dim2..(idx_row + 1) * dim2].to_vec())
1977 }
1978 }
1979 None => {
1980 let mut src_index = self.strided_index();
1981 for _idx_row in 0..dim1 {
1982 let row = (0..dim2).map(|_| data[src_index.next().unwrap()]).collect();
1983 rows.push(row)
1984 }
1985 assert!(src_index.next().is_none());
1986 }
1987 }
1988 Ok(rows)
1989 };
1990 match &*self.storage() {
1991 Storage::Cpu(storage) => from_cpu_storage(storage),
1992 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1993 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1994 #[cfg(feature = "rocm")]
1995 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1996 #[cfg(feature = "vulkan")]
1997 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1998 #[cfg(feature = "wgpu")]
1999 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2000 }
2001 }
2002
2003 pub fn to_vec3<S: crate::WithDType>(&self) -> Result<Vec<Vec<Vec<S>>>> {
2005 let (dim1, dim2, dim3) = self.dims3()?;
2006 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
2007 let data = S::cpu_storage_as_slice(cpu_storage)?;
2008 let mut top_rows = vec![];
2009 match self.layout.contiguous_offsets() {
2010 Some((o1, o2)) => {
2011 let data = &data[o1..o2];
2012 let dim23 = dim2 * dim3;
2013 for idx1 in 0..dim1 {
2014 let data = &data[idx1 * dim23..(idx1 + 1) * dim23];
2015 let mut rows = vec![];
2016 for idx2 in 0..dim2 {
2017 rows.push(data[idx2 * dim3..(idx2 + 1) * dim3].to_vec())
2018 }
2019 top_rows.push(rows);
2020 }
2021 }
2022 None => {
2023 let mut src_index = self.strided_index();
2024 for _idx in 0..dim1 {
2025 let mut rows = vec![];
2026 for _jdx in 0..dim2 {
2027 let row = (0..dim3).map(|_| data[src_index.next().unwrap()]).collect();
2028 rows.push(row)
2029 }
2030 top_rows.push(rows);
2031 }
2032 assert!(src_index.next().is_none());
2033 }
2034 }
2035 Ok(top_rows)
2036 };
2037 match &*self.storage() {
2038 Storage::Cpu(storage) => from_cpu_storage(storage),
2039 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2040 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2041 #[cfg(feature = "rocm")]
2042 Storage::Rocm(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2043 #[cfg(feature = "vulkan")]
2044 Storage::Vulkan(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2045 #[cfg(feature = "wgpu")]
2046 Storage::Wgpu(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2047 }
2048 }
2049
2050 pub fn dtype(&self) -> DType {
2052 self.dtype
2053 }
2054
2055 pub fn device(&self) -> &Device {
2057 &self.device
2058 }
2059
2060 pub fn shape(&self) -> &Shape {
2062 self.layout().shape()
2063 }
2064
2065 pub fn dims(&self) -> &[usize] {
2067 self.shape().dims()
2068 }
2069
2070 pub fn dim<D: Dim>(&self, dim: D) -> Result<usize> {
2072 let dim = dim.to_index(self.shape(), "dim")?;
2073 Ok(self.dims()[dim])
2074 }
2075
2076 pub fn layout(&self) -> &Layout {
2079 &self.layout
2080 }
2081
2082 pub fn stride(&self) -> &[usize] {
2083 self.layout.stride()
2084 }
2085
2086 pub fn rank(&self) -> usize {
2088 self.shape().rank()
2089 }
2090
2091 pub fn elem_count(&self) -> usize {
2093 self.shape().elem_count()
2094 }
2095
2096 pub fn id(&self) -> TensorId {
2098 self.id
2099 }
2100
2101 pub fn is_variable(&self) -> bool {
2104 self.is_variable
2105 }
2106
2107 pub(crate) fn op(&self) -> &Option<Op> {
2108 &self.op
2109 }
2110
2111 pub fn max_all(&self) -> Result<Tensor> {
2122 if self.rank() == 0 {
2123 Ok(self.clone())
2124 } else {
2125 self.flatten_all()?.max(0)
2126 }
2127 }
2128
2129 pub fn min_all(&self) -> Result<Tensor> {
2140 if self.rank() == 0 {
2141 Ok(self.clone())
2142 } else {
2143 self.flatten_all()?.min(0)
2144 }
2145 }
2146
2147 pub fn sum_all(&self) -> Result<Tensor> {
2158 let dims: Vec<_> = (0..self.rank()).collect();
2159 self.sum(dims)
2160 }
2161
2162 pub fn mean_all(&self) -> Result<Tensor> {
2163 self.sum_all()? / self.elem_count() as f64
2164 }
2165
2166 fn flatten_<D1: Dim, D2: Dim>(
2167 &self,
2168 start_dim: Option<D1>,
2169 end_dim: Option<D2>,
2170 ) -> Result<Tensor> {
2171 if self.rank() == 0 {
2172 self.reshape(1)
2173 } else {
2174 let start_dim = match start_dim {
2175 None => 0,
2176 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2177 };
2178 let end_dim = match end_dim {
2179 None => self.rank() - 1,
2180 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2181 };
2182 if start_dim < end_dim {
2183 let dims = self.dims();
2184 let mut dst_dims = dims[..start_dim].to_vec();
2185 dst_dims.push(dims[start_dim..end_dim + 1].iter().product::<usize>());
2186 if end_dim + 1 < dims.len() {
2187 dst_dims.extend(&dims[end_dim + 1..]);
2188 }
2189 self.reshape(dst_dims)
2190 } else {
2191 Ok(self.clone())
2192 }
2193 }
2194 }
2195
2196 pub fn flatten<D1: Dim, D2: Dim>(&self, start_dim: D1, end_dim: D2) -> Result<Tensor> {
2199 self.flatten_(Some(start_dim), Some(end_dim))
2200 }
2201
2202 pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Tensor> {
2204 self.flatten_(None::<usize>, Some(end_dim))
2205 }
2206
2207 pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Tensor> {
2210 self.flatten_(Some(start_dim), None::<usize>)
2211 }
2212
2213 pub fn flatten_all(&self) -> Result<Tensor> {
2223 self.flatten_(None::<usize>, None::<usize>)
2224 }
2225
2226 pub fn get(&self, i: usize) -> Result<Tensor> {
2238 let dims = self.dims();
2239 if dims.is_empty() {
2240 Ok(self.clone())
2241 } else {
2242 self.narrow(0, i, 1)?.reshape(&dims[1..])
2243 }
2244 }
2245
2246 pub fn get_on_dim<D: Dim>(&self, dim: D, index: usize) -> Result<Tensor> {
2260 let dim = dim.to_index(self.shape(), "get_on_dim")?;
2261 self.narrow(dim, index, 1)?.squeeze(dim)
2262 }
2263
2264 pub fn t(&self) -> Result<Tensor> {
2275 let rank = self.rank();
2276 if rank < 2 {
2277 Err(Error::UnexpectedNumberOfDims {
2278 expected: 2,
2279 got: rank,
2280 shape: self.shape().clone(),
2281 }
2282 .bt())?
2283 }
2284 self.transpose(rank - 2, rank - 1)
2285 }
2286
2287 pub fn transpose<D1: Dim, D2: Dim>(&self, dim1: D1, dim2: D2) -> Result<Tensor> {
2290 let dim1 = dim1.to_index(self.shape(), "transpose")?;
2291 let dim2 = dim2.to_index(self.shape(), "transpose")?;
2292 if dim1 == dim2 {
2293 return Ok(self.clone());
2294 }
2295 let op = BackpropOp::new1(self, |t| Op::Transpose(t, dim1, dim2));
2296 let tensor_ = Tensor_ {
2297 id: TensorId::new(),
2298 storage: self.storage.clone(),
2299 layout: self.layout.transpose(dim1, dim2)?,
2300 op,
2301 is_variable: false,
2302 dtype: self.dtype,
2303 device: self.device.clone(),
2304 };
2305 Ok(Tensor(Arc::new(tensor_)))
2306 }
2307
2308 pub fn permute<D: Dims>(&self, dims: D) -> Result<Tensor> {
2320 let dims = dims.to_indexes(self.shape(), "permute")?;
2321 let is_permutation =
2323 dims.len() == self.rank() && (0..dims.len()).all(|i| dims.contains(&i));
2324 if !is_permutation {
2325 bail!(
2326 "dimension mismatch in permute, tensor {:?}, dims: {:?}",
2327 self.dims(),
2328 dims
2329 )
2330 }
2331 let op = BackpropOp::new1(self, |t| Op::Permute(t, dims.clone()));
2332 let tensor_ = Tensor_ {
2333 id: TensorId::new(),
2334 storage: self.storage.clone(),
2335 layout: self.layout.permute(&dims)?,
2336 op,
2337 is_variable: false,
2338 dtype: self.dtype,
2339 device: self.device.clone(),
2340 };
2341 Ok(Tensor(Arc::new(tensor_)))
2342 }
2343
2344 pub fn is_contiguous(&self) -> bool {
2346 self.layout.is_contiguous()
2347 }
2348
2349 pub fn is_fortran_contiguous(&self) -> bool {
2351 self.layout.is_fortran_contiguous()
2352 }
2353
2354 pub fn copy(&self) -> Result<Tensor> {
2357 let op = BackpropOp::new1(self, Op::Copy);
2358 let tensor_ = Tensor_ {
2359 id: TensorId::new(),
2360 storage: Arc::new(RwLock::new(self.storage().try_clone(self.layout())?)),
2361 layout: self.layout.clone(),
2362 op,
2363 is_variable: false,
2364 dtype: self.dtype,
2365 device: self.device.clone(),
2366 };
2367 Ok(Tensor(Arc::new(tensor_)))
2368 }
2369
2370 pub fn detach(&self) -> Tensor {
2375 if self.op.is_none() && !self.is_variable {
2376 self.clone()
2377 } else {
2378 let tensor_ = Tensor_ {
2379 id: TensorId::new(),
2380 storage: self.storage.clone(),
2381 layout: self.layout.clone(),
2382 op: BackpropOp::none(),
2383 is_variable: false,
2384 dtype: self.dtype,
2385 device: self.device.clone(),
2386 };
2387 Tensor(Arc::new(tensor_))
2388 }
2389 }
2390
2391 pub fn to_device(&self, device: &Device) -> Result<Tensor> {
2393 if self.device().same_device(device) {
2394 Ok(self.clone())
2395 } else {
2396 let storage = match (&*self.storage(), device) {
2397 (Storage::Cpu(storage), Device::Cuda(cuda)) => {
2398 Storage::Cuda(cuda.storage_from_cpu_storage(storage)?)
2399 }
2400 (Storage::Cpu(storage), Device::Metal(metal)) => {
2401 Storage::Metal(metal.storage_from_cpu_storage(storage)?)
2402 }
2403 (Storage::Cuda(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2404 (Storage::Metal(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2405 #[cfg(feature = "rocm")]
2406 (Storage::Rocm(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2407 #[cfg(feature = "vulkan")]
2408 (Storage::Vulkan(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2409 #[cfg(feature = "wgpu")]
2410 (Storage::Wgpu(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2411 #[cfg(feature = "rocm")]
2412 (Storage::Cpu(storage), Device::Rocm(rocm)) => {
2413 Storage::Rocm(rocm.storage_from_cpu_storage(storage)?)
2414 }
2415 #[cfg(feature = "vulkan")]
2416 (Storage::Cpu(storage), Device::Vulkan(vulkan)) => {
2417 Storage::Vulkan(vulkan.storage_from_cpu_storage(storage)?)
2418 }
2419 #[cfg(feature = "wgpu")]
2420 (Storage::Cpu(storage), Device::Wgpu(wgpu)) => {
2421 Storage::Wgpu(wgpu.storage_from_cpu_storage(storage)?)
2422 }
2423 #[cfg(feature = "rocm")]
2424 (Storage::Rocm(storage), Device::Rocm(rocm)) => {
2425 let cpu_storage = storage.to_cpu_storage()?;
2426 Storage::Rocm(rocm.storage_from_cpu_storage(&cpu_storage)?)
2427 }
2428 #[cfg(feature = "vulkan")]
2429 (Storage::Vulkan(storage), Device::Vulkan(vulkan)) => {
2430 let cpu_storage = storage.to_cpu_storage()?;
2431 Storage::Vulkan(vulkan.storage_from_cpu_storage(&cpu_storage)?)
2432 }
2433 #[cfg(feature = "wgpu")]
2434 (Storage::Wgpu(storage), Device::Wgpu(wgpu)) => {
2435 let cpu_storage = storage.to_cpu_storage()?;
2436 Storage::Wgpu(wgpu.storage_from_cpu_storage(&cpu_storage)?)
2437 }
2438 (Storage::Cuda(storage), Device::Cuda(cuda)) => {
2439 let cpu_storage = storage.to_cpu_storage()?;
2442 Storage::Cuda(cuda.storage_from_cpu_storage(&cpu_storage)?)
2443 }
2444 (Storage::Cpu(storage), Device::Cpu) => Storage::Cpu(storage.clone()),
2445 _ => {
2446 bail!(
2447 "not implemented yet, self.device: {:?}, device: {:?}",
2448 self.device(),
2449 device
2450 )
2451 }
2452 };
2453 let op = BackpropOp::new1(self, Op::ToDevice);
2454 let tensor_ = Tensor_ {
2455 id: TensorId::new(),
2456 storage: Arc::new(RwLock::new(storage)),
2457 layout: self.layout.clone(),
2458 op,
2459 is_variable: false,
2460 dtype: self.dtype,
2461 device: device.clone(),
2462 };
2463 Ok(Tensor(Arc::new(tensor_)))
2464 }
2465 }
2466
2467 pub fn broadcast_left<S: Into<Shape>>(&self, left_shape: S) -> Result<Self> {
2470 let left_shape = left_shape.into();
2471 let mut dims = left_shape.into_dims();
2472 dims.extend(self.dims());
2473 self.broadcast_as(dims)
2474 }
2475
2476 pub fn broadcast_as<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2484 let tensor_ = Tensor_ {
2485 id: TensorId::new(),
2486 storage: self.storage.clone(),
2487 layout: self.layout.broadcast_as(shape)?,
2488 op: BackpropOp::new1(self, Op::Broadcast),
2489 is_variable: false,
2490 dtype: self.dtype,
2491 device: self.device.clone(),
2492 };
2493 Ok(Tensor(Arc::new(tensor_)))
2494 }
2495
2496 pub fn expand<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2498 self.broadcast_as(shape)
2499 }
2500
2501 pub fn to_dtype(&self, dtype: DType) -> Result<Self> {
2512 if self.dtype() == dtype {
2513 Ok(self.clone())
2514 } else {
2515 let shape = self.shape();
2516 let storage = self.storage().to_dtype(self.layout(), dtype)?;
2517 let op = BackpropOp::new1(self, Op::ToDType);
2518 Ok(from_storage(storage, shape.clone(), op, false))
2519 }
2520 }
2521
2522 pub fn contiguous(&self) -> Result<Tensor> {
2525 if self.is_contiguous() {
2526 Ok(self.clone())
2527 } else {
2528 let shape = self.shape();
2529 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2530 self.storage()
2531 .copy_strided_src(&mut storage, 0, self.layout())?;
2532 let op = BackpropOp::new1(self, Op::Copy);
2533 Ok(from_storage(storage, shape.clone(), op, false))
2534 }
2535 }
2536
2537 pub fn force_contiguous(&self) -> Result<Tensor> {
2539 let shape = self.shape();
2540 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2541 self.storage()
2542 .copy_strided_src(&mut storage, 0, self.layout())?;
2543 let op = BackpropOp::new1(self, Op::Copy);
2544 Ok(from_storage(storage, shape.clone(), op, false))
2545 }
2546
2547 pub(crate) fn make_var(&self) -> Result<Tensor> {
2550 let shape = self.shape().clone();
2551 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2552 self.storage()
2553 .copy_strided_src(&mut storage, 0, self.layout())?;
2554 Ok(from_storage(storage, shape, BackpropOp::none(), true))
2555 }
2556
2557 pub fn reshape<S: ShapeWithOneHole>(&self, s: S) -> Result<Tensor> {
2582 let shape = s.into_shape(self.elem_count())?;
2583 if shape.elem_count() != self.elem_count() {
2584 return Err(Error::ShapeMismatchBinaryOp {
2585 lhs: self.shape().clone(),
2586 rhs: shape,
2587 op: "reshape",
2588 }
2589 .bt());
2590 }
2591 let op = BackpropOp::new1(self, Op::Reshape);
2592 if self.is_contiguous() {
2593 let tensor_ = Tensor_ {
2594 id: TensorId::new(),
2595 storage: self.storage.clone(),
2596 layout: Layout::contiguous_with_offset(shape, self.layout.start_offset()),
2597 op,
2598 is_variable: false,
2599 dtype: self.dtype,
2600 device: self.device.clone(),
2601 };
2602 Ok(Tensor(Arc::new(tensor_)))
2603 } else {
2604 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2605 self.storage()
2606 .copy_strided_src(&mut storage, 0, self.layout())?;
2607 Ok(from_storage(storage, shape, op, false))
2608 }
2609 }
2610
2611 pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
2625 let dims = self.dims();
2628 let dim = dim.to_index(self.shape(), "squeeze")?;
2629 if dims[dim] == 1 {
2630 let mut dims = dims.to_vec();
2631 let mut strides = self.stride().to_vec();
2632 dims.remove(dim);
2633 strides.remove(dim);
2634 let tensor_ = Tensor_ {
2635 id: TensorId::new(),
2636 storage: self.storage.clone(),
2637 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2638 op: BackpropOp::new1(self, Op::Reshape),
2639 is_variable: false,
2640 dtype: self.dtype,
2641 device: self.device.clone(),
2642 };
2643 Ok(Tensor(Arc::new(tensor_)))
2644 } else {
2645 Ok(self.clone())
2646 }
2647 }
2648
2649 pub fn unsqueeze<D: Dim>(&self, dim: D) -> Result<Self> {
2663 let mut dims = self.dims().to_vec();
2664 let mut strides = self.stride().to_vec();
2665 let dim = dim.to_index_plus_one(self.shape(), "unsqueeze")?;
2666 dims.insert(dim, 1);
2668 let stride = if dim < strides.len() { strides[dim] } else { 1 };
2671 strides.insert(dim, stride);
2672 let tensor_ = Tensor_ {
2673 id: TensorId::new(),
2674 storage: self.storage.clone(),
2675 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2676 op: BackpropOp::new1(self, Op::Reshape),
2677 is_variable: false,
2678 dtype: self.dtype,
2679 device: self.device.clone(),
2680 };
2681 Ok(Tensor(Arc::new(tensor_)))
2682 }
2683
2684 pub fn stack<A: AsRef<Tensor>, D: Dim>(args: &[A], dim: D) -> Result<Self> {
2701 if args.is_empty() {
2702 Err(Error::OpRequiresAtLeastOneTensor { op: "stack" }.bt())?
2703 }
2704 let dim = dim.to_index_plus_one(args[0].as_ref().shape(), "stack")?;
2705 let args = args
2706 .iter()
2707 .map(|t| t.as_ref().unsqueeze(dim))
2708 .collect::<Result<Vec<_>>>()?;
2709 Self::cat(&args, dim)
2710 }
2711
2712 pub fn pad_with_zeros<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2715 if left == 0 && right == 0 {
2716 Ok(self.clone())
2717 } else if left == 0 {
2718 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2719 let mut dims = self.dims().to_vec();
2720 dims[dim] = right;
2721 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2722 Tensor::cat(&[self, &right], dim)
2723 } else if right == 0 {
2724 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2725 let mut dims = self.dims().to_vec();
2726 dims[dim] = left;
2727 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2728 Tensor::cat(&[&left, self], dim)
2729 } else {
2730 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2731 let mut dims = self.dims().to_vec();
2732 dims[dim] = left;
2733 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2734 dims[dim] = right;
2735 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2736 Tensor::cat(&[&left, self, &right], dim)
2737 }
2738 }
2739
2740 pub fn pad_with_same<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2743 if left == 0 && right == 0 {
2744 Ok(self.clone())
2745 } else if self.elem_count() == 0 {
2746 bail!("cannot use pad_with_same on an empty tensor")
2747 } else if left == 0 {
2748 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2749 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2750 let mut v = vec![self];
2751 for _ in 0..right {
2752 v.push(&r)
2753 }
2754 Tensor::cat(&v, dim)
2755 } else if right == 0 {
2756 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2757 let l = self.narrow(dim, 0, 1)?;
2758 let mut v = vec![];
2759 for _ in 0..left {
2760 v.push(&l)
2761 }
2762 v.push(self);
2763 Tensor::cat(&v, dim)
2764 } else {
2765 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2766 let l = self.narrow(dim, 0, 1)?;
2767 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2768 let mut v = vec![];
2769 for _ in 0..left {
2770 v.push(&l)
2771 }
2772 v.push(self);
2773 for _ in 0..right {
2774 v.push(&r)
2775 }
2776 Tensor::cat(&v, dim)
2777 }
2778 }
2779
2780 pub fn apply<M: crate::Module>(&self, m: &M) -> Result<Self> {
2782 m.forward(self)
2783 }
2784
2785 pub fn apply_t<M: crate::ModuleT>(&self, m: &M, train: bool) -> Result<Self> {
2787 m.forward_t(self, train)
2788 }
2789
2790 pub(crate) fn storage(&self) -> std::sync::RwLockReadGuard<'_, Storage> {
2791 self.storage.read().unwrap()
2792 }
2793
2794 pub(crate) fn storage_mut(&self) -> std::sync::RwLockWriteGuard<'_, Storage> {
2795 self.storage.write().unwrap()
2796 }
2797
2798 pub(crate) fn storage_mut_and_layout(
2801 &self,
2802 ) -> (std::sync::RwLockWriteGuard<'_, Storage>, &Layout) {
2803 let storage = self.storage.write().unwrap();
2804 (storage, &self.layout)
2805 }
2806
2807 pub fn storage_and_layout(&self) -> (std::sync::RwLockReadGuard<'_, Storage>, &Layout) {
2809 let storage = self.storage.read().unwrap();
2810 (storage, &self.layout)
2811 }
2812
2813 pub(crate) fn same_storage(&self, rhs: &Self) -> bool {
2814 let lhs: &RwLock<Storage> = self.storage.as_ref();
2815 let rhs: &RwLock<Storage> = rhs.storage.as_ref();
2816 std::ptr::eq(lhs, rhs)
2817 }
2818
2819 pub fn normalize_axis(&self, axis: i64) -> Result<usize> {
2822 let rank = self.rank() as i64;
2823 if rank <= axis {
2824 bail!("axis {axis} is too large, tensor rank {rank}")
2825 } else if 0 <= axis {
2826 Ok(axis as usize)
2827 } else {
2828 let naxis = rank + axis;
2829 if naxis < 0 {
2830 bail!("axis {axis} is too small, tensor rank {rank}")
2831 }
2832 Ok(naxis as usize)
2833 }
2834 }
2835
2836 pub fn tril2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2838 let t = Tensor::arange(0u32, n as u32, device)?;
2839 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2840 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2841 t1.le(&t2)?.to_dtype(dtype)
2842 }
2843
2844 pub fn triu2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2846 let t = Tensor::arange(0u32, n as u32, device)?;
2847 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2848 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2849 t1.ge(&t2)?.to_dtype(dtype)
2850 }
2851
2852 pub fn eye(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2854 let t = Tensor::arange(0u32, n as u32, device)?;
2855 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2856 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2857 t1.eq(&t2)?.to_dtype(dtype)
2858 }
2859
2860 pub fn cumsum<D: Dim>(&self, dim: D) -> Result<Self> {
2865 let dim = dim.to_index(self.shape(), "cumsum")?;
2866 let rank = self.rank();
2867 if rank == 0 {
2868 return Ok(self.clone());
2869 }
2870 let n_axis = self.dim(dim)?;
2871 let triu = Tensor::triu2(n_axis, self.dtype(), self.device())?;
2872 if rank == 1 {
2873 self.unsqueeze(0)?.matmul(&triu)?.squeeze(0)
2874 } else {
2875 let last = rank - 1;
2876 let t = self.transpose(dim, last)?;
2877 let t = t.broadcast_matmul(&triu)?;
2878 t.transpose(dim, last)
2879 }
2880 }
2881
2882 pub fn slice_assign<D: std::ops::RangeBounds<usize>>(
2885 &self,
2886 ranges: &[D],
2887 src: &Tensor,
2888 ) -> Result<Self> {
2889 let src_dims = src.dims();
2890 let self_dims = self.dims();
2891 if self_dims.len() != src_dims.len() {
2892 bail!(
2893 "slice-assign requires input with the same rank {} <> {}",
2894 self_dims.len(),
2895 src_dims.len()
2896 )
2897 }
2898 if self_dims.len() != ranges.len() {
2899 bail!(
2900 "slice-assign requires input with the same rank as there are ranges {} <> {}",
2901 self_dims.len(),
2902 ranges.len()
2903 )
2904 }
2905 let mut src = src.clone();
2906 let mut mask = Self::ones(src.shape(), DType::U8, src.device())?;
2907 for (i, range) in ranges.iter().enumerate() {
2908 let start_included = match range.start_bound() {
2909 std::ops::Bound::Unbounded => 0,
2910 std::ops::Bound::Included(v) => *v,
2911 std::ops::Bound::Excluded(v) => *v + 1,
2912 };
2913 let end_excluded = match range.end_bound() {
2914 std::ops::Bound::Unbounded => self_dims[i],
2915 std::ops::Bound::Included(v) => *v + 1,
2916 std::ops::Bound::Excluded(v) => *v,
2917 };
2918 if end_excluded <= start_included {
2919 bail!("slice-assign: empty range for dim {i}, {start_included} {end_excluded}")
2920 }
2921 if self_dims[i] < end_excluded {
2922 bail!(
2923 "slice-assign: upper bound is out of range for dim {i}, {end_excluded} {}",
2924 self_dims[i]
2925 )
2926 }
2927 if end_excluded - start_included != src_dims[i] {
2928 bail!(
2929 "slice-assign: the range for dim {i} ({start_included}..{end_excluded}) does not match the size of src {}", src_dims[i]
2930 )
2931 }
2932 src = src.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?;
2933 mask = mask.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?
2934 }
2935 mask.where_cond(&src, self)
2936 }
2937
2938 pub fn log_sum_exp<D: Dims>(&self, sum_dims: D) -> Result<Self> {
2940 let sum_dims = sum_dims.to_indexes(self.shape(), "log-sum-exp")?;
2941 if sum_dims.is_empty() {
2942 return Ok(self.clone());
2943 }
2944 let max = sum_dims[1..]
2945 .iter()
2946 .try_fold(self.max_keepdim(sum_dims[0])?, |max, &dim| {
2947 max.max_keepdim(dim)
2948 })?;
2949 let exp = self.broadcast_sub(&max)?.exp()?;
2950 let sum = exp.sum(sum_dims.clone())?;
2951
2952 sum.log()? + max.squeeze_dims(&sum_dims)
2953 }
2954
2955 pub fn pow(&self, rhs: &Tensor) -> Result<Self> {
2957 rhs.mul(&self.log()?)?.exp()
2958 }
2959
2960 pub fn broadcast_pow(&self, rhs: &Tensor) -> Result<Self> {
2962 rhs.broadcast_mul(&self.log()?)?.exp()
2963 }
2964
2965 pub fn flip(&self, dims: &[usize]) -> Result<Tensor> {
2977 let mut result = self.clone();
2978 for &dim in dims.iter() {
2979 let size = result.dim(dim)?;
2980 let indices: Vec<i64> = (0..size).rev().map(|x| x as i64).collect();
2981 let indices_tensor = Tensor::from_vec(indices, (size,), result.device())?;
2982 result = result.index_select(&indices_tensor, dim)?;
2983 }
2984 Ok(result)
2985 }
2986
2987 pub fn unfold<D: Dim>(&self, dim: D, size: usize, step: usize) -> Result<Self> {
2990 let mut sizes = self.dims().to_vec();
2992 let mut strides = self.stride().to_vec();
2993
2994 let dim = dim.to_index(self.shape(), "unfold")?;
2995
2996 let max_len = if self.dims().is_empty() {
2997 1
2998 } else {
2999 sizes[dim]
3000 };
3001 if size > max_len {
3002 bail!(
3003 "unsqueeze: maximum size for tensor at dimension {dim} is {max_len} but size is {size}"
3004 )
3005 }
3006 sizes.push(size);
3007 strides.push(if self.dims().is_empty() {
3008 1
3009 } else {
3010 strides[dim]
3011 });
3012
3013 if !self.dims().is_empty() {
3014 sizes[dim] = ((sizes[dim] as f32 - size as f32) / step as f32 + 1.) as usize;
3015 strides[dim] *= step;
3016 }
3017
3018 let tensor_ = Tensor_ {
3019 id: TensorId::new(),
3020 storage: self.storage.clone(),
3021 layout: Layout::new(sizes.into(), strides, self.layout.start_offset()),
3022 op: BackpropOp::new1(self, Op::Reshape),
3023 is_variable: false,
3024 dtype: self.dtype,
3025 device: self.device.clone(),
3026 };
3027 Ok(Tensor(Arc::new(tensor_)))
3028 }
3029}
3030
3031macro_rules! bin_trait {
3032 ($trait:ident, $fn1:ident, $mul:expr, $add:expr) => {
3033 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for Tensor {
3034 type Output = Result<Tensor>;
3035
3036 fn $fn1(self, rhs: B) -> Self::Output {
3037 Tensor::$fn1(&self, rhs.borrow())
3038 }
3039 }
3040
3041 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for &Tensor {
3042 type Output = Result<Tensor>;
3043
3044 fn $fn1(self, rhs: B) -> Self::Output {
3045 Tensor::$fn1(&self, rhs.borrow())
3046 }
3047 }
3048
3049 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Tensor> for Result<B> {
3050 type Output = Result<Tensor>;
3051
3052 fn $fn1(self, rhs: Tensor) -> Self::Output {
3053 Tensor::$fn1(self?.borrow(), &rhs)
3054 }
3055 }
3056
3057 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<&Tensor> for Result<B> {
3058 type Output = Result<Tensor>;
3059
3060 fn $fn1(self, rhs: &Tensor) -> Self::Output {
3061 Tensor::$fn1(self?.borrow(), rhs)
3062 }
3063 }
3064
3065 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for Tensor {
3066 type Output = Result<Tensor>;
3067
3068 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3069 Tensor::$fn1(&self, rhs?.borrow())
3070 }
3071 }
3072
3073 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for &Tensor {
3074 type Output = Result<Tensor>;
3075
3076 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3077 Tensor::$fn1(&self, rhs?.borrow())
3078 }
3079 }
3080
3081 impl std::ops::$trait<f64> for Tensor {
3082 type Output = Result<Tensor>;
3083
3084 fn $fn1(self, rhs: f64) -> Self::Output {
3085 self.affine($mul(rhs), $add(rhs))
3086 }
3087 }
3088
3089 impl std::ops::$trait<f64> for &Tensor {
3090 type Output = Result<Tensor>;
3091
3092 fn $fn1(self, rhs: f64) -> Self::Output {
3093 self.affine($mul(rhs), $add(rhs))
3094 }
3095 }
3096 };
3097}
3098
3099bin_trait!(Add, add, |_| 1., |v| v);
3100bin_trait!(Sub, sub, |_| 1., |v: f64| -v);
3101bin_trait!(Mul, mul, |v| v, |_| 0.);
3102bin_trait!(Div, div, |v| 1. / v, |_| 0.);
3103
3104impl std::ops::Add<Tensor> for f64 {
3105 type Output = Result<Tensor>;
3106
3107 fn add(self, rhs: Tensor) -> Self::Output {
3108 rhs + self
3109 }
3110}
3111
3112impl std::ops::Add<&Tensor> for f64 {
3113 type Output = Result<Tensor>;
3114
3115 fn add(self, rhs: &Tensor) -> Self::Output {
3116 rhs + self
3117 }
3118}
3119
3120impl std::ops::Mul<Tensor> for f64 {
3121 type Output = Result<Tensor>;
3122
3123 fn mul(self, rhs: Tensor) -> Self::Output {
3124 rhs * self
3125 }
3126}
3127
3128impl std::ops::Mul<&Tensor> for f64 {
3129 type Output = Result<Tensor>;
3130
3131 fn mul(self, rhs: &Tensor) -> Self::Output {
3132 rhs * self
3133 }
3134}
3135
3136impl std::ops::Sub<Tensor> for f64 {
3137 type Output = Result<Tensor>;
3138
3139 fn sub(self, rhs: Tensor) -> Self::Output {
3140 rhs.affine(-1., self)
3141 }
3142}
3143
3144impl std::ops::Sub<&Tensor> for f64 {
3145 type Output = Result<Tensor>;
3146
3147 fn sub(self, rhs: &Tensor) -> Self::Output {
3148 rhs.affine(-1., self)
3149 }
3150}
3151
3152impl std::ops::Div<Tensor> for f64 {
3153 type Output = Result<Tensor>;
3154
3155 #[allow(clippy::suspicious_arithmetic_impl)]
3156 fn div(self, rhs: Tensor) -> Self::Output {
3157 rhs.recip()? * self
3158 }
3159}
3160
3161impl std::ops::Div<&Tensor> for f64 {
3162 type Output = Result<Tensor>;
3163
3164 #[allow(clippy::suspicious_arithmetic_impl)]
3165 fn div(self, rhs: &Tensor) -> Self::Output {
3166 rhs.recip()? * self
3167 }
3168}
3169
3170impl<S: Into<Shape>> From<(Storage, S)> for Tensor {
3171 fn from((storage, shape): (Storage, S)) -> Self {
3172 from_storage(storage, shape, BackpropOp::none(), false)
3173 }
3174}