Skip to main content

candle_core/
tensor.rs

1//! Tensors are N-dimensional matrixes of elements using a single data type.
2#![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/// Unique identifier for tensors.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct TensorId(usize);
15
16impl TensorId {
17    fn new() -> Self {
18        // https://users.rust-lang.org/t/idiomatic-rust-way-to-generate-unique-id/33805
19        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    // As we provide inner mutability on the tensor content, the alternatives are:
28    // - Using a mutex, this would have the highest cost when retrieving the storage but would
29    //   prevent errors when concurrent access takes place. Mutex would also be subject to
30    //   deadlocks for example using the current code if the same tensor is used twice by a single
31    //   binary op.
32    // - Using a refcell unsafe cell would have some intermediary cost, borrow checking would be
33    //   verified dynamically, but the resulting tensors would not be send or sync.
34    // - Using an unsafe cell would have the lowest cost but undefined behavior on concurrent
35    //   accesses.
36    // Ideally, we would use Arc<Storage> for tensors on which we don't plan on modifying the data
37    // and Arc<Mutex<Storage>> for tensors where the data could be modified, e.g. variables but
38    // that's tricky to encode in the current setup.
39    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// Tensors are refcounted so that cloning is cheap when building the op graph.
54// Storages are also refcounted independently so that its possible to avoid
55// copying the storage for operations that only modify the shape or stride.
56#[derive(Clone)]
57/// The core struct for manipulating tensors.
58///
59/// ```rust
60/// use candle_core::{Tensor, DType, Device};
61///
62/// let a = Tensor::arange(0f32, 6f32, &Device::Cpu)?.reshape((2, 3))?;
63/// let b = Tensor::arange(0f32, 12f32, &Device::Cpu)?.reshape((3, 4))?;
64///
65/// let c = a.matmul(&b)?;
66/// # Ok::<(), candle_core::Error>(())
67/// ```
68///
69/// Tensors are reference counted with [`Arc`] so cloning them is cheap.
70pub 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
160/// Creates a fresh tensor structure based on a storage and a shape, this uses contiguous strides.
161pub(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    /// Creates a new tensor filled with ones.
197    ///
198    /// ```rust
199    /// use candle_core::{Tensor, DType, Device};
200    /// let a = Tensor::ones((2, 3), DType::F32, &Device::Cpu)?;
201    /// let b = Tensor::from_slice(&[1.0f32, 1.0, 1.0, 1.0, 1.0, 1.0], (2, 3), &Device::Cpu)?;
202    /// // a == b
203    /// # Ok::<(), candle_core::Error>(())
204    /// ```
205    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    /// Creates a new tensor filled with ones with same shape, dtype, and device as the other tensor.
222    ///
223    /// ```rust
224    /// use candle_core::{Tensor, DType, Device};
225    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
226    /// let b = a.ones_like()?;
227    /// // b == a + 1
228    /// # Ok::<(), candle_core::Error>(())
229    /// ```
230    pub fn ones_like(&self) -> Result<Self> {
231        Tensor::ones(self.shape(), self.dtype(), self.device())
232    }
233
234    // Do not expose outside of the crate, the `is_variable=true` case should only be accessed from
235    // the variable module.
236    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    /// Creates a new tensor filled with zeros.
249    ///
250    /// ```rust
251    /// use candle_core::{Tensor, DType, Device};
252    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
253    /// let b = Tensor::from_slice(&[0.0f32, 0.0, 0.0, 0.0, 0.0, 0.0], (2, 3), &Device::Cpu)?;
254    /// // a == b
255    /// # Ok::<(), candle_core::Error>(())
256    /// ```
257    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    /// Creates a new tensor filled with zeros with same shape, dtype, and device as the other
262    /// tensor.
263    ///
264    /// ```rust
265    /// use candle_core::{Tensor, DType, Device};
266    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
267    /// let b = a.zeros_like()?;
268    /// // b is on CPU f32.
269    /// # Ok::<(), candle_core::Error>(())
270    /// ```
271    pub fn zeros_like(&self) -> Result<Self> {
272        Tensor::zeros(self.shape(), self.dtype(), self.device())
273    }
274
275    // Do not expose outside of the crate, the `is_variable=true` case should only be accessed from
276    // the variable module.
277    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    /// Creates a new tensor filled with uninitialized memory.
290    ///
291    /// # Safety
292    /// This returns uninitialized memory.
293    ///
294    /// ```rust
295    /// use candle_core::{Tensor, DType, Device};
296    /// let a = unsafe { Tensor::empty((2, 3), DType::F32, &Device::Cpu)? };
297    /// // a == b
298    /// # Ok::<(), candle_core::Error>(())
299    /// ```
300    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    /// Creates a new tensor filled with uninitialized memory of the same shape, dtype, and device as the other
305    /// tensor.
306    ///
307    /// # Safety
308    /// This returns uninitialized memory.
309    ///
310    /// ```rust
311    /// use candle_core::{Tensor, DType, Device};
312    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
313    /// let b = unsafe { a.empty_like()? };
314    /// # Ok::<(), candle_core::Error>(())
315    /// ```
316    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    /// Creates a new tensor initialized with values sampled uniformly between `lo` and `up`.
348    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    /// Creates a new tensor initialized with values sampled from a normal distribution with the
400    /// specified `mean` and standard deviation `std`.
401    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    /// Creates a new tensor on the specified device using the content and shape of the input.
427    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    /// Returns a new tensor with all the elements having the same specified value.
433    ///```rust
434    /// use candle_core::{Tensor, Device};
435    /// let a = Tensor::full(3.5, (2, 4), &Device::Cpu)?;
436    ///
437    /// assert_eq!(a.to_vec2::<f64>()?, &[
438    ///     [3.5, 3.5, 3.5, 3.5],
439    ///     [3.5, 3.5, 3.5, 3.5],
440    /// ]);
441    /// # Ok::<(), candle_core::Error>(())
442    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    /// Creates a new 1D tensor from an iterator.
456    ///```rust
457    /// use candle_core::{Tensor, Device};
458    /// let a = Tensor::from_iter( [1.0, 2.0, 3.0, 4.0].into_iter(), &Device::Cpu)?;
459    ///
460    /// assert_eq!(a.to_vec1::<f64>()?, &[1.0, 2.0, 3.0, 4.0]);
461    /// # Ok::<(), candle_core::Error>(())
462    /// ```
463    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    /// Creates a new 1D tensor with values from the interval `[start, end)` taken with a common
473    /// difference `1` from `start`.
474    ///```rust
475    /// use candle_core::{Tensor, Device};
476    /// let a = Tensor::arange(2., 5., &Device::Cpu)?;
477    ///
478    /// assert_eq!(a.to_vec1::<f64>()?, &[2., 3., 4.]);
479    /// # Ok::<(), candle_core::Error>(())
480    /// ```
481    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    /// Creates a new 1D tensor with values from the interval `[start, end)` taken with a common
486    /// difference `step` from `start`.
487    ///```rust
488    /// use candle_core::{Tensor, Device};
489    /// let a = Tensor::arange_step(2.0, 4.0, 0.5, &Device::Cpu)?;
490    ///
491    /// assert_eq!(a.to_vec1::<f64>()?, &[2.0, 2.5, 3.0, 3.5]);
492    /// # Ok::<(), candle_core::Error>(())
493    /// ```
494    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    /// Creates a new tensor initialized with values from the input vector. The number of elements
533    /// in this vector must be the same as the number of elements defined by the shape.
534    /// If the device is cpu, no data copy is made.
535    ///```rust
536    /// use candle_core::{Tensor, Device};
537    /// let a = Tensor::from_vec(vec!{1., 2., 3., 4., 5., 6.}, (2, 3), &Device::Cpu)?;
538    ///
539    /// assert_eq!(a.to_vec2::<f64>()?, &[
540    ///     [1., 2., 3.],
541    ///     [4., 5., 6.]
542    /// ]);
543    /// # Ok::<(), candle_core::Error>(())
544    /// ```
545    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    /// Creates a new tensor initialized with values from the input slice. The number of elements
554    /// in this vector must be the same as the number of elements defined by the shape.
555    ///```rust
556    /// use candle_core::{Tensor, Device};
557    /// let values = vec![1., 2., 3., 4., 5., 6., 7., 8.];
558    /// let a = Tensor::from_slice(&values[1..7], (2, 3), &Device::Cpu)?;
559    ///
560    /// assert_eq!(a.to_vec2::<f64>()?, &[
561    ///     [2., 3., 4.],
562    ///     [5., 6., 7.]
563    /// ]);
564    /// # Ok::<(), candle_core::Error>(())
565    /// ```
566    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    /// Returns true if the computation graph should track this op, that is if it is
593    /// a variable or if it has some variable as dependencies.
594    pub fn track_op(&self) -> bool {
595        self.is_variable || self.op.is_some()
596    }
597
598    /// Creates a fresh tensor structure based on a storage and a shape.
599    ///
600    /// # Note
601    /// - This uses contiguous strides
602    /// - Ensure the shape is compatible with the shape of the storage.
603    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    // TODO: Also make an inplace version or a pre-allocated? This could be tricky
613    // if this can create cycles in the compute graph.
614    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    /// Round element of the input tensor to the nearest integer.
654    ///
655    /// If the number of decimals is negative, it specifies the number of positions to the left of
656    /// the decimal point.
657    pub fn round_to(&self, decimals: i32) -> Result<Self> {
658        let mult = 10f64.powi(decimals);
659        (self * mult)?.round()? * (1f64 / mult)
660    }
661
662    /// Retrieves the single scalar value hold in the tensor. If the tensor contains multiple
663    /// dimensions, an error is returned instead.
664    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    /// An alias for `to_scalar`.
685    pub fn to_vec0<S: crate::WithDType>(&self) -> Result<S> {
686        self.to_scalar::<S>()
687    }
688
689    /// Repeat this tensor along the specified dimensions.
690    pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Tensor> {
691        // Similar to PyTorch, we extend the number of dimensions of self if needed.
692        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    /// Creates grids of coordinates specified by the 1D inputs.
711    ///
712    /// # Arguments
713    ///
714    /// * `args` - A slice of 1D tensors.
715    /// * `xy_indexing` - Whether to use xy indexing or ij indexing. If xy is selected, the
716    ///   first dimension corresponds to the cardinality of the second input and the second
717    ///   dimension corresponds to the cardinality of the first input. If ij is selected, the
718    ///   dimensions are in the same order as the cardinality of the inputs.
719    ///
720    /// # Examples
721    ///
722    /// ```rust
723    /// use candle_core::{Tensor, Device, Shape};
724    /// let x = Tensor::new(&[1f32, 2., 3.], &Device::Cpu)?;
725    /// let y = Tensor::new(&[4f32, 5., 6.], &Device::Cpu)?;
726    ///
727    /// let grids_xy = Tensor::meshgrid(&[&x, &y], true)?;
728    ///
729    /// assert_eq!(grids_xy.len(), 2);
730    /// assert_eq!(grids_xy[0].dims(), &[3, 3]);
731    ///
732    /// assert_eq!(grids_xy[0].to_vec2::<f32>()?, &[[1., 2., 3.], [1., 2., 3.], [1., 2., 3.]]);
733    /// assert_eq!(grids_xy[1].to_vec2::<f32>()?, &[[4., 4., 4.], [5., 5., 5.], [6., 6., 6.]]);
734    ///
735    /// let grids_ij = Tensor::meshgrid(&[&x, &y], false)?;
736    ///
737    /// assert_eq!(grids_ij[0].to_vec2::<f32>()?, &[[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]);
738    /// assert_eq!(grids_ij[1].to_vec2::<f32>()?, &[[4., 5., 6.], [4., 5., 6.], [4., 5., 6.]]);
739    /// # Ok::<(), candle_core::Error>(())
740    /// ```
741    ///
742    /// # Errors
743    ///
744    /// * Will return `Err` if `args` contains less than 2 tensors.
745    ///
746    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    /// This operation multiplies the input tensor by `mul` then adds `add` and return the result.
778    /// The input values `mul` and `add` are casted to the appropriate type so some rounding might
779    /// be performed.
780    ///
781    /// ```rust
782    /// use candle_core::{Tensor, Device};
783    /// let a = Tensor::new(&[[0f32, 1.], [2., 3.]], &Device::Cpu)?;
784    /// let a = a.affine(4., -2.)?;
785    /// assert_eq!(a.to_vec2::<f32>()?, &[[-2.0, 2.0], [6.0, 10.0]]);
786    /// # Ok::<(), candle_core::Error>(())
787    /// ```
788    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    /// Applies the Exponential Linear Unit (ELU) function on each element of the input tensor.
798    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    /// Raise the tensor to some float exponent `e`.
808    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    /// Split a tensor into the specified number of chunks, this may return less chunks than
831    /// specified.
832    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    /// Returns a new tensor that is a narrowed version of the input, the dimension `dim`
857    /// ranges from `start` to `start + len`.
858    /// ```
859    /// use candle_core::{Tensor, Device};
860    /// let a = Tensor::new(&[
861    ///     [0f32, 1., 2.],
862    ///     [3.  , 4., 5.],
863    ///     [6.  , 7., 8.]
864    /// ], &Device::Cpu)?;
865    ///
866    /// let b = a.narrow(0, 1, 2)?;
867    /// assert_eq!(b.shape().dims(), &[2, 3]);
868    /// assert_eq!(b.to_vec2::<f32>()?, &[
869    ///     [3., 4., 5.],
870    ///     [6., 7., 8.]
871    /// ]);
872    ///
873    /// let c = a.narrow(1, 1, 1)?;
874    /// assert_eq!(c.shape().dims(), &[3, 1]);
875    /// assert_eq!(c.to_vec2::<f32>()?, &[
876    ///     [1.],
877    ///     [4.],
878    ///     [7.]
879    /// ]);
880    /// # Ok::<(), candle_core::Error>(())
881    /// ```
882    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    /// Roll the tensor input along the given dimension.
981    /// Elements that are shifted beyond the last position are re-introduced at the first position.
982    ///
983    /// ```rust
984    /// # use candle_core::{Tensor, Device};
985    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
986    /// let tensor = tensor.roll(1, 0)?;
987    /// assert_eq!(tensor.to_vec2::<f32>()?, &[[4., 5.], [0., 1.], [2., 3.]]);
988    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
989    /// let tensor = tensor.roll(-1, 0)?;
990    /// assert_eq!(tensor.to_vec2::<f32>()?, &[[2., 3.], [4., 5.], [0., 1.]]);
991    /// # Ok::<(), candle_core::Error>(())
992    /// ```
993    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    /// Returns the sum of all elements in the input tensor. The sum is performed over all the
1010    /// input dimensions.
1011    ///
1012    /// The resulting tensor has a shape that is similar to the shape of the input tensor, except
1013    /// that the number of elements for each dimension index in `sum_dims` is 1.
1014    ///
1015    /// ```rust
1016    /// use candle_core::{Tensor, Device};
1017    /// let a = Tensor::new(&[[0f32, 1.], [2., 3.]], &Device::Cpu)?;
1018    /// let s = a.sum_keepdim(0)?;
1019    /// assert_eq!(s.to_vec2::<f32>()?, &[[2., 4.]]);
1020    /// let s = a.sum_keepdim(1)?;
1021    /// assert_eq!(s.to_vec2::<f32>()?, &[[1.], [5.]]);
1022    /// let s = a.sum_keepdim((0, 1))?;
1023    /// assert_eq!(s.to_vec2::<f32>()?, &[[6.]]);
1024    /// # Ok::<(), candle_core::Error>(())
1025    /// ```
1026    pub fn sum_keepdim<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1027        self.sum_impl(sum_dims, true)
1028    }
1029
1030    /// Returns the sum of all elements in the input tensor. The sum is performed over all the
1031    /// input dimensions and compared to `sum_keepdim` these dimensions are squeezed rather than
1032    /// kept.
1033    pub fn sum<D: Dims>(&self, sum_dims: D) -> Result<Self> {
1034        self.sum_impl(sum_dims, false)
1035    }
1036
1037    /// Returns the mean of all elements in the input tensor. The mean is performed over all the
1038    /// input dimensions.
1039    ///
1040    /// The resulting tensor has a shape that is similar to the shape of the input tensor, except
1041    /// that the number of elements for each dimension index in `mean_dims` is 1.
1042    ///
1043    /// ```rust
1044    /// use candle_core::{Tensor, Device};
1045    /// let a = Tensor::new(&[[0f32, 1.], [2., 3.]], &Device::Cpu)?;
1046    /// let s = a.mean_keepdim(0)?;
1047    /// assert_eq!(s.to_vec2::<f32>()?, &[[1., 2.]]);
1048    /// let s = a.mean_keepdim(1)?;
1049    /// assert_eq!(s.to_vec2::<f32>()?, &[[0.5], [2.5]]);
1050    /// let s = a.mean_keepdim((0, 1))?;
1051    /// assert_eq!(s.to_vec2::<f32>()?, &[[1.5]]);
1052    /// # Ok::<(), candle_core::Error>(())
1053    /// ```
1054    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    /// Returns the mean of all elements in the input tensor. The mean is performed over all the
1062    /// input dimensions and compared to `mean_keepdim` these dimensions are squeezed rather than
1063    /// kept.
1064    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    /// Returns the unbiased variance over the selected dimension.
1072    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    /// Returns the unbiased variance over the selected dimension.
1080    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    /// Gathers the maximum value across the selected dimension. The resulting shape has the same
1086    /// number of dimensions as the original tensor and the select dimension has a single element.
1087    pub fn max_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1088        self.reduce_impl(dim, true, ReduceOp::Max)
1089    }
1090
1091    /// Similar to `max_keepdim` but the target dimension is squeezed.
1092    pub fn max<D: Dim>(&self, dim: D) -> Result<Self> {
1093        self.reduce_impl(dim, false, ReduceOp::Max)
1094    }
1095
1096    /// Gathers the minimum value across the selected dimension. The resulting shape has the same
1097    /// number of dimensions as the original tensor and the select dimension has a single element.
1098    pub fn min_keepdim<D: Dim>(&self, dim: D) -> Result<Self> {
1099        self.reduce_impl(dim, true, ReduceOp::Min)
1100    }
1101
1102    /// Similar to `min_keepdim` but the target dimension is squeezed.
1103    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    /// Similar to `argmax_keepdim` but the target dimension is squeezed.
1112    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    /// Similar to `argmin_keepdim` but the target dimension is squeezed.
1121    pub fn argmin<D: Dim>(&self, dim: D) -> Result<Self> {
1122        self.reduce_impl(dim, false, ReduceOp::ArgMin)
1123    }
1124
1125    /// Element-wise comparison between two tensors, e.g. equality, greater than, ... The actual
1126    /// comparison operation is specified by the `op` argument.
1127    ///
1128    /// The returned tensor has the same shape as the original tensors and uses `u8` elements.
1129    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    /// Element-wise equality.
1146    pub fn eq<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1147        self.cmp(rhs, CmpOp::Eq)
1148    }
1149
1150    /// Element-wise non-equality.
1151    pub fn ne<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1152        self.cmp(rhs, CmpOp::Ne)
1153    }
1154
1155    /// Element-wise comparison with lower-than, the returned tensor uses value 1 where `self <
1156    /// rhs` and 0 otherwise.
1157    pub fn lt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1158        self.cmp(rhs, CmpOp::Lt)
1159    }
1160
1161    /// Element-wise comparison with greater-than, the returned tensor uses value 1 where `self >
1162    /// rhs` and 0 otherwise.
1163    pub fn gt<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1164        self.cmp(rhs, CmpOp::Gt)
1165    }
1166
1167    /// Element-wise comparison with greater-equal, the returned tensor uses value 1 where `self >=
1168    /// rhs` and 0 otherwise.
1169    pub fn ge<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1170        self.cmp(rhs, CmpOp::Ge)
1171    }
1172
1173    /// Element-wise comparison with lower-equal, the returned tensor uses value 1 where `self <=
1174    /// rhs` and 0 otherwise.
1175    pub fn le<T: TensorOrScalar>(&self, rhs: T) -> Result<Self> {
1176        self.cmp(rhs, CmpOp::Le)
1177    }
1178
1179    /// Clamp the tensor values to be between `min` and `max`.
1180    pub fn clamp<T1: TensorOrScalar, T2: TensorOrScalar>(&self, min: T1, max: T2) -> Result<Self> {
1181        self.maximum(min)?.minimum(max)
1182    }
1183
1184    /// Interpolate the input tensor to the `target_size` size, taking the value of the nearest element.
1185    ///
1186    /// The input tensor should have three dimensions, `(batch, channels, l)`, the returned
1187    /// tensor also has three dimensions, `(batch, channels, target_size)`.
1188    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    /// Alias for `interpolate1d`.
1198    pub fn upsample_nearest1d(&self, target_size: usize) -> Result<Self> {
1199        self.interpolate1d(target_size)
1200    }
1201
1202    /// Interpolate the input tensor to the `(target_h, target_w)` size, taking the value of the
1203    /// nearest element.
1204    ///
1205    /// The input tensor should have four dimensions, `(batch, channels, h, w)`, the returned
1206    /// tensor also has four dimensions, `(batch, channels, target_h, target_w)`.
1207    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    /// Alias for `interpolate2d`.
1221    pub fn upsample_nearest2d(&self, target_h: usize, target_w: usize) -> Result<Self> {
1222        self.interpolate2d(target_h, target_w)
1223    }
1224
1225    /// Bilinear interpolation to resize the input tensor to the specified size.
1226    ///
1227    /// The input tensor should have four dimensions: `(batch, channels, h, w)`.
1228    /// The returned tensor also has four dimensions: `(batch, channels, target_h, target_w)`.
1229    ///
1230    /// # Arguments
1231    ///
1232    /// * `target_h` - Target height
1233    /// * `target_w` - Target width
1234    /// * `align_corners` - If true, corner pixels are aligned. If false (default),
1235    ///   pixels are treated as areas (matches PyTorch default behavior).
1236    ///
1237    /// # Example
1238    ///
1239    /// ```rust
1240    /// use candle_core::{Tensor, Device};
1241    /// # fn main() -> candle_core::Result<()> {
1242    /// let t = Tensor::arange(0f32, 16f32, &Device::Cpu)?.reshape((1, 1, 4, 4))?;
1243    /// let upsampled = t.upsample_bilinear2d(8, 8, false)?;
1244    /// assert_eq!(upsampled.dims(), &[1, 1, 8, 8]);
1245    /// # Ok(())
1246    /// # }
1247    /// ```
1248    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        // Pass None for scale factors (size mode)
1262        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    /// Bilinear interpolation using scale factors.
1274    ///
1275    /// Similar to `upsample_bilinear2d` but uses scale factors instead of absolute sizes.
1276    /// This matches PyTorch's `interpolate(scale_factor=...)` behavior.
1277    ///
1278    /// # Arguments
1279    ///
1280    /// * `scale_h` - Height scaling factor
1281    /// * `scale_w` - Width scaling factor
1282    /// * `align_corners` - If true, corner pixels are aligned
1283    ///
1284    /// # Example
1285    ///
1286    /// ```rust
1287    /// use candle_core::{Tensor, Device};
1288    /// # fn main() -> candle_core::Result<()> {
1289    /// let t = Tensor::arange(0f32, 16f32, &Device::Cpu)?.reshape((1, 1, 4, 4))?;
1290    /// // Scale by 2x in both dimensions
1291    /// let upsampled = t.upsample_bilinear2d_with_scale(2.0, 2.0, false)?;
1292    /// assert_eq!(upsampled.dims(), &[1, 1, 8, 8]);
1293    /// # Ok(())
1294    /// # }
1295    /// ```
1296    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        // Calculate output size (floor, matching PyTorch)
1305        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        // Early return if size unchanged
1309        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        // Pass original scale factors (scale_factor mode)
1321        // This ensures PyTorch-compatible scale calculation
1322        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    /// 2D average pooling over an input tensor with multiple channels.
1339    ///
1340    /// The input tensor should have four dimensions, `(batch, channels, h, w)`, the returned
1341    /// tensor also has four dimensions, `(batch, channels, h', w')`. The pooling is performed on
1342    /// the two last dimensions using a kernel of size `sz`. The returned element is the average
1343    /// value over the kernel window.
1344    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    /// Same as `avg_pool2d` but with a `stride` that can be set to a value different from the
1350    /// kernel size.
1351    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        // https://pytorch.org/docs/stable/generated/torch.nn.AvgPool2d.html#torch.nn.AvgPool2d
1363        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    /// 2D max pooling over an input tensor with multiple channels.
1377    ///
1378    /// The input tensor should have four dimensions, `(batch, channels, h, w)`, the returned
1379    /// tensor also has four dimensions, `(batch, channels, h', w')`. The pooling is performed on
1380    /// the two last dimensions using a kernel of size `sz`, the returned element is the maximum
1381    /// value over the kernel window.
1382    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    /// Same as `max_pool2d` but with a `stride` that can be set to a value different from the
1388    /// kernel size.
1389    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        // https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html#torch.nn.MaxPool2d
1401        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    /// Computes the dot product of two 1D tensors.
1415    ///
1416    /// - If inputs are 1D vectors (`[n]`), returns their scalar dot product.
1417    /// - Panics if shapes are not compatible
1418    /// - Not supported for integer dtypes
1419    ///
1420    /// # Example (vectors)
1421    /// ```rust
1422    /// use candle_core::{Tensor, Device};
1423    /// let t1 = Tensor::new(&[1.0, 2.0, 3.0], &Device::Cpu)?;
1424    /// let t2 = Tensor::new(&[4.0, 5.0, 6.0], &Device::Cpu)?;
1425    /// let res = t1.dot(&t2)?;
1426    /// assert_eq!(res.to_scalar::<f64>()?, 32.);
1427    /// # Ok::<(), candle_core::Error>(())
1428    /// ```
1429    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    /// Computes the **Frobenius norm** (L2 norm of all elements) of the tensor.
1442    /// - Output is `sqrt(sum(x^2))`.
1443    /// - Always returns a scalar (`[]` shape).
1444    ///
1445    /// # Example
1446    /// ```rust
1447    /// use candle_core::{Tensor, Device};
1448    /// let t = Tensor::new(&[[3., 4.], [0., 0.]], &Device::Cpu)?;
1449    /// let norm = t.norm()?;
1450    /// assert_eq!(norm.to_scalar::<f64>()?, 5.);
1451    /// # Ok::<(), candle_core::Error>(())
1452    /// ```
1453    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    /// Performs strict matrix-vector multiplication (`[m, n] * [n] = [m]`).
1462    ///
1463    /// - If `self` is a matrix (`[m, n]`) and `rhs` is a vector (`[n]`), returns a vector (`[m]`).
1464    /// - **No broadcasting**: Panics if `self` is not 2D or if `rhs` is not 1D with matching size.
1465    ///
1466    /// # Example
1467    /// ```rust
1468    /// use candle_core::{Tensor, Device};
1469    /// let mat = Tensor::new(&[[1., 2., 3.], [4., 5., 6.]], &Device::Cpu)?;
1470    /// let vec = Tensor::new(&[1., 1., 1.], &Device::Cpu)?;
1471    /// let res = mat.mv(&vec)?;
1472    /// assert_eq!(res.to_vec1::<f64>()?, [6., 15.]);
1473    /// # Ok::<(), candle_core::Error>(())
1474    /// ```
1475    pub fn mv(&self, rhs: &Self) -> Result<Self> {
1476        // Strict shape checks
1477        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        // Direct matmul after ensuring rhs is column vector
1488        self.matmul(&rhs.unsqueeze(1)?)?.squeeze(1)
1489    }
1490
1491    /// Returns the matrix-multiplication of the input tensor with the other provided tensor.
1492    ///
1493    /// # Arguments
1494    ///
1495    /// * `self` - A tensor with dimensions `b1, b2, ..., bi, m, k`.
1496    /// * `rhs` - A tensor with dimensions `b1, b2, ..., bi, k, n`.
1497    ///
1498    /// The resulting tensor has dimensions `b1, b2, ..., bi, m, n`.
1499    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    /// Matrix-multiplication with broadcasting support.
1554    ///
1555    /// Compared to `matmul` the two matrixes are allowed to have different dimensions as long as
1556    /// they are compatible for broadcast. E.g. if `self` has shape `(j, 1, n, k)` and `rhs` has
1557    /// shape `(l, k, m)`, the output will have shape `(j, l, n, m)`.
1558    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        // TODO: Avoid concretising the broadcasted matrixes via contiguous.
1564        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            // A rank-2 rhs is only broadcast over the batch dimensions, so the whole product is
1570            // a single 2D matmul once the leading dims of lhs are folded into its row dimension.
1571            // Broadcasting the rhs instead would copy it `batch` times -- for an lm_head that is
1572            // the entire vocabulary matrix, per call. Same trick, and same contiguity guard, as
1573            // `candle_nn::Linear::forward`.
1574            (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    /// Returns a tensor with the same shape as the input tensor, the values are taken from
1591    /// `on_true` if the input tensor value is not zero, and `on_false` at the positions where the
1592    /// input tensor is equal to zero.
1593    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    /// Returns a tensor with the values from the `self` tensor at the index corresponding to the
1608    /// values hold in the `ids` tensor.
1609    ///
1610    /// # Arguments
1611    ///
1612    /// * `self` - A tensor with dimensions `v, h`.
1613    /// * `ids` - A tensor with dimensions `s` and with integer values between 0 and v (exclusive).
1614    ///
1615    /// The resulting tensor has dimensions `s, h`. `s` is called the sequence length, `v` the
1616    /// vocabulary size, and `h` the hidden size.
1617    ///
1618    /// ```rust
1619    /// use candle_core::{Tensor, Device};
1620    /// let values = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
1621    /// let ids = Tensor::new(&[2u32, 1u32, 2u32], &Device::Cpu)?;
1622    /// let emb = values.embedding(&ids)?;
1623    /// assert_eq!(emb.to_vec2::<f32>()?, &[[4., 5.], [2., 3.], [4., 5.]]);
1624    /// # Ok::<(), candle_core::Error>(())
1625    /// ```
1626    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    /// Embeds the values of the `src` tensor into the `self` tensor on the specified dimension.
1751    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            // TODO: Maybe we want to add a more efficient implementation at some point.
1757            self.transpose(0, dim)?
1758                .slice_scatter0(&src.transpose(0, dim)?, start)?
1759                .transpose(0, dim)
1760        }
1761    }
1762
1763    /// Embeds the values of the `src` tensor into the `self` tensor on the first dimension.
1764    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    /// Accumulate element from `source` at indexes `indexes` and add them to `self`.
1820    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        // The number of element in indexes must match the dimension on which the add is
1845        // performed on the source tensor (and the index values from `indexes` are taken from
1846        // the target tensor self)
1847        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    /// Gather values across the target dimension.
1871    ///
1872    /// # Arguments
1873    ///
1874    /// * `self` - The input tensor.
1875    /// * `indexes` - The indices of elements to gather, this should have same number of dimensions as `self`
1876    ///   and indexes.dims()[d] <= self.dims()[d] for all dimensions d != dim
1877    /// * `dim` - the target dimension.
1878    ///
1879    /// The resulting tensor has the same shape as `indexes` and use values from `self` indexed on
1880    /// dimension `dim` by the values in `indexes`.
1881    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    /// Select values for the input tensor at the target indexes across the specified dimension.
1914    ///
1915    /// The `indexes` is argument is an int tensor with a single dimension.
1916    /// The output has the same number of dimension as the `self` input. The target dimension of
1917    /// the output has length the length of `indexes` and the values are taken from `self` using
1918    /// the index from `indexes`. Other dimensions have the same number of elements as the input
1919    /// tensor.
1920    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    /// Returns an iterator over position of the elements in the storage when ranging over the
1944    /// index tuples in lexicographic order.
1945    pub fn strided_index(&self) -> crate::StridedIndex<'_> {
1946        self.layout.strided_index()
1947    }
1948
1949    /// Similar to `strided_index` but returns the position of the start of each contiguous block
1950    /// as well as the length of the contiguous blocks. For a contiguous tensor, the index iterator
1951    /// will only return the start offset and the size would be the number of elements in the
1952    /// tensor.
1953    pub fn strided_blocks(&self) -> crate::StridedBlocks<'_> {
1954        self.layout.strided_blocks()
1955    }
1956
1957    /// Returns the data contained in a 1D tensor as a vector of scalar values.
1958    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    /// Returns the data contained in a 2D tensor as a vector of vector of scalar values.
1983    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    /// Returns the data contained in a 3D tensor.
2014    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    /// The dtype for the elements stored in the input tensor.
2055    pub fn dtype(&self) -> DType {
2056        self.dtype
2057    }
2058
2059    /// The device on which the input tensor is located.
2060    pub fn device(&self) -> &Device {
2061        &self.device
2062    }
2063
2064    /// The tensor shape, i.e. dimension sizes on each axis.
2065    pub fn shape(&self) -> &Shape {
2066        self.layout().shape()
2067    }
2068
2069    /// The dimension size for this tensor on each axis.
2070    pub fn dims(&self) -> &[usize] {
2071        self.shape().dims()
2072    }
2073
2074    /// The dimension size for a specified dimension index.
2075    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    /// The layout of the input tensor, this stores both the shape of the tensor as well as the
2081    /// strides and the start offset to apply to the underlying storage.
2082    pub fn layout(&self) -> &Layout {
2083        &self.layout
2084    }
2085
2086    pub fn stride(&self) -> &[usize] {
2087        self.layout.stride()
2088    }
2089
2090    /// The number of dimensions for this tensor, 0 for a scalar tensor, 1 for a 1D tensor, etc.
2091    pub fn rank(&self) -> usize {
2092        self.shape().rank()
2093    }
2094
2095    /// The number of elements stored in this tensor.
2096    pub fn elem_count(&self) -> usize {
2097        self.shape().elem_count()
2098    }
2099
2100    /// The unique identifier for this tensor.
2101    pub fn id(&self) -> TensorId {
2102        self.id
2103    }
2104
2105    /// Whether this tensor is a variable or not. A variable is a tensor for which gradient is
2106    /// tracked and on which backpropagation can be performed.
2107    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    /// Computes the max of all the elements in this tensor and returns a tensor holding this
2116    /// scalar with zero dimensions.
2117    ///
2118    /// ```rust
2119    /// use candle_core::{Tensor, Device};
2120    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2121    /// let tensor = tensor.max_all()?;
2122    /// assert_eq!(tensor.to_scalar::<f32>()?, 5.);
2123    /// # Ok::<(), candle_core::Error>(())
2124    /// ```
2125    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    /// Computes the min of all the elements in this tensor and returns a tensor holding this
2134    /// scalar with zero dimensions.
2135    ///
2136    /// ```rust
2137    /// use candle_core::{Tensor, Device};
2138    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2139    /// let tensor = tensor.min_all()?;
2140    /// assert_eq!(tensor.to_scalar::<f32>()?, 0.);
2141    /// # Ok::<(), candle_core::Error>(())
2142    /// ```
2143    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    /// Computes the sum of all the elements in this tensor and returns a tensor holding this
2152    /// scalar with zero dimensions.
2153    ///
2154    /// ```rust
2155    /// use candle_core::{Tensor, Device};
2156    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2157    /// let tensor = tensor.sum_all()?;
2158    /// assert_eq!(tensor.to_scalar::<f32>()?, 15.);
2159    /// # Ok::<(), candle_core::Error>(())
2160    /// ```
2161    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    /// Flattens the input tensor on the dimension indexes from `start_dim` to `end_dim` (both
2201    /// inclusive).
2202    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    /// Flattens the input tensor on the dimension indexes from `0` to `end_dim` (inclusive).
2207    pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Tensor> {
2208        self.flatten_(None::<usize>, Some(end_dim))
2209    }
2210
2211    /// Flattens the input tensor on the dimension indexes from `start_dim` (inclusive) to the last
2212    /// dimension.
2213    pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Tensor> {
2214        self.flatten_(Some(start_dim), None::<usize>)
2215    }
2216
2217    /// Flattens the input tensor by reshaping it into a one dimension tensor.
2218    ///
2219    /// ```rust
2220    /// use candle_core::{Tensor, Device};
2221    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2222    /// let tensor = tensor.flatten_all()?;
2223    /// assert_eq!(tensor.to_vec1::<f32>()?, &[0., 1., 2., 3., 4., 5.]);
2224    /// # Ok::<(), candle_core::Error>(())
2225    /// ```
2226    pub fn flatten_all(&self) -> Result<Tensor> {
2227        self.flatten_(None::<usize>, None::<usize>)
2228    }
2229
2230    /// Returns the sub-tensor fixing the index at `i` on the first dimension.
2231    ///
2232    /// ```rust
2233    /// use candle_core::{Tensor, Device};
2234    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2235    /// let t = tensor.get(0)?;
2236    /// assert_eq!(t.to_vec1::<f32>()?, &[0., 1.]);
2237    /// let t = tensor.get(1)?;
2238    /// assert_eq!(t.to_vec1::<f32>()?, &[2., 3.]);
2239    /// # Ok::<(), candle_core::Error>(())
2240    /// ```
2241    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    /// Returns the sub-tensor fixing the index at `index` on the dimension `dim`.
2251    ///
2252    /// ```rust
2253    /// use candle_core::{Tensor, Device};
2254    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2255    /// let t = tensor.get_on_dim(1, 0)?;
2256    /// assert_eq!(t.to_vec1::<f32>()?, &[0., 2., 4.]);
2257    /// let t = tensor.get_on_dim(1, 1)?;
2258    /// assert_eq!(t.to_vec1::<f32>()?, &[1., 3., 5.]);
2259    /// let t = tensor.get_on_dim(0, 1)?;
2260    /// assert_eq!(t.to_vec1::<f32>()?, &[2., 3.]);
2261    /// # Ok::<(), candle_core::Error>(())
2262    /// ```
2263    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    /// Returns a tensor that is a transposed version of the input, the two last dimensions of the
2269    /// input are swapped.
2270    ///
2271    /// ```rust
2272    /// use candle_core::{Tensor, Device};
2273    /// let tensor = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]], &Device::Cpu)?;
2274    /// let tensor = tensor.t()?;
2275    /// assert_eq!(tensor.to_vec2::<f32>()?, &[[0.0, 2.0, 4.0], [1.0, 3.0, 5.0]]);
2276    /// # Ok::<(), candle_core::Error>(())
2277    /// ```
2278    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    /// Returns a tensor that is a transposed version of the input, the given dimensions are
2292    /// swapped.
2293    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    /// Returns a tensor with the same data as the input where the dimensions have been permuted.
2313    /// dims must be a permutation, i.e. include each dimension index exactly once.
2314    ///
2315    /// ```rust
2316    /// use candle_core::{Tensor, Device};
2317    /// let tensor = Tensor::arange(0u32, 120u32, &Device::Cpu)?.reshape((2, 3, 4, 5))?;
2318    /// assert_eq!(tensor.dims(), &[2, 3, 4, 5]);
2319    /// let tensor = tensor.permute((2, 3, 1, 0))?;
2320    /// assert_eq!(tensor.dims(), &[4, 5, 3, 2]);
2321    /// # Ok::<(), candle_core::Error>(())
2322    /// ```
2323    pub fn permute<D: Dims>(&self, dims: D) -> Result<Tensor> {
2324        let dims = dims.to_indexes(self.shape(), "permute")?;
2325        // O(n^2) permutation check but these arrays are small.
2326        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    /// Returns true if the data is stored in a C contiguous (aka row major) way.
2349    pub fn is_contiguous(&self) -> bool {
2350        self.layout.is_contiguous()
2351    }
2352
2353    /// Returns true if the data is stored in a Fortran contiguous (aka column major) way.
2354    pub fn is_fortran_contiguous(&self) -> bool {
2355        self.layout.is_fortran_contiguous()
2356    }
2357
2358    /// Compared to clone, this copies the actual storage but may fail because of running out of
2359    /// memory.
2360    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    /// Returns a new tensor detached from the current graph, gradient are not propagated through
2375    /// this new node. The storage of this tensor is shared with the initial tensor.
2376    ///
2377    /// If the tensor is already detached from the computation graph, the same tensor is returned.
2378    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    /// If the target device is the same as the tensor device, only a shallow copy is performed.
2396    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                    // can't clone storage if it's the same device because of the underlying device ptr
2411                    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    /// Returns a new tensor duplicating data from the original tensor. New dimensions are inserted
2438    /// on the left.
2439    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    /// Broadcast the input tensor to the target shape. This returns an error if the input shape is
2447    /// not compatible with the target shape.
2448    ///
2449    /// If the input shape is `i_1, i_2, ... i_k`, the target shape has to have `k` dimensions or
2450    /// more and shape `j_1, ..., j_l, t_1, t_2, ..., t_k`. The dimensions `j_1` to `j_l` can have
2451    /// any value, the dimension `t_a` must be equal to `i_a` if `i_a` is different from 1. If
2452    /// `i_a` is equal to 1, any value can be used.
2453    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    /// An alias for broadcast_as.
2467    pub fn expand<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
2468        self.broadcast_as(shape)
2469    }
2470
2471    /// Casts the input tensor to the target `dtype`.
2472    ///
2473    /// ```rust
2474    /// use candle_core::{Tensor, Device};
2475    /// let tensor = Tensor::new(3.14159265358979f64, &Device::Cpu)?;
2476    /// assert_eq!(tensor.to_scalar::<f64>()?, 3.14159265358979);
2477    /// let tensor = tensor.to_dtype(candle_core::DType::F32)?;
2478    /// assert_eq!(tensor.to_scalar::<f32>()?, 3.1415927);
2479    /// # Ok::<(), candle_core::Error>(())
2480    /// ```
2481    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    /// Returns a tensor that is in row major order. This is the same as the original tensor if it
2493    /// was already contiguous, otherwise a copy is triggered.
2494    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    /// Returns a tensor that is in row major order. This always makes a copy.
2508    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    /// Create a variable based on the values currently stored in a tensor. The storage is always
2518    /// copied.
2519    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    /// Reshape returns a tensor with the target shape provided that the number of elements of the
2528    /// original tensor is the same.
2529    /// If the input tensor is contiguous, this is a view on the original data. Otherwise this uses
2530    /// a new storage and copies the data over, the returned tensor is always contiguous.
2531    ///
2532    /// The shape can be specified using a tuple of `usize` and at most one `()` in which case
2533    /// the behavior is the same as when using `-1` in PyTorch: this dimension size is adjusted so
2534    /// as to match the number of elements in the tensor.
2535    ///
2536    /// ```rust
2537    /// # use candle_core::{Tensor, DType, Device, D};
2538    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
2539    ///
2540    /// let c = a.reshape((1, 6))?;
2541    /// assert_eq!(c.shape().dims(), &[1, 6]);
2542    ///
2543    /// let c = a.reshape((3, 2))?;
2544    /// assert_eq!(c.shape().dims(), &[3, 2]);
2545    ///
2546    /// let c = a.reshape((2, (), 1))?;
2547    /// assert_eq!(c.shape().dims(), &[2, 3, 1]);
2548    ///
2549    /// # Ok::<(), candle_core::Error>(())
2550    /// ```
2551    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    /// Creates a new tensor with the specified dimension removed if its size was one.
2582    ///
2583    /// ```rust
2584    /// # use candle_core::{Tensor, DType, Device, D};
2585    /// let a = Tensor::zeros((2, 3, 1), DType::F32, &Device::Cpu)?;
2586    ///
2587    /// let c = a.squeeze(2)?;
2588    /// assert_eq!(c.shape().dims(), &[2, 3]);
2589    ///
2590    /// let c = a.squeeze(D::Minus1)?;
2591    /// assert_eq!(c.shape().dims(), &[2, 3]);
2592    /// # Ok::<(), candle_core::Error>(())
2593    /// ```
2594    pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
2595        // The PyTorch semantics are to return the same tensor if the target dimension
2596        // does not have a size of 1.
2597        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    /// Creates a new tensor with a dimension of size one inserted at the specified position.
2620    ///
2621    /// ```rust
2622    /// # use candle_core::{Tensor, DType, Device, D};
2623    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
2624    ///
2625    /// let c = a.unsqueeze(0)?;
2626    /// assert_eq!(c.shape().dims(), &[1, 2, 3]);
2627    ///
2628    /// let c = a.unsqueeze(D::Minus1)?;
2629    /// assert_eq!(c.shape().dims(), &[2, 3, 1]);
2630    /// # Ok::<(), candle_core::Error>(())
2631    /// ```
2632    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        // Cannot panic because to_index_plus_one already checks dimensions
2637        dims.insert(dim, 1);
2638        // Any stride would work here, but we pick one so as to maximize the probability to remain
2639        // C contiguous.
2640        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    /// Stacks two or more tensors along a particular dimension.
2655    ///
2656    /// All tensors must have the same rank, and the output has one additional rank
2657    ///
2658    /// ```rust
2659    /// # use candle_core::{Tensor, DType, Device};
2660    /// let a = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
2661    /// let b = Tensor::zeros((2, 3), DType::F32, &Device::Cpu)?;
2662    ///
2663    /// let c = Tensor::stack(&[&a, &b], 0)?;
2664    /// assert_eq!(c.shape().dims(), &[2, 2, 3]);
2665    ///
2666    /// let c = Tensor::stack(&[&a, &b], 2)?;
2667    /// assert_eq!(c.shape().dims(), &[2, 3, 2]);
2668    /// # Ok::<(), candle_core::Error>(())
2669    /// ```
2670    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    /// Pad the input tensor using 0s along dimension `dim`. This adds `left` elements before the
2683    /// input tensor values and `right` elements after.
2684    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    /// Pad the input tensor using same values along dimension `dim`. This adds `left` elements before the
2711    /// input tensor values and `right` elements after.
2712    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    /// Run the `forward` method of `m` on `self`.
2751    pub fn apply<M: crate::Module>(&self, m: &M) -> Result<Self> {
2752        m.forward(self)
2753    }
2754
2755    /// Run the `forward` method of `m` on `self`.
2756    pub fn apply_t<M: crate::ModuleT>(&self, m: &M, train: bool) -> Result<Self> {
2757        m.forward_t(self, train)
2758    }
2759
2760    /// Acquire read lock on storage and returns guard.
2761    /// `read_recursive` allows for shared read access.
2762    pub(crate) fn storage(&self) -> StorageRef<'_> {
2763        self.storage.read_recursive()
2764    }
2765
2766    /// Acquire write lock on storage and returns guard.
2767    pub(crate) fn storage_mut(&self) -> StorageMutRef<'_> {
2768        self.storage.write()
2769    }
2770
2771    // If we extend the visibility of this function to be usable outside of this crate, we should
2772    // make it unsafe.
2773    pub(crate) fn storage_mut_and_layout(&self) -> (StorageMutRef<'_>, &Layout) {
2774        let storage = self.storage.write();
2775        (storage, &self.layout)
2776    }
2777
2778    /// The storage used by this tensor, together with the layout to use to access it safely.
2779    pub fn storage_and_layout(&self) -> (StorageRef<'_>, &Layout) {
2780        let storage = self.storage.read();
2781        (storage, &self.layout)
2782    }
2783
2784    /// Unique key for this tensor's storage. Equal keys mean the tensors share the same allocation.
2785    #[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    /// Check if two tensors share the same underlying allocation.
2792    #[inline]
2793    pub(crate) fn same_storage(&self, rhs: &Self) -> bool {
2794        self.storage_key() == rhs.storage_key()
2795    }
2796
2797    /// Normalize a 'relative' axis value: positive values are kept, negative
2798    /// values means counting the dimensions from the back.
2799    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    /// Returns a lower triangular matrix of ones of size n by n.
2815    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    /// Returns an upper triangular matrix of ones of size n by n.
2823    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    /// Returns a matrix with a diagonal of ones of size n by n.
2831    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    /// Returns the cumulative sum of elements of the input tensor summed over the specified
2839    /// dimension.
2840    ///
2841    /// This operation is most efficient when dim is the last dimension of the tensor.
2842    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    /// Returns a copy of `self` where the values within `ranges` have been replaced with the
2861    /// content of `src`.
2862    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(/* on_true= */ &src, /* on_false= */ self)
2914    }
2915
2916    /// Returns log(sum(exp(tensor), dim)).
2917    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    /// Pointwise pow operation.
2934    pub fn pow(&self, rhs: &Tensor) -> Result<Self> {
2935        rhs.mul(&self.log()?)?.exp()
2936    }
2937
2938    /// Broadcasting version of `pow`.
2939    pub fn broadcast_pow(&self, rhs: &Tensor) -> Result<Self> {
2940        rhs.broadcast_mul(&self.log()?)?.exp()
2941    }
2942
2943    /// Returns a new tensor with the order of elements reversed along the specified dimensions.
2944    /// This function makes a copy of the tensor’s data.
2945    ///
2946    /// ```rust
2947    /// # use candle_core::{Tensor, Device};
2948    /// let t = Tensor::arange(0., 6., &Device::Cpu)?.reshape((2, 3))?;
2949    /// assert_eq!(t.to_vec2::<f64>()?, &[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
2950    /// let t_flipped = t.flip(&[0])?;
2951    /// assert_eq!(t_flipped.to_vec2::<f64>()?, &[[3.0, 4.0, 5.0], [0.0, 1.0, 2.0]]);
2952    /// # Ok::<(), candle_core::Error>(())
2953    /// ```
2954    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    /// Returns a view of which contains all slices of size `size` from self tensor in the dimension
2966    /// `dim` and stepped by `step`.
2967    pub fn unfold<D: Dim>(&self, dim: D, size: usize, step: usize) -> Result<Self> {
2968        // https://github.com/pytorch/pytorch/blob/75b0720a97ac5d82e8a7a1a6ae7c5f7a87d7183d/aten/src/ATen/native/TensorShape.cpp#L3785-L3804
2969        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}