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 }
682 }
683
684 pub fn to_vec0<S: crate::WithDType>(&self) -> Result<S> {
686 self.to_scalar::<S>()
687 }
688
689 pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Tensor> {
691 let repeats = shape.into();
693 let repeats = repeats.dims();
694 let mut inp = if self.rank() < repeats.len() {
695 let shape = [vec![1; repeats.len() - self.rank()], self.dims().to_vec()].concat();
696 self.reshape(shape)?
697 } else {
698 self.clone()
699 };
700 for (idx, &repeat) in repeats.iter().enumerate() {
701 inp = match repeat {
702 0 => inp.narrow(idx, 0, 0)?,
703 1 => inp,
704 repeat => Tensor::cat(&vec![&inp; repeat], idx)?,
705 };
706 }
707 Ok(inp)
708 }
709
710 pub fn meshgrid<A: AsRef<Tensor>>(args: &[A], xy_indexing: bool) -> Result<Vec<Self>> {
747 if args.len() <= 1 {
748 Err(Error::OpRequiresAtLeastTwoTensors { op: "meshgrid" }.bt())?
749 }
750 let args: Vec<_> = if xy_indexing {
751 args.iter().rev().collect()
752 } else {
753 args.iter().collect()
754 };
755
756 let mut shape = Vec::with_capacity(args.len());
757 for arg in args.iter() {
758 shape.push(arg.as_ref().dims1()?)
759 }
760
761 let mut grids = Vec::with_capacity(args.len());
762 for idx in 0..args.len() {
763 let mut ones = vec![1usize; args.len()];
764 ones[idx] = shape[idx];
765 let arg = args[idx].as_ref().reshape(ones)?;
766 let mut repeats = shape.clone();
767 repeats[idx] = 1;
768 let repeated_tensor = arg.repeat(repeats)?;
769 grids.push(repeated_tensor);
770 }
771 if xy_indexing {
772 grids.reverse();
773 }
774 Ok(grids)
775 }
776
777 pub fn affine(&self, mul: f64, add: f64) -> Result<Self> {
789 if self.elem_count() == 0 {
790 return Ok(self.clone());
791 }
792 let storage = self.storage().affine(self.layout(), mul, add)?;
793 let op = BackpropOp::new1(self, |arg| Op::Affine { arg, mul, add });
794 Ok(from_storage(storage, self.shape(), op, false))
795 }
796
797 pub fn elu(&self, alpha: f64) -> Result<Self> {
799 if self.elem_count() == 0 {
800 return Ok(self.clone());
801 }
802 let storage = self.storage().elu(self.layout(), alpha)?;
803 let op = BackpropOp::new1(self, |t| Op::Elu(t, alpha));
804 Ok(from_storage(storage, self.shape(), op, false))
805 }
806
807 pub fn powf(&self, e: f64) -> Result<Self> {
809 if self.elem_count() == 0 {
810 return Ok(self.clone());
811 }
812 let storage = self.storage().powf(self.layout(), e)?;
813 let op = BackpropOp::new1(self, |t| Op::Powf(t, e));
814 Ok(from_storage(storage, self.shape(), op, false))
815 }
816
817 pub(crate) fn check_dim(&self, dim: usize, op: &'static str) -> Result<()> {
818 if dim >= self.dims().len() {
819 Err(Error::DimOutOfRange {
820 shape: self.shape().clone(),
821 dim: dim as i32,
822 op,
823 }
824 .bt())?
825 } else {
826 Ok(())
827 }
828 }
829
830 pub fn chunk<D: Dim>(&self, chunks: usize, dim: D) -> Result<Vec<Self>> {
833 let dim = dim.to_index(self.shape(), "chunk")?;
834 let size = self.dim(dim)?;
835 if size < chunks {
836 (0..size).map(|i| self.narrow(dim, i, 1)).collect()
837 } else {
838 let chunk_size = size / chunks;
839 let cnt_additional = size % chunks;
840 let mut tensors = vec![];
841 let mut sum_chunk_size = 0;
842 for i in 0..chunks {
843 let chunk_size = if i < cnt_additional {
844 chunk_size + 1
845 } else {
846 chunk_size
847 };
848 let tensor = self.narrow(dim, sum_chunk_size, chunk_size)?;
849 tensors.push(tensor);
850 sum_chunk_size += chunk_size
851 }
852 Ok(tensors)
853 }
854 }
855
856 pub fn narrow<D: Dim>(&self, dim: D, start: usize, len: usize) -> Result<Self> {
883 let dims = self.dims();
884 let dim = dim.to_index(self.shape(), "narrow")?;
885 let err = |msg| {
886 Err::<(), _>(
887 Error::NarrowInvalidArgs {
888 shape: self.shape().clone(),
889 dim,
890 start,
891 len,
892 msg,
893 }
894 .bt(),
895 )
896 };
897 if start > dims[dim] {
898 err("start > dim_len")?
899 }
900 if start.saturating_add(len) > dims[dim] {
901 err("start + len > dim_len")?
902 }
903 if start == 0 && dims[dim] == len {
904 Ok(self.clone())
905 } else {
906 let op = BackpropOp::new1(self, |t| Op::Narrow(t, dim, start, len));
907 let layout = self.layout().narrow(dim, start, len)?;
908 let tensor_ = Tensor_ {
909 id: TensorId::new(),
910 storage: self.storage.clone(),
911 layout,
912 op,
913 is_variable: false,
914 dtype: self.dtype,
915 device: self.device.clone(),
916 };
917 Ok(Tensor(Arc::new(tensor_)))
918 }
919 }
920
921 fn squeeze_dims(self, dims: &[usize]) -> Result<Self> {
922 match dims {
923 [] => Ok(self),
924 [i] => self.squeeze(*i),
925 dims => {
926 let dims = self
927 .dims()
928 .iter()
929 .enumerate()
930 .filter_map(|(dim_idx, &v)| {
931 if dims.contains(&dim_idx) {
932 None
933 } else {
934 Some(v)
935 }
936 })
937 .collect::<Vec<_>>();
938 self.reshape(dims)
939 }
940 }
941 }
942
943 fn reduce_impl<D: Dim>(&self, dim: D, keepdim: bool, op: ReduceOp) -> Result<Self> {
944 let dim = dim.to_index(self.shape(), op.name())?;
945 let storage = self.storage().reduce_op(op, self.layout(), &[dim])?;
946 let mut dims = self.dims().to_vec();
947 dims[dim] = 1;
948 let op = match op {
949 ReduceOp::Sum | ReduceOp::Min | ReduceOp::Max => {
950 BackpropOp::new1(self, |arg| Op::Reduce(arg, op, dims.to_vec()))
951 }
952 ReduceOp::ArgMin | ReduceOp::ArgMax => BackpropOp::none(),
953 };
954 let res = from_storage(storage, dims, op, false);
955 if keepdim {
956 Ok(res)
957 } else {
958 res.squeeze_dims(&[dim])
959 }
960 }
961
962 fn sum_impl<D: Dims>(&self, sum_dims: D, keepdim: bool) -> Result<Self> {
963 let sum_dims = sum_dims.to_indexes(self.shape(), "sum")?;
964 let storage = self
965 .storage()
966 .reduce_op(ReduceOp::Sum, self.layout(), &sum_dims)?;
967 let mut dims = self.dims().to_vec();
968 for &sum_dim in sum_dims.iter() {
969 dims[sum_dim] = 1
970 }
971 let op = BackpropOp::new1(self, |a| Op::Reduce(a, ReduceOp::Sum, dims.to_vec()));
972 let sum = from_storage(storage, dims, op, false);
973 if keepdim {
974 Ok(sum)
975 } else {
976 sum.squeeze_dims(&sum_dims)
977 }
978 }
979
980 pub fn roll<D>(&self, shift: i32, dim: D) -> Result<Self>
994 where
995 D: Dim + Clone,
996 {
997 let dim = dim.to_index(self.shape(), "roll")?;
998 let dim_size = self.dim(dim)?;
999 let shift = shift.rem_euclid(dim_size as i32) as usize;
1000 if shift == 0 {
1001 Ok(self.clone())
1002 } else {
1003 let a = self.narrow(dim, 0, dim_size - shift)?;
1004 let b = self.narrow(dim, dim_size - shift, shift)?;
1005 Tensor::cat(&[&b, &a], dim)
1006 }
1007 }
1008
1009 pub fn sum_keepdim<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1027 self.sum_impl(sum_dims, true)
1028 }
1029
1030 pub fn sum<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1034 self.sum_impl(sum_dims, false)
1035 }
1036
1037 pub fn mean_keepdim<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1055 let mean_dims = mean_dims.to_indexes(self.shape(), "mean-keepdim")?;
1056 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1057 let scale = 1f64 / (reduced_dim as f64);
1058 self.sum_impl(mean_dims, true)? * scale
1059 }
1060
1061 pub fn mean<D: Dims>(&self, mean_dims: D) -> Result<Self> {
1065 let mean_dims = mean_dims.to_indexes(self.shape(), "mean")?;
1066 let reduced_dim: usize = mean_dims.iter().map(|i| self.dims()[*i]).product();
1067 let scale = 1f64 / (reduced_dim as f64);
1068 self.sum_impl(mean_dims, false)? * scale
1069 }
1070
1071 pub fn var_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1073 let dim = dim.to_index(self.shape(), "var")?;
1074 let mean = self.mean_keepdim(dim)?;
1075 let squares = self.broadcast_sub(&mean)?.sqr()?;
1076 squares.sum_impl(dim, true)? / (self.dim(dim)? - 1) as f64
1077 }
1078
1079 pub fn var<D: Dim>(&self, dim: D) -> Result<Self> {
1081 let dim = dim.to_index(self.shape(), "var")?;
1082 self.var_keepdim(dim)?.squeeze(dim)
1083 }
1084
1085 pub fn max_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1088 self.reduce_impl(dim, true, ReduceOp::Max)
1089 }
1090
1091 pub fn max<D: Dim>(&self, dim: D) -> Result<Self> {
1093 self.reduce_impl(dim, false, ReduceOp::Max)
1094 }
1095
1096 pub fn min_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1099 self.reduce_impl(dim, true, ReduceOp::Min)
1100 }
1101
1102 pub fn min<D: Dim>(&self, dim: D) -> Result<Self> {
1104 self.reduce_impl(dim, false, ReduceOp::Min)
1105 }
1106
1107 pub fn argmax_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1108 self.reduce_impl(dim, true, ReduceOp::ArgMax)
1109 }
1110
1111 pub fn argmax<D: Dim>(&self, dim: D) -> Result<Self> {
1113 self.reduce_impl(dim, false, ReduceOp::ArgMax)
1114 }
1115
1116 pub fn argmin_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1117 self.reduce_impl(dim, true, ReduceOp::ArgMin)
1118 }
1119
1120 pub fn argmin<D: Dim>(&self, dim: D) -> Result<Self> {
1122 self.reduce_impl(dim, false, ReduceOp::ArgMin)
1123 }
1124
1125 pub fn cmp<T: TensorOrScalar>(&self, rhs: T, op: CmpOp) -> Result<Self> {
1130 let rhs = match rhs.to_tensor_scalar()? {
1131 crate::scalar::TensorScalar::Tensor(rhs) => rhs,
1132 crate::scalar::TensorScalar::Scalar(rhs) => rhs
1133 .to_dtype(self.dtype())?
1134 .to_device(self.device())?
1135 .broadcast_as(self.shape())?,
1136 };
1137 let shape = self.same_shape_binary_op(&rhs, "cmp")?;
1138 let storage = self
1139 .storage()
1140 .cmp(op, &rhs.storage(), self.layout(), rhs.layout())?;
1141 let op = BackpropOp::new1(self, |a| Op::Cmp(a, op));
1142 Ok(from_storage(storage, shape.dims(), op, false))
1143 }
1144
1145 pub fn eq<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1147 self.cmp(rhs, CmpOp::Eq)
1148 }
1149
1150 pub fn ne<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1152 self.cmp(rhs, CmpOp::Ne)
1153 }
1154
1155 pub fn lt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1158 self.cmp(rhs, CmpOp::Lt)
1159 }
1160
1161 pub fn gt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1164 self.cmp(rhs, CmpOp::Gt)
1165 }
1166
1167 pub fn ge<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1170 self.cmp(rhs, CmpOp::Ge)
1171 }
1172
1173 pub fn le<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1176 self.cmp(rhs, CmpOp::Le)
1177 }
1178
1179 pub fn clamp<T1: TensorOrScalar, T2: TensorOrScalar>(&self, min: T1, max: T2) -> Result<Self> {
1181 self.maximum(min)?.minimum(max)
1182 }
1183
1184 pub fn interpolate1d(&self, target_size: usize) -> Result<Self> {
1189 let (n, c, _l) = self.dims3()?;
1190 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest1D { arg, target_size });
1191 let storage = self
1192 .storage()
1193 .upsample_nearest1d(self.layout(), target_size)?;
1194 Ok(from_storage(storage, (n, c, target_size), op, false))
1195 }
1196
1197 pub fn upsample_nearest1d(&self, target_size: usize) -> Result<Self> {
1199 self.interpolate1d(target_size)
1200 }
1201
1202 pub fn interpolate2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1208 let (n, c, _h, _w) = self.dims4()?;
1209 let op = BackpropOp::new1(self, |arg| Op::UpsampleNearest2D {
1210 arg,
1211 target_h,
1212 target_w,
1213 });
1214 let storage = self
1215 .storage()
1216 .upsample_nearest2d(self.layout(), target_h, target_w)?;
1217 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1218 }
1219
1220 pub fn upsample_nearest2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1222 self.interpolate2d(target_h, target_w)
1223 }
1224
1225 pub fn upsample_bilinear2d(
1249 &self,
1250 target_h: usize,
1251 target_w: usize,
1252 align_corners: bool,
1253 ) -> Result<Self> {
1254 let (n, c, _h, _w) = self.dims4()?;
1255 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1256 arg,
1257 target_h,
1258 target_w,
1259 align_corners,
1260 });
1261 let storage = self.storage().upsample_bilinear2d(
1263 self.layout(),
1264 target_h,
1265 target_w,
1266 align_corners,
1267 None,
1268 None,
1269 )?;
1270 Ok(from_storage(storage, (n, c, target_h, target_w), op, false))
1271 }
1272
1273 pub fn upsample_bilinear2d_with_scale(
1297 &self,
1298 scale_h: f64,
1299 scale_w: f64,
1300 align_corners: bool,
1301 ) -> Result<Self> {
1302 let (n, c, height_in, width_in) = self.dims4()?;
1303
1304 let height_out = (height_in as f64 * scale_h).floor() as usize;
1306 let width_out = (width_in as f64 * scale_w).floor() as usize;
1307
1308 if height_in == height_out && width_in == width_out {
1310 return Ok(self.clone());
1311 }
1312
1313 let op = BackpropOp::new1(self, |arg| Op::UpsampleBilinear2D {
1314 arg,
1315 target_h: height_out,
1316 target_w: width_out,
1317 align_corners,
1318 });
1319
1320 let storage = self.storage().upsample_bilinear2d(
1323 self.layout(),
1324 height_out,
1325 width_out,
1326 align_corners,
1327 Some(scale_h),
1328 Some(scale_w),
1329 )?;
1330 Ok(from_storage(
1331 storage,
1332 (n, c, height_out, width_out),
1333 op,
1334 false,
1335 ))
1336 }
1337
1338 pub fn avg_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1345 let sz = sz.to_usize2();
1346 self.avg_pool2d_with_stride(sz, sz)
1347 }
1348
1349 pub fn avg_pool2d_with_stride<T: crate::ToUsize2>(
1352 &self,
1353 kernel_size: T,
1354 stride: T,
1355 ) -> Result<Self> {
1356 let kernel_size = kernel_size.to_usize2();
1357 let stride = stride.to_usize2();
1358 let (n, c, h, w) = self.dims4()?;
1359 if h < kernel_size.0 || w < kernel_size.1 {
1360 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1361 }
1362 let h_out = (h - kernel_size.0) / stride.0 + 1;
1364 let w_out = (w - kernel_size.1) / stride.1 + 1;
1365 let op = BackpropOp::new1(self, |arg| Op::AvgPool2D {
1366 arg,
1367 kernel_size,
1368 stride,
1369 });
1370 let storage = self
1371 .storage()
1372 .avg_pool2d(self.layout(), kernel_size, stride)?;
1373 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1374 }
1375
1376 pub fn max_pool2d<T: crate::ToUsize2>(&self, sz: T) -> Result<Self> {
1383 let sz = sz.to_usize2();
1384 self.max_pool2d_with_stride(sz, sz)
1385 }
1386
1387 pub fn max_pool2d_with_stride<T: crate::ToUsize2>(
1390 &self,
1391 kernel_size: T,
1392 stride: T,
1393 ) -> Result<Self> {
1394 let kernel_size = kernel_size.to_usize2();
1395 let stride = stride.to_usize2();
1396 let (n, c, h, w) = self.dims4()?;
1397 if h < kernel_size.0 || w < kernel_size.1 {
1398 bail!("kernel-size {kernel_size:?} is larger than the input size {h},{w}")
1399 }
1400 let h_out = (h - kernel_size.0) / stride.0 + 1;
1402 let w_out = (w - kernel_size.1) / stride.1 + 1;
1403 let op = BackpropOp::new1(self, |arg| Op::MaxPool2D {
1404 arg,
1405 kernel_size,
1406 stride,
1407 });
1408 let storage = self
1409 .storage()
1410 .max_pool2d(self.layout(), kernel_size, stride)?;
1411 Ok(from_storage(storage, (n, c, h_out, w_out), op, false))
1412 }
1413
1414 pub fn dot(&self, rhs: &Self) -> Result<Self> {
1430 if self.dims().len() != 1 || rhs.dims().len() != 1 {
1431 return Err(Error::ShapeMismatchBinaryOp {
1432 lhs: self.shape().clone(),
1433 rhs: rhs.shape().clone(),
1434 op: "dot",
1435 });
1436 }
1437
1438 (self * rhs).and_then(|ret| ret.sum_all())
1439 }
1440
1441 pub fn norm(&self) -> Result<Self> {
1454 if self.dtype().is_int() {
1455 bail!("norm not supported for integer dtypes");
1456 }
1457
1458 self.sqr().and_then(|x| x.sum_all()).and_then(|x| x.sqrt())
1459 }
1460
1461 pub fn mv(&self, rhs: &Self) -> Result<Self> {
1476 let lhs_dims = self.dims();
1478 let rhs_dims = rhs.dims();
1479 if lhs_dims.len() != 2 || rhs_dims.len() != 1 || lhs_dims[1] != rhs_dims[0] {
1480 return Err(Error::ShapeMismatchBinaryOp {
1481 lhs: self.shape().clone(),
1482 rhs: rhs.shape().clone(),
1483 op: "mv",
1484 });
1485 }
1486
1487 self.matmul(&rhs.unsqueeze(1)?)?.squeeze(1)
1489 }
1490
1491 pub fn matmul(&self, rhs: &Self) -> Result<Self> {
1500 let a_dims = self.shape().dims();
1501 let b_dims = rhs.shape().dims();
1502
1503 let dim = a_dims.len();
1504
1505 if dim < 2 || b_dims.len() != dim {
1506 Err(Error::ShapeMismatchBinaryOp {
1507 lhs: self.shape().clone(),
1508 rhs: rhs.shape().clone(),
1509 op: "matmul",
1510 }
1511 .bt())?
1512 }
1513
1514 let m = a_dims[dim - 2];
1515 let k = a_dims[dim - 1];
1516 let k2 = b_dims[dim - 2];
1517 let n = b_dims[dim - 1];
1518
1519 let c_shape = Shape::from(&a_dims[..dim - 2]).extend(&[m, n]);
1520 let batching: usize = a_dims[..dim - 2].iter().product();
1521 let batching_b: usize = b_dims[..dim - 2].iter().product();
1522 if k != k2 || batching != batching_b {
1523 Err(Error::ShapeMismatchBinaryOp {
1524 lhs: self.shape().clone(),
1525 rhs: rhs.shape().clone(),
1526 op: "matmul",
1527 }
1528 .bt())?
1529 }
1530 if c_shape.elem_count() == 0 || k == 0 {
1531 {
1532 let lhs_storage = self.storage();
1533 let rhs_storage = rhs.storage();
1534 lhs_storage.same_device(&rhs_storage, "matmul")?;
1535 lhs_storage.same_dtype(&rhs_storage, "matmul")?;
1536 }
1537
1538 let storage = self.device().zeros(&c_shape, self.dtype())?;
1539 let op = BackpropOp::new2(self, rhs, Op::Matmul);
1540 return Ok(from_storage(storage, c_shape, op, false));
1541 }
1542
1543 let storage = self.storage().matmul(
1544 &rhs.storage(),
1545 (batching, m, n, k),
1546 self.layout(),
1547 rhs.layout(),
1548 )?;
1549 let op = BackpropOp::new2(self, rhs, Op::Matmul);
1550 Ok(from_storage(storage, c_shape, op, false))
1551 }
1552
1553 pub fn broadcast_matmul(&self, rhs: &Self) -> Result<Self> {
1559 let lhs = self;
1560 let (l_shape, r_shape) = lhs.shape().broadcast_shape_matmul(rhs.shape())?;
1561 let l_broadcast = l_shape != *lhs.shape();
1562 let r_broadcast = r_shape != *rhs.shape();
1563 match (l_broadcast, r_broadcast) {
1565 (true, true) => lhs
1566 .broadcast_as(&l_shape)?
1567 .contiguous()?
1568 .matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1569 (false, true) if rhs.rank() == 2 && lhs.is_contiguous() => {
1575 let (lhs_dims, rhs_dims) = (lhs.dims(), rhs.dims());
1576 let (m, k) = (lhs_dims[lhs.rank() - 2], lhs_dims[lhs.rank() - 1]);
1577 let n = rhs_dims[1];
1578 let batch: usize = lhs_dims[..lhs.rank() - 2].iter().product();
1579 let mut out_dims = lhs_dims.to_vec();
1580 out_dims.pop();
1581 out_dims.push(n);
1582 lhs.reshape((batch * m, k))?.matmul(rhs)?.reshape(out_dims)
1583 }
1584 (false, true) => lhs.matmul(&rhs.broadcast_as(&r_shape)?.contiguous()?),
1585 (true, false) => lhs.broadcast_as(&l_shape)?.contiguous()?.matmul(rhs),
1586 (false, false) => lhs.matmul(rhs),
1587 }
1588 }
1589
1590 pub fn where_cond(&self, on_true: &Self, on_false: &Self) -> Result<Self> {
1594 let _shap = self.same_shape_binary_op(on_true, "where_cond")?;
1595 let shape = self.same_shape_binary_op(on_false, "where_cond")?;
1596 let storage = self.storage().where_cond(
1597 self.layout(),
1598 &on_true.storage(),
1599 on_true.layout(),
1600 &on_false.storage(),
1601 on_false.layout(),
1602 )?;
1603 let op = BackpropOp::new3(self, on_true, on_false, Op::WhereCond);
1604 Ok(from_storage(storage, shape, op, false))
1605 }
1606
1607 pub fn embedding(&self, ids: &Self) -> Result<Self> {
1627 if self.rank() != 2 || ids.rank() != 1 {
1628 Err(Error::ShapeMismatchBinaryOp {
1629 lhs: self.shape().clone(),
1630 rhs: ids.shape().clone(),
1631 op: "embedding",
1632 }
1633 .bt())?
1634 }
1635 self.index_select(ids, 0)
1636 }
1637
1638 fn scatter_checks(&self, indexes: &Self, source: &Self, dim: usize) -> Result<()> {
1639 let source_dims = source.dims();
1640 let self_dims = self.dims();
1641 let mismatch = if source_dims.len() != self_dims.len() {
1642 true
1643 } else {
1644 let mut mismatch = false;
1645 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1646 if i != dim && d1 != d2 {
1647 mismatch = true;
1648 break;
1649 }
1650 }
1651 mismatch
1652 };
1653 if mismatch {
1654 Err(Error::ShapeMismatchBinaryOp {
1655 op: "scatter (self, src)",
1656 lhs: self.shape().clone(),
1657 rhs: source.shape().clone(),
1658 }
1659 .bt())?
1660 }
1661 if indexes.dims() != source.dims() {
1662 Err(Error::ShapeMismatchBinaryOp {
1663 op: "scatter (indexes, src)",
1664 lhs: indexes.shape().clone(),
1665 rhs: source.shape().clone(),
1666 }
1667 .bt())?
1668 }
1669 Ok(())
1670 }
1671
1672 pub fn scatter<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1673 let dim = dim.to_index(self.shape(), "scatter")?;
1674 self.scatter_checks(indexes, source, dim)?;
1675 let shape = self.shape();
1676 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1677 self.storage()
1678 .copy_strided_src(&mut storage, 0, self.layout())?;
1679 let layout = Layout::contiguous(shape);
1680 storage.scatter_set(
1681 &layout,
1682 &indexes.storage(),
1683 indexes.layout(),
1684 &source.storage(),
1685 source.layout(),
1686 dim,
1687 )?;
1688 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1689 Op::Scatter(t1, t2, t3, dim)
1690 });
1691 Ok(from_storage(storage, self.shape(), op, false))
1692 }
1693
1694 pub fn scatter_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1695 if self.same_storage(source) {
1696 crate::bail!("cannot use slice_set when self and src share their storage")
1697 }
1698 let dim = dim.to_index(self.shape(), "scatter-set")?;
1699 self.scatter_checks(indexes, source, dim)?;
1700 self.storage_mut().scatter_set(
1701 self.layout(),
1702 &indexes.storage(),
1703 indexes.layout(),
1704 &source.storage(),
1705 source.layout(),
1706 dim,
1707 )?;
1708 Ok(())
1709 }
1710
1711 pub fn scatter_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1712 let dim = dim.to_index(self.shape(), "scatter-add")?;
1713 self.scatter_checks(indexes, source, dim)?;
1714 let shape = self.shape();
1715 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
1716 self.storage()
1717 .copy_strided_src(&mut storage, 0, self.layout())?;
1718 let layout = Layout::contiguous(shape);
1719 storage.scatter_add(
1720 &layout,
1721 &indexes.storage(),
1722 indexes.layout(),
1723 &source.storage(),
1724 source.layout(),
1725 dim,
1726 )?;
1727 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1728 Op::ScatterAdd(t1, t2, t3, dim)
1729 });
1730 Ok(from_storage(storage, self.shape(), op, false))
1731 }
1732
1733 pub fn scatter_add_set<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<()> {
1734 if self.same_storage(source) {
1735 crate::bail!("cannot use slice_set when self and src share their storage")
1736 }
1737 let dim = dim.to_index(self.shape(), "scatter-add-set")?;
1738 self.scatter_checks(indexes, source, dim)?;
1739 self.storage_mut().scatter_add(
1740 self.layout(),
1741 &indexes.storage(),
1742 indexes.layout(),
1743 &source.storage(),
1744 source.layout(),
1745 dim,
1746 )?;
1747 Ok(())
1748 }
1749
1750 pub fn slice_scatter<D: Dim>(&self, src: &Self, dim: D, start: usize) -> Result<Self> {
1752 let dim = dim.to_index(self.shape(), "slice-scatter")?;
1753 if dim == 0 {
1754 self.slice_scatter0(src, start)
1755 } else {
1756 self.transpose(0, dim)?
1758 .slice_scatter0(&src.transpose(0, dim)?, start)?
1759 .transpose(0, dim)
1760 }
1761 }
1762
1763 pub fn slice_scatter0(&self, src: &Self, start: usize) -> Result<Self> {
1765 if self.dtype() != src.dtype() {
1766 Err(Error::DTypeMismatchBinaryOp {
1767 lhs: self.dtype(),
1768 rhs: src.dtype(),
1769 op: "slice-scatter",
1770 }
1771 .bt())?
1772 }
1773 if self.device().location() != src.device.location() {
1774 Err(Error::DeviceMismatchBinaryOp {
1775 lhs: self.device().location(),
1776 rhs: src.device().location(),
1777 op: "slice-scatter",
1778 }
1779 .bt())?
1780 }
1781 if self.rank() != src.rank() {
1782 Err(Error::UnexpectedNumberOfDims {
1783 expected: self.rank(),
1784 got: src.rank(),
1785 shape: src.shape().clone(),
1786 }
1787 .bt())?
1788 }
1789 let shape_ok =
1790 self.dims()
1791 .iter()
1792 .zip(src.dims().iter())
1793 .enumerate()
1794 .all(|(dim_idx, (&d1, &d2))| {
1795 if 0 == dim_idx {
1796 d2 + start <= d1
1797 } else {
1798 d1 == d2
1799 }
1800 });
1801 if !shape_ok {
1802 Err(Error::ShapeMismatchBinaryOp {
1803 op: "slice-scatter (self, src)",
1804 lhs: self.shape().clone(),
1805 rhs: src.shape().clone(),
1806 }
1807 .bt())?
1808 }
1809 let mut storage = unsafe { self.device().alloc_uninit(self.shape(), self.dtype())? };
1810 self.storage()
1811 .copy_strided_src(&mut storage, 0, self.layout())?;
1812 let offset = start * src.dims()[1..].iter().product::<usize>();
1813 src.storage()
1814 .copy_strided_src(&mut storage, offset, src.layout())?;
1815 let op = BackpropOp::new2(self, src, |t1, t2| Op::SliceScatter0(t1, t2, start));
1816 Ok(from_storage(storage, self.shape(), op, false))
1817 }
1818
1819 pub fn index_add<D: Dim>(&self, indexes: &Self, source: &Self, dim: D) -> Result<Self> {
1821 let dim = dim.to_index(self.shape(), "index-add")?;
1822 let source_dims = source.dims();
1823 let self_dims = self.dims();
1824 let mismatch = if source_dims.len() != self_dims.len() {
1825 true
1826 } else {
1827 let mut mismatch = false;
1828 for (i, (&d1, &d2)) in self_dims.iter().zip(source_dims.iter()).enumerate() {
1829 if i != dim && d1 != d2 {
1830 mismatch = true;
1831 break;
1832 }
1833 }
1834 mismatch
1835 };
1836 if mismatch {
1837 Err(Error::ShapeMismatchBinaryOp {
1838 op: "index-add (self, source)",
1839 lhs: self.shape().clone(),
1840 rhs: source.shape().clone(),
1841 }
1842 .bt())?
1843 }
1844 let indexes_len = indexes.dims1()?;
1848 if source_dims[dim] != indexes_len {
1849 Err(Error::ShapeMismatchBinaryOp {
1850 op: "index-add (ids, source))",
1851 lhs: indexes.shape().clone(),
1852 rhs: source.shape().clone(),
1853 }
1854 .bt())?
1855 }
1856 let storage = self.storage().index_add(
1857 self.layout(),
1858 &indexes.storage(),
1859 indexes.layout(),
1860 &source.storage(),
1861 source.layout(),
1862 dim,
1863 )?;
1864 let op = BackpropOp::new3(self, indexes, source, |t1, t2, t3| {
1865 Op::IndexAdd(t1, t2, t3, dim)
1866 });
1867 Ok(from_storage(storage, self.shape(), op, false))
1868 }
1869
1870 pub fn gather<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1882 let dim = dim.to_index(self.shape(), "gather")?;
1883
1884 let self_dims = self.dims();
1885 let indexes_dims = indexes.dims();
1886 let mismatch = if indexes_dims.len() != self_dims.len() {
1887 true
1888 } else {
1889 let mut mismatch = false;
1890 for (i, (&d1, &d2)) in self_dims.iter().zip(indexes_dims.iter()).enumerate() {
1891 if i != dim && d1 < d2 {
1892 mismatch = true;
1893 break;
1894 }
1895 }
1896 mismatch
1897 };
1898 if mismatch {
1899 Err(Error::ShapeMismatchBinaryOp {
1900 op: "gather",
1901 lhs: self.shape().clone(),
1902 rhs: indexes.shape().clone(),
1903 }
1904 .bt())?
1905 }
1906 let storage =
1907 self.storage()
1908 .gather(self.layout(), &indexes.storage(), indexes.layout(), dim)?;
1909 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::Gather(t1, t2, dim));
1910 Ok(from_storage(storage, indexes.shape(), op, false))
1911 }
1912
1913 pub fn index_select<D: Dim>(&self, indexes: &Self, dim: D) -> Result<Self> {
1921 let dim = dim.to_index(self.shape(), "index-select")?;
1922 let indexes_len = match indexes.dims() {
1923 [l] => *l,
1924 _ => Err(Error::ShapeMismatchBinaryOp {
1925 lhs: self.shape().clone(),
1926 rhs: indexes.shape().clone(),
1927 op: "index-select",
1928 }
1929 .bt())?,
1930 };
1931 let storage = self.storage().index_select(
1932 &indexes.storage(),
1933 self.layout(),
1934 indexes.layout(),
1935 dim,
1936 )?;
1937 let mut dims = self.dims().to_vec();
1938 dims[dim] = indexes_len;
1939 let op = BackpropOp::new2(self, indexes, |t1, t2| Op::IndexSelect(t1, t2, dim));
1940 Ok(from_storage(storage, dims, op, false))
1941 }
1942
1943 pub fn strided_index(&self) -> crate::StridedIndex<'_> {
1946 self.layout.strided_index()
1947 }
1948
1949 pub fn strided_blocks(&self) -> crate::StridedBlocks<'_> {
1954 self.layout.strided_blocks()
1955 }
1956
1957 pub fn to_vec1<S: crate::WithDType>(&self) -> Result<Vec<S>> {
1959 if self.rank() != 1 {
1960 Err(Error::UnexpectedNumberOfDims {
1961 expected: 1,
1962 got: self.rank(),
1963 shape: self.shape().clone(),
1964 }
1965 .bt())?
1966 }
1967 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1968 let data = S::cpu_storage_as_slice(cpu_storage)?;
1969 let data = match self.layout.contiguous_offsets() {
1970 Some((o1, o2)) => data[o1..o2].to_vec(),
1971 None => self.strided_index().map(|i| data[i]).collect(),
1972 };
1973 Ok::<Vec<_>, Error>(data)
1974 };
1975 match &*self.storage() {
1976 Storage::Cpu(storage) => from_cpu_storage(storage),
1977 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1978 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
1979 }
1980 }
1981
1982 pub fn to_vec2<S: crate::WithDType>(&self) -> Result<Vec<Vec<S>>> {
1984 let (dim1, dim2) = self.dims2()?;
1985 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
1986 let data = S::cpu_storage_as_slice(cpu_storage)?;
1987 let mut rows = vec![];
1988 match self.layout.contiguous_offsets() {
1989 Some((o1, o2)) => {
1990 let data = &data[o1..o2];
1991 for idx_row in 0..dim1 {
1992 rows.push(data[idx_row * dim2..(idx_row + 1) * dim2].to_vec())
1993 }
1994 }
1995 None => {
1996 let mut src_index = self.strided_index();
1997 for _idx_row in 0..dim1 {
1998 let row = (0..dim2).map(|_| data[src_index.next().unwrap()]).collect();
1999 rows.push(row)
2000 }
2001 assert!(src_index.next().is_none());
2002 }
2003 }
2004 Ok(rows)
2005 };
2006 match &*self.storage() {
2007 Storage::Cpu(storage) => from_cpu_storage(storage),
2008 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2009 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2010 }
2011 }
2012
2013 pub fn to_vec3<S: crate::WithDType>(&self) -> Result<Vec<Vec<Vec<S>>>> {
2015 let (dim1, dim2, dim3) = self.dims3()?;
2016 let from_cpu_storage = |cpu_storage: &crate::CpuStorage| {
2017 let data = S::cpu_storage_as_slice(cpu_storage)?;
2018 let mut top_rows = vec![];
2019 match self.layout.contiguous_offsets() {
2020 Some((o1, o2)) => {
2021 let data = &data[o1..o2];
2022 let dim23 = dim2 * dim3;
2023 for idx1 in 0..dim1 {
2024 let data = &data[idx1 * dim23..(idx1 + 1) * dim23];
2025 let mut rows = vec![];
2026 for idx2 in 0..dim2 {
2027 rows.push(data[idx2 * dim3..(idx2 + 1) * dim3].to_vec())
2028 }
2029 top_rows.push(rows);
2030 }
2031 }
2032 None => {
2033 let mut src_index = self.strided_index();
2034 for _idx in 0..dim1 {
2035 let mut rows = vec![];
2036 for _jdx in 0..dim2 {
2037 let row = (0..dim3).map(|_| data[src_index.next().unwrap()]).collect();
2038 rows.push(row)
2039 }
2040 top_rows.push(rows);
2041 }
2042 assert!(src_index.next().is_none());
2043 }
2044 }
2045 Ok(top_rows)
2046 };
2047 match &*self.storage() {
2048 Storage::Cpu(storage) => from_cpu_storage(storage),
2049 Storage::Cuda(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2050 Storage::Metal(storage) => from_cpu_storage(&storage.to_cpu_storage()?),
2051 }
2052 }
2053
2054 pub fn dtype(&self) -> DType {
2056 self.dtype
2057 }
2058
2059 pub fn device(&self) -> &Device {
2061 &self.device
2062 }
2063
2064 pub fn shape(&self) -> &Shape {
2066 self.layout().shape()
2067 }
2068
2069 pub fn dims(&self) -> &[usize] {
2071 self.shape().dims()
2072 }
2073
2074 pub fn dim<D: Dim>(&self, dim: D) -> Result<usize> {
2076 let dim = dim.to_index(self.shape(), "dim")?;
2077 Ok(self.dims()[dim])
2078 }
2079
2080 pub fn layout(&self) -> &Layout {
2083 &self.layout
2084 }
2085
2086 pub fn stride(&self) -> &[usize] {
2087 self.layout.stride()
2088 }
2089
2090 pub fn rank(&self) -> usize {
2092 self.shape().rank()
2093 }
2094
2095 pub fn elem_count(&self) -> usize {
2097 self.shape().elem_count()
2098 }
2099
2100 pub fn id(&self) -> TensorId {
2102 self.id
2103 }
2104
2105 pub fn is_variable(&self) -> bool {
2108 self.is_variable
2109 }
2110
2111 pub(crate) fn op(&self) -> &Option<Op> {
2112 &self.op
2113 }
2114
2115 pub fn max_all(&self) -> Result<Tensor> {
2126 if self.rank() == 0 {
2127 Ok(self.clone())
2128 } else {
2129 self.flatten_all()?.max(0)
2130 }
2131 }
2132
2133 pub fn min_all(&self) -> Result<Tensor> {
2144 if self.rank() == 0 {
2145 Ok(self.clone())
2146 } else {
2147 self.flatten_all()?.min(0)
2148 }
2149 }
2150
2151 pub fn sum_all(&self) -> Result<Tensor> {
2162 let dims: Vec<_> = (0..self.rank()).collect();
2163 self.sum(dims)
2164 }
2165
2166 pub fn mean_all(&self) -> Result<Tensor> {
2167 self.sum_all()? / self.elem_count() as f64
2168 }
2169
2170 fn flatten_<D1: Dim, D2: Dim>(
2171 &self,
2172 start_dim: Option<D1>,
2173 end_dim: Option<D2>,
2174 ) -> Result<Tensor> {
2175 if self.rank() == 0 {
2176 self.reshape(1)
2177 } else {
2178 let start_dim = match start_dim {
2179 None => 0,
2180 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2181 };
2182 let end_dim = match end_dim {
2183 None => self.rank() - 1,
2184 Some(dim) => dim.to_index(self.shape(), "flatten")?,
2185 };
2186 if start_dim < end_dim {
2187 let dims = self.dims();
2188 let mut dst_dims = dims[..start_dim].to_vec();
2189 dst_dims.push(dims[start_dim..end_dim + 1].iter().product::<usize>());
2190 if end_dim + 1 < dims.len() {
2191 dst_dims.extend(&dims[end_dim + 1..]);
2192 }
2193 self.reshape(dst_dims)
2194 } else {
2195 Ok(self.clone())
2196 }
2197 }
2198 }
2199
2200 pub fn flatten<D1: Dim, D2: Dim>(&self, start_dim: D1, end_dim: D2) -> Result<Tensor> {
2203 self.flatten_(Some(start_dim), Some(end_dim))
2204 }
2205
2206 pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Tensor> {
2208 self.flatten_(None::<usize>, Some(end_dim))
2209 }
2210
2211 pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Tensor> {
2214 self.flatten_(Some(start_dim), None::<usize>)
2215 }
2216
2217 pub fn flatten_all(&self) -> Result<Tensor> {
2227 self.flatten_(None::<usize>, None::<usize>)
2228 }
2229
2230 pub fn get(&self, i: usize) -> Result<Tensor> {
2242 let dims = self.dims();
2243 if dims.is_empty() {
2244 Ok(self.clone())
2245 } else {
2246 self.narrow(0, i, 1)?.reshape(&dims[1..])
2247 }
2248 }
2249
2250 pub fn get_on_dim<D: Dim>(&self, dim: D, index: usize) -> Result<Tensor> {
2264 let dim = dim.to_index(self.shape(), "get_on_dim")?;
2265 self.narrow(dim, index, 1)?.squeeze(dim)
2266 }
2267
2268 pub fn t(&self) -> Result<Tensor> {
2279 let rank = self.rank();
2280 if rank < 2 {
2281 Err(Error::UnexpectedNumberOfDims {
2282 expected: 2,
2283 got: rank,
2284 shape: self.shape().clone(),
2285 }
2286 .bt())?
2287 }
2288 self.transpose(rank - 2, rank - 1)
2289 }
2290
2291 pub fn transpose<D1: Dim, D2: Dim>(&self, dim1: D1, dim2: D2) -> Result<Tensor> {
2294 let dim1 = dim1.to_index(self.shape(), "transpose")?;
2295 let dim2 = dim2.to_index(self.shape(), "transpose")?;
2296 if dim1 == dim2 {
2297 return Ok(self.clone());
2298 }
2299 let op = BackpropOp::new1(self, |t| Op::Transpose(t, dim1, dim2));
2300 let tensor_ = Tensor_ {
2301 id: TensorId::new(),
2302 storage: self.storage.clone(),
2303 layout: self.layout.transpose(dim1, dim2)?,
2304 op,
2305 is_variable: false,
2306 dtype: self.dtype,
2307 device: self.device.clone(),
2308 };
2309 Ok(Tensor(Arc::new(tensor_)))
2310 }
2311
2312 pub fn permute<D: Dims>(&self, dims: D) -> Result<Tensor> {
2324 let dims = dims.to_indexes(self.shape(), "permute")?;
2325 let is_permutation =
2327 dims.len() == self.rank() && (0..dims.len()).all(|i| dims.contains(&i));
2328 if !is_permutation {
2329 bail!(
2330 "dimension mismatch in permute, tensor {:?}, dims: {:?}",
2331 self.dims(),
2332 dims
2333 )
2334 }
2335 let op = BackpropOp::new1(self, |t| Op::Permute(t, dims.clone()));
2336 let tensor_ = Tensor_ {
2337 id: TensorId::new(),
2338 storage: self.storage.clone(),
2339 layout: self.layout.permute(&dims)?,
2340 op,
2341 is_variable: false,
2342 dtype: self.dtype,
2343 device: self.device.clone(),
2344 };
2345 Ok(Tensor(Arc::new(tensor_)))
2346 }
2347
2348 pub fn is_contiguous(&self) -> bool {
2350 self.layout.is_contiguous()
2351 }
2352
2353 pub fn is_fortran_contiguous(&self) -> bool {
2355 self.layout.is_fortran_contiguous()
2356 }
2357
2358 pub fn copy(&self) -> Result<Tensor> {
2361 let op = BackpropOp::new1(self, Op::Copy);
2362 let tensor_ = Tensor_ {
2363 id: TensorId::new(),
2364 storage: Arc::new(RwLock::new(self.storage().try_clone(self.layout())?)),
2365 layout: self.layout.clone(),
2366 op,
2367 is_variable: false,
2368 dtype: self.dtype,
2369 device: self.device.clone(),
2370 };
2371 Ok(Tensor(Arc::new(tensor_)))
2372 }
2373
2374 pub fn detach(&self) -> Tensor {
2379 if self.op.is_none() && !self.is_variable {
2380 self.clone()
2381 } else {
2382 let tensor_ = Tensor_ {
2383 id: TensorId::new(),
2384 storage: self.storage.clone(),
2385 layout: self.layout.clone(),
2386 op: BackpropOp::none(),
2387 is_variable: false,
2388 dtype: self.dtype,
2389 device: self.device.clone(),
2390 };
2391 Tensor(Arc::new(tensor_))
2392 }
2393 }
2394
2395 pub fn to_device(&self, device: &Device) -> Result<Tensor> {
2397 if self.device().same_device(device) {
2398 Ok(self.clone())
2399 } else {
2400 let storage = match (&*self.storage(), device) {
2401 (Storage::Cpu(storage), Device::Cuda(cuda)) => {
2402 Storage::Cuda(cuda.storage_from_cpu_storage(storage)?)
2403 }
2404 (Storage::Cpu(storage), Device::Metal(metal)) => {
2405 Storage::Metal(metal.storage_from_cpu_storage(storage)?)
2406 }
2407 (Storage::Cuda(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2408 (Storage::Metal(storage), Device::Cpu) => Storage::Cpu(storage.to_cpu_storage()?),
2409 (Storage::Cuda(storage), Device::Cuda(cuda)) => {
2410 let dst_storage = storage.transfer_to_device(cuda)?;
2412 Storage::Cuda(dst_storage)
2413 }
2414 (Storage::Cpu(storage), Device::Cpu) => Storage::Cpu(storage.clone()),
2415 _ => {
2416 bail!(
2417 "not implemented yet, self.device: {:?}, device: {:?}",
2418 self.device(),
2419 device
2420 )
2421 }
2422 };
2423 let op = BackpropOp::new1(self, Op::ToDevice);
2424 let tensor_ = Tensor_ {
2425 id: TensorId::new(),
2426 storage: Arc::new(RwLock::new(storage)),
2427 layout: self.layout.clone(),
2428 op,
2429 is_variable: false,
2430 dtype: self.dtype,
2431 device: device.clone(),
2432 };
2433 Ok(Tensor(Arc::new(tensor_)))
2434 }
2435 }
2436
2437 pub fn broadcast_left<S: Into<Shape>>(&self, left_shape: S) -> Result<Self> {
2440 let left_shape = left_shape.into();
2441 let mut dims = left_shape.into_dims();
2442 dims.extend(self.dims());
2443 self.broadcast_as(dims)
2444 }
2445
2446 pub fn broadcast_as<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2454 let tensor_ = Tensor_ {
2455 id: TensorId::new(),
2456 storage: self.storage.clone(),
2457 layout: self.layout.broadcast_as(shape)?,
2458 op: BackpropOp::new1(self, Op::Broadcast),
2459 is_variable: false,
2460 dtype: self.dtype,
2461 device: self.device.clone(),
2462 };
2463 Ok(Tensor(Arc::new(tensor_)))
2464 }
2465
2466 pub fn expand<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2468 self.broadcast_as(shape)
2469 }
2470
2471 pub fn to_dtype(&self, dtype: DType) -> Result<Self> {
2482 if self.dtype() == dtype {
2483 Ok(self.clone())
2484 } else {
2485 let shape = self.shape();
2486 let storage = self.storage().to_dtype(self.layout(), dtype)?;
2487 let op = BackpropOp::new1(self, Op::ToDType);
2488 Ok(from_storage(storage, shape.clone(), op, false))
2489 }
2490 }
2491
2492 pub fn contiguous(&self) -> Result<Tensor> {
2495 if self.is_contiguous() {
2496 Ok(self.clone())
2497 } else {
2498 let shape = self.shape();
2499 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2500 self.storage()
2501 .copy_strided_src(&mut storage, 0, self.layout())?;
2502 let op = BackpropOp::new1(self, Op::Copy);
2503 Ok(from_storage(storage, shape.clone(), op, false))
2504 }
2505 }
2506
2507 pub fn force_contiguous(&self) -> Result<Tensor> {
2509 let shape = self.shape();
2510 let mut storage = unsafe { self.device().alloc_uninit(shape, self.dtype())? };
2511 self.storage()
2512 .copy_strided_src(&mut storage, 0, self.layout())?;
2513 let op = BackpropOp::new1(self, Op::Copy);
2514 Ok(from_storage(storage, shape.clone(), op, false))
2515 }
2516
2517 pub(crate) fn make_var(&self) -> Result<Tensor> {
2520 let shape = self.shape().clone();
2521 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2522 self.storage()
2523 .copy_strided_src(&mut storage, 0, self.layout())?;
2524 Ok(from_storage(storage, shape, BackpropOp::none(), true))
2525 }
2526
2527 pub fn reshape<S: ShapeWithOneHole>(&self, s: S) -> Result<Tensor> {
2552 let shape = s.into_shape(self.elem_count())?;
2553 if shape.elem_count() != self.elem_count() {
2554 return Err(Error::ShapeMismatchBinaryOp {
2555 lhs: self.shape().clone(),
2556 rhs: shape,
2557 op: "reshape",
2558 }
2559 .bt());
2560 }
2561 let op = BackpropOp::new1(self, Op::Reshape);
2562 if self.is_contiguous() {
2563 let tensor_ = Tensor_ {
2564 id: TensorId::new(),
2565 storage: self.storage.clone(),
2566 layout: Layout::contiguous_with_offset(shape, self.layout.start_offset()),
2567 op,
2568 is_variable: false,
2569 dtype: self.dtype,
2570 device: self.device.clone(),
2571 };
2572 Ok(Tensor(Arc::new(tensor_)))
2573 } else {
2574 let mut storage = unsafe { self.device().alloc_uninit(&shape, self.dtype())? };
2575 self.storage()
2576 .copy_strided_src(&mut storage, 0, self.layout())?;
2577 Ok(from_storage(storage, shape, op, false))
2578 }
2579 }
2580
2581 pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
2595 let dims = self.dims();
2598 let dim = dim.to_index(self.shape(), "squeeze")?;
2599 if dims[dim] == 1 {
2600 let mut dims = dims.to_vec();
2601 let mut strides = self.stride().to_vec();
2602 dims.remove(dim);
2603 strides.remove(dim);
2604 let tensor_ = Tensor_ {
2605 id: TensorId::new(),
2606 storage: self.storage.clone(),
2607 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2608 op: BackpropOp::new1(self, Op::Reshape),
2609 is_variable: false,
2610 dtype: self.dtype,
2611 device: self.device.clone(),
2612 };
2613 Ok(Tensor(Arc::new(tensor_)))
2614 } else {
2615 Ok(self.clone())
2616 }
2617 }
2618
2619 pub fn unsqueeze<D: Dim>(&self, dim: D) -> Result<Self> {
2633 let mut dims = self.dims().to_vec();
2634 let mut strides = self.stride().to_vec();
2635 let dim = dim.to_index_plus_one(self.shape(), "unsqueeze")?;
2636 dims.insert(dim, 1);
2638 let stride = if dim < strides.len() { strides[dim] } else { 1 };
2641 strides.insert(dim, stride);
2642 let tensor_ = Tensor_ {
2643 id: TensorId::new(),
2644 storage: self.storage.clone(),
2645 layout: Layout::new(dims.into(), strides, self.layout.start_offset()),
2646 op: BackpropOp::new1(self, Op::Reshape),
2647 is_variable: false,
2648 dtype: self.dtype,
2649 device: self.device.clone(),
2650 };
2651 Ok(Tensor(Arc::new(tensor_)))
2652 }
2653
2654 pub fn stack<A: AsRef<Tensor>, D: Dim>(args: &[A], dim: D) -> Result<Self> {
2671 if args.is_empty() {
2672 Err(Error::OpRequiresAtLeastOneTensor { op: "stack" }.bt())?
2673 }
2674 let dim = dim.to_index_plus_one(args[0].as_ref().shape(), "stack")?;
2675 let args = args
2676 .iter()
2677 .map(|t| t.as_ref().unsqueeze(dim))
2678 .collect::<Result<Vec<_>>>()?;
2679 Self::cat(&args, dim)
2680 }
2681
2682 pub fn pad_with_zeros<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2685 if left == 0 && right == 0 {
2686 Ok(self.clone())
2687 } else if left == 0 {
2688 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2689 let mut dims = self.dims().to_vec();
2690 dims[dim] = right;
2691 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2692 Tensor::cat(&[self, &right], dim)
2693 } else if right == 0 {
2694 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2695 let mut dims = self.dims().to_vec();
2696 dims[dim] = left;
2697 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2698 Tensor::cat(&[&left, self], dim)
2699 } else {
2700 let dim = dim.to_index(self.shape(), "pad_with_zeros")?;
2701 let mut dims = self.dims().to_vec();
2702 dims[dim] = left;
2703 let left = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2704 dims[dim] = right;
2705 let right = Tensor::zeros(dims.as_slice(), self.dtype, self.device())?;
2706 Tensor::cat(&[&left, self, &right], dim)
2707 }
2708 }
2709
2710 pub fn pad_with_same<D: Dim>(&self, dim: D, left: usize, right: usize) -> Result<Self> {
2713 if left == 0 && right == 0 {
2714 Ok(self.clone())
2715 } else if self.elem_count() == 0 {
2716 bail!("cannot use pad_with_same on an empty tensor")
2717 } else if left == 0 {
2718 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2719 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2720 let mut v = vec![self];
2721 for _ in 0..right {
2722 v.push(&r)
2723 }
2724 Tensor::cat(&v, dim)
2725 } else if right == 0 {
2726 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2727 let l = self.narrow(dim, 0, 1)?;
2728 let mut v = vec![];
2729 for _ in 0..left {
2730 v.push(&l)
2731 }
2732 v.push(self);
2733 Tensor::cat(&v, dim)
2734 } else {
2735 let dim = dim.to_index(self.shape(), "pad_with_same")?;
2736 let l = self.narrow(dim, 0, 1)?;
2737 let r = self.narrow(dim, self.dim(dim)? - 1, 1)?;
2738 let mut v = vec![];
2739 for _ in 0..left {
2740 v.push(&l)
2741 }
2742 v.push(self);
2743 for _ in 0..right {
2744 v.push(&r)
2745 }
2746 Tensor::cat(&v, dim)
2747 }
2748 }
2749
2750 pub fn apply<M: crate::Module>(&self, m: &M) -> Result<Self> {
2752 m.forward(self)
2753 }
2754
2755 pub fn apply_t<M: crate::ModuleT>(&self, m: &M, train: bool) -> Result<Self> {
2757 m.forward_t(self, train)
2758 }
2759
2760 pub(crate) fn storage(&self) -> StorageRef<'_> {
2763 self.storage.read_recursive()
2764 }
2765
2766 pub(crate) fn storage_mut(&self) -> StorageMutRef<'_> {
2768 self.storage.write()
2769 }
2770
2771 pub(crate) fn storage_mut_and_layout(&self) -> (StorageMutRef<'_>, &Layout) {
2774 let storage = self.storage.write();
2775 (storage, &self.layout)
2776 }
2777
2778 pub fn storage_and_layout(&self) -> (StorageRef<'_>, &Layout) {
2780 let storage = self.storage.read();
2781 (storage, &self.layout)
2782 }
2783
2784 #[inline]
2786 pub(crate) fn storage_key(&self) -> usize {
2787 let lock: &RwLock<Storage> = self.storage.as_ref();
2788 std::ptr::from_ref(lock).addr()
2789 }
2790
2791 #[inline]
2793 pub(crate) fn same_storage(&self, rhs: &Self) -> bool {
2794 self.storage_key() == rhs.storage_key()
2795 }
2796
2797 pub fn normalize_axis(&self, axis: i64) -> Result<usize> {
2800 let rank = self.rank() as i64;
2801 if rank <= axis {
2802 bail!("axis {axis} is too large, tensor rank {rank}")
2803 } else if 0 <= axis {
2804 Ok(axis as usize)
2805 } else {
2806 let naxis = rank + axis;
2807 if naxis < 0 {
2808 bail!("axis {axis} is too small, tensor rank {rank}")
2809 }
2810 Ok(naxis as usize)
2811 }
2812 }
2813
2814 pub fn tril2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2816 let t = Tensor::arange(0u32, n as u32, device)?;
2817 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2818 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2819 t1.le(&t2)?.to_dtype(dtype)
2820 }
2821
2822 pub fn triu2(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2824 let t = Tensor::arange(0u32, n as u32, device)?;
2825 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2826 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2827 t1.ge(&t2)?.to_dtype(dtype)
2828 }
2829
2830 pub fn eye(n: usize, dtype: DType, device: &Device) -> Result<Self> {
2832 let t = Tensor::arange(0u32, n as u32, device)?;
2833 let t1 = t.reshape((1, n))?.broadcast_as((n, n))?;
2834 let t2 = t.reshape((n, 1))?.broadcast_as((n, n))?;
2835 t1.eq(&t2)?.to_dtype(dtype)
2836 }
2837
2838 pub fn cumsum<D: Dim>(&self, dim: D) -> Result<Self> {
2843 let dim = dim.to_index(self.shape(), "cumsum")?;
2844 let rank = self.rank();
2845 if rank == 0 {
2846 return Ok(self.clone());
2847 }
2848 let n_axis = self.dim(dim)?;
2849 let triu = Tensor::triu2(n_axis, self.dtype(), self.device())?;
2850 if rank == 1 {
2851 self.unsqueeze(0)?.matmul(&triu)?.squeeze(0)
2852 } else {
2853 let last = rank - 1;
2854 let t = self.transpose(dim, last)?;
2855 let t = t.broadcast_matmul(&triu)?;
2856 t.transpose(dim, last)
2857 }
2858 }
2859
2860 pub fn slice_assign<D: std::ops::RangeBounds<usize>>(
2863 &self,
2864 ranges: &[D],
2865 src: &Tensor,
2866 ) -> Result<Self> {
2867 let src_dims = src.dims();
2868 let self_dims = self.dims();
2869 if self_dims.len() != src_dims.len() {
2870 bail!(
2871 "slice-assign requires input with the same rank {} <> {}",
2872 self_dims.len(),
2873 src_dims.len()
2874 )
2875 }
2876 if self_dims.len() != ranges.len() {
2877 bail!(
2878 "slice-assign requires input with the same rank as there are ranges {} <> {}",
2879 self_dims.len(),
2880 ranges.len()
2881 )
2882 }
2883 let mut src = src.clone();
2884 let mut mask = Self::ones(src.shape(), DType::U8, src.device())?;
2885 for (i, range) in ranges.iter().enumerate() {
2886 let start_included = match range.start_bound() {
2887 std::ops::Bound::Unbounded => 0,
2888 std::ops::Bound::Included(v) => *v,
2889 std::ops::Bound::Excluded(v) => *v + 1,
2890 };
2891 let end_excluded = match range.end_bound() {
2892 std::ops::Bound::Unbounded => self_dims[i],
2893 std::ops::Bound::Included(v) => *v + 1,
2894 std::ops::Bound::Excluded(v) => *v,
2895 };
2896 if end_excluded <= start_included {
2897 bail!("slice-assign: empty range for dim {i}, {start_included} {end_excluded}")
2898 }
2899 if self_dims[i] < end_excluded {
2900 bail!(
2901 "slice-assign: upper bound is out of range for dim {i}, {end_excluded} {}",
2902 self_dims[i]
2903 )
2904 }
2905 if end_excluded - start_included != src_dims[i] {
2906 bail!(
2907 "slice-assign: the range for dim {i} ({start_included}..{end_excluded}) does not match the size of src {}", src_dims[i]
2908 )
2909 }
2910 src = src.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?;
2911 mask = mask.pad_with_zeros(i, start_included, self_dims[i] - end_excluded)?
2912 }
2913 mask.where_cond(&src, self)
2914 }
2915
2916 pub fn log_sum_exp<D: Dims>(&self, sum_dims: D) -> Result<Self> {
2918 let sum_dims = sum_dims.to_indexes(self.shape(), "log-sum-exp")?;
2919 if sum_dims.is_empty() {
2920 return Ok(self.clone());
2921 }
2922 let max = sum_dims[1..]
2923 .iter()
2924 .try_fold(self.max_keepdim(sum_dims[0])?, |max, &dim| {
2925 max.max_keepdim(dim)
2926 })?;
2927 let exp = self.broadcast_sub(&max)?.exp()?;
2928 let sum = exp.sum(sum_dims.clone())?;
2929
2930 sum.log()? + max.squeeze_dims(&sum_dims)
2931 }
2932
2933 pub fn pow(&self, rhs: &Tensor) -> Result<Self> {
2935 rhs.mul(&self.log()?)?.exp()
2936 }
2937
2938 pub fn broadcast_pow(&self, rhs: &Tensor) -> Result<Self> {
2940 rhs.broadcast_mul(&self.log()?)?.exp()
2941 }
2942
2943 pub fn flip(&self, dims: &[usize]) -> Result<Tensor> {
2955 let mut result = self.clone();
2956 for &dim in dims.iter() {
2957 let size = result.dim(dim)?;
2958 let indices: Vec<i64> = (0..size).rev().map(|x| x as i64).collect();
2959 let indices_tensor = Tensor::from_vec(indices, (size,), result.device())?;
2960 result = result.index_select(&indices_tensor, dim)?;
2961 }
2962 Ok(result)
2963 }
2964
2965 pub fn unfold<D: Dim>(&self, dim: D, size: usize, step: usize) -> Result<Self> {
2968 let mut sizes = self.dims().to_vec();
2970 let mut strides = self.stride().to_vec();
2971
2972 let dim = dim.to_index(self.shape(), "unfold")?;
2973
2974 let max_len = if self.dims().is_empty() {
2975 1
2976 } else {
2977 sizes[dim]
2978 };
2979 if size > max_len {
2980 bail!(
2981 "unsqueeze: maximum size for tensor at dimension {dim} is {max_len} but size is {size}"
2982 )
2983 }
2984 sizes.push(size);
2985 strides.push(if self.dims().is_empty() {
2986 1
2987 } else {
2988 strides[dim]
2989 });
2990
2991 if !self.dims().is_empty() {
2992 sizes[dim] = ((sizes[dim] as f32 - size as f32) / step as f32 + 1.) as usize;
2993 strides[dim] *= step;
2994 }
2995
2996 let tensor_ = Tensor_ {
2997 id: TensorId::new(),
2998 storage: self.storage.clone(),
2999 layout: Layout::new(sizes.into(), strides, self.layout.start_offset()),
3000 op: BackpropOp::new1(self, Op::Reshape),
3001 is_variable: false,
3002 dtype: self.dtype,
3003 device: self.device.clone(),
3004 };
3005 Ok(Tensor(Arc::new(tensor_)))
3006 }
3007}
3008
3009macro_rules! bin_trait {
3010 ($trait:ident, $fn1:ident, $mul:expr, $add:expr) => {
3011 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for Tensor {
3012 type Output = Result<Tensor>;
3013
3014 fn $fn1(self, rhs: B) -> Self::Output {
3015 Tensor::$fn1(&self, rhs.borrow())
3016 }
3017 }
3018
3019 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<B> for &Tensor {
3020 type Output = Result<Tensor>;
3021
3022 fn $fn1(self, rhs: B) -> Self::Output {
3023 Tensor::$fn1(&self, rhs.borrow())
3024 }
3025 }
3026
3027 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Tensor> for Result<B> {
3028 type Output = Result<Tensor>;
3029
3030 fn $fn1(self, rhs: Tensor) -> Self::Output {
3031 Tensor::$fn1(self?.borrow(), &rhs)
3032 }
3033 }
3034
3035 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<&Tensor> for Result<B> {
3036 type Output = Result<Tensor>;
3037
3038 fn $fn1(self, rhs: &Tensor) -> Self::Output {
3039 Tensor::$fn1(self?.borrow(), rhs)
3040 }
3041 }
3042
3043 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for Tensor {
3044 type Output = Result<Tensor>;
3045
3046 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3047 Tensor::$fn1(&self, rhs?.borrow())
3048 }
3049 }
3050
3051 impl<B: std::borrow::Borrow<Tensor>> std::ops::$trait<Result<B>> for &Tensor {
3052 type Output = Result<Tensor>;
3053
3054 fn $fn1(self, rhs: Result<B>) -> Self::Output {
3055 Tensor::$fn1(&self, rhs?.borrow())
3056 }
3057 }
3058
3059 impl std::ops::$trait<f64> for Tensor {
3060 type Output = Result<Tensor>;
3061
3062 fn $fn1(self, rhs: f64) -> Self::Output {
3063 self.affine($mul(rhs), $add(rhs))
3064 }
3065 }
3066
3067 impl std::ops::$trait<f64> for &Tensor {
3068 type Output = Result<Tensor>;
3069
3070 fn $fn1(self, rhs: f64) -> Self::Output {
3071 self.affine($mul(rhs), $add(rhs))
3072 }
3073 }
3074 };
3075}
3076
3077bin_trait!(Add, add, |_| 1., |v| v);
3078bin_trait!(Sub, sub, |_| 1., |v: f64| -v);
3079bin_trait!(Mul, mul, |v| v, |_| 0.);
3080bin_trait!(Div, div, |v| 1. / v, |_| 0.);
3081
3082impl std::ops::Add<Tensor> for f64 {
3083 type Output = Result<Tensor>;
3084
3085 fn add(self, rhs: Tensor) -> Self::Output {
3086 rhs + self
3087 }
3088}
3089
3090impl std::ops::Add<&Tensor> for f64 {
3091 type Output = Result<Tensor>;
3092
3093 fn add(self, rhs: &Tensor) -> Self::Output {
3094 rhs + self
3095 }
3096}
3097
3098impl std::ops::Mul<Tensor> for f64 {
3099 type Output = Result<Tensor>;
3100
3101 fn mul(self, rhs: Tensor) -> Self::Output {
3102 rhs * self
3103 }
3104}
3105
3106impl std::ops::Mul<&Tensor> for f64 {
3107 type Output = Result<Tensor>;
3108
3109 fn mul(self, rhs: &Tensor) -> Self::Output {
3110 rhs * self
3111 }
3112}
3113
3114impl std::ops::Sub<Tensor> for f64 {
3115 type Output = Result<Tensor>;
3116
3117 fn sub(self, rhs: Tensor) -> Self::Output {
3118 rhs.affine(-1., self)
3119 }
3120}
3121
3122impl std::ops::Sub<&Tensor> for f64 {
3123 type Output = Result<Tensor>;
3124
3125 fn sub(self, rhs: &Tensor) -> Self::Output {
3126 rhs.affine(-1., self)
3127 }
3128}
3129
3130impl std::ops::Div<Tensor> for f64 {
3131 type Output = Result<Tensor>;
3132
3133 #[allow(clippy::suspicious_arithmetic_impl)]
3134 fn div(self, rhs: Tensor) -> Self::Output {
3135 rhs.recip()? * self
3136 }
3137}
3138
3139impl std::ops::Div<&Tensor> for f64 {
3140 type Output = Result<Tensor>;
3141
3142 #[allow(clippy::suspicious_arithmetic_impl)]
3143 fn div(self, rhs: &Tensor) -> Self::Output {
3144 rhs.recip()? * self
3145 }
3146}
3147
3148impl<S: Into<Shape>> From<(Storage, S)> for Tensor {
3149 fn from((storage, shape): (Storage, S)) -> Self {
3150 from_storage(storage, shape, BackpropOp::none(), false)
3151 }
3152}