Skip to main content

lumen_core/ops/
shape.rs

1use std::sync::Arc;
2use crate::{AutogradMetaT, Dim, Dims, Error, Layout, Result, Shape, Storage, WithDType, D};
3use crate::{Tensor, TensorId, TensorImpl, Slice};
4
5impl<T: WithDType> Tensor<T> {
6    /// Creates a new tensor with the specified dimension removed if its size was one.
7    ///
8    /// ```rust
9    /// use lumen_core::{Tensor, DType, D};
10    /// let a = Tensor::<f32>::zeros((2, 3, 1)).unwrap();
11    ///
12    /// let c = a.squeeze(2).unwrap();
13    /// assert_eq!(c.shape().dims(), &[2, 3]);
14    /// ```
15    pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
16        let dims = self.dims();
17        let dim = dim.to_index(self.shape(), "squeeze")?;
18        if dims[dim] == 1 {
19            let mut dims = dims.to_vec();
20            let mut strides = self.layout().stride().to_vec();
21            dims.remove(dim);
22            strides.remove(dim);
23            let tensor_ = TensorImpl {
24                id: TensorId::new(),
25                storage: self.0.storage.clone(),
26                layout: Layout::new(dims, strides, self.layout().start_offset()),
27                meta: T::AutogradMeta::on_reshape_op(self)
28            };
29            Ok(Self(Arc::new(tensor_)))
30        } else {
31            Err( Error::SqueezeDimNot1 { shape: self.shape().clone(), dim } )?
32        }
33    }
34
35    /// Creates a new tensor with a dimension of size one inserted at the specified position.
36    ///
37    /// ```rust
38    /// use lumen_core::{Tensor, DType, D};
39    /// let a = Tensor::<f32>::zeros((2, 3)).unwrap();
40    ///
41    /// let c = a.unsqueeze(0).unwrap();
42    /// assert_eq!(c.shape().dims(), &[1, 2, 3]);
43    ///
44    /// let c = a.unsqueeze(D::Minus1).unwrap();
45    /// assert_eq!(c.shape().dims(), &[2, 3, 1]);
46    /// ```
47    pub fn unsqueeze<D: Dim>(&self, dim: D) -> Result<Self> {
48        let mut dims = self.dims().to_vec();
49        let mut strides = self.layout().stride().to_vec();
50        let dim = dim.to_index_plus_one(self.shape(), "unsqueeze")?;
51        dims.insert(dim, 1);
52        let stride = if dim < strides.len() { strides[dim] } else { 1 };
53        strides.insert(dim, stride);
54        let tensor_ = TensorImpl {
55            id: TensorId::new(),
56            storage: self.0.storage.clone(),
57            layout: Layout::new(dims, strides, self.layout().start_offset()),
58            meta: T::AutogradMeta::on_reshape_op(self),
59        };
60        Ok(Self(Arc::new(tensor_)))
61    }
62
63    /// Returns a new tensor that is a narrowed version of the input, the dimension `dim`
64    /// ranges from `start` to `start + len`.
65    /// ```
66    /// use lumen_core::Tensor;
67    /// let a = Tensor::new(&[
68    ///     [0f32, 1., 2.],
69    ///     [3.  , 4., 5.],
70    ///     [6.  , 7., 8.]
71    /// ]).unwrap();
72    ///
73    /// let b = a.narrow(0, 1, 2).unwrap();
74    /// assert_eq!(b.shape().dims(), &[2, 3]);
75    ///
76    /// let c = a.narrow(1, 1, 1).unwrap();
77    /// assert_eq!(c.shape().dims(), &[3, 1]);
78    /// ```
79    pub fn narrow<D: Dim>(&self, dim: D, start: usize, len: usize) -> Result<Self> {
80        let dims = self.dims();
81        let dim = dim.to_index(self.shape(), "narrow")?;
82        let err = |msg| {
83            Err::<(), _>(Error::NarrowInvalidArgs {
84                shape: self.shape().clone(),
85                dim,
86                start,
87                len,
88                msg,
89            })
90        };
91
92        if start > dims[dim] {
93            err("start > dim_len")?;
94        }
95        if start.saturating_add(len) > dims[dim] {
96            err("start + len > dim_len")?
97        }
98        if start == 0 && dims[dim] == len {
99            Ok(self.clone())
100        } else {
101            let meta = T::AutogradMeta::on_narrow_op(self, dim, start, len);
102            let layout = self.layout().narrow(dim, start, len)?;
103            let tensor_ = TensorImpl {
104                id: TensorId::new(),
105                storage: self.0.storage.clone(),
106                layout,
107                meta
108            };
109            Ok(Self(Arc::new(tensor_)))
110        }
111    }
112
113    /// Returns a new tensor that is a narrowed version of the input, the dimension `dim`
114    /// slice from `start` to `start : end : step`.
115    /// 
116    /// ```
117    /// use lumen_core::{Tensor, DType, s, Slice};
118    /// let a = Tensor::<i32>::zeros((5, 5, 5)).unwrap();
119    ///
120    /// let b = a.narrow(0, 1, 2).unwrap();
121    /// assert_eq!(b.shape().dims(), &[2, 5, 5]);
122    ///
123    /// let c = a.slice(1, &s!(::2)).unwrap();
124    /// assert_eq!(c.shape().dims(), &[5, 3, 5]);
125    /// ```
126    pub fn slice<D: Dim>(&self, dim: D, slice: &Slice) -> Result<Self> {
127        let dims = self.dims();
128        let dim = dim.to_index(self.shape(), "narrow")?;
129        let err = |msg| {
130            Err::<(), _>(Error::SliceInvalidArgs {
131                shape: self.shape().clone(),
132                dim,
133                slice: slice.clone(),
134                msg,
135            })
136        };
137
138        let end = match slice.end {
139            Some(end) if end >= 0 => end as usize,
140            Some(end) => {
141                let dis = -end as usize;
142                if dis > dims[dim] {
143                    0
144                } else {
145                    dims[dim] - dis
146                }
147            }
148            None => dims[dim],
149        };
150        if slice.start > dims[dim] {
151            err("start > dim_len")?;
152        }
153        if end > dims[dim] {
154            err("end > dim_len")?
155        }
156        if slice.start == 0 && dims[dim] == end && slice.step == 1 {
157            Ok(self.clone())
158        } else {
159            let meta = T::AutogradMeta::on_slice_op(self, dim, slice.start, end, slice.step);
160            let layout = self.layout().slice(dim, slice.start, end, slice.step)?;
161            Ok(self.share_storage(layout, meta))
162        }
163    }
164
165    /// Reshape returns a tensor with the target shape provided that the number of elements of the
166    /// original tensor is the same.
167    /// If the input tensor is contiguous, this is a view on the original data. Otherwise this uses
168    /// a new storage and copies the data over, the returned tensor is always contiguous.
169    ///
170    /// The shape can be specified using a tuple of `usize` and at most one `()` in which case
171    /// the behavior is the same as when using `-1` in PyTorch: this dimension size is adjusted so
172    /// as to match the number of elements in the tensor.
173    /// 
174    /// ```rust
175    /// use lumen_core::{Tensor, DType, D};
176    /// let a = Tensor::<f32>::zeros((2, 3)).unwrap();
177    ///
178    /// let c = a.reshape((1, 6)).unwrap();
179    /// assert_eq!(c.shape().dims(), &[1, 6]);
180    /// ```
181    pub fn reshape<S: ShapeWithHolder>(&self, shape: S) -> Result<Self> {
182        let shape = shape.into_shape(self.shape())?;
183        let meta = T::AutogradMeta::on_reshape_op(self);
184        if self.is_contiguous() {
185            let layout = Layout::contiguous_with_offset(shape, self.layout().start_offset());
186            Ok(self.share_storage(layout, meta))
187        } else {
188            let storage = self.storage_read()?.copy(self.layout());
189            Ok(Self::from_storage(storage, shape, meta))
190        }
191    }
192    
193    /// Returns a Tensor that is a transposed version of the input, the given dimensions are
194    pub fn transpose<D1: Dim, D2: Dim>(&self, dim1: D1, dim2: D2) -> Result<Self> {
195        let dim1 = dim1.to_index(self.shape(), "transpose")?;
196        let dim2 = dim2.to_index(self.shape(), "transpose")?;
197        if dim1 == dim2 {
198            return Ok(self.clone());
199        }
200
201        let meta = T::AutogradMeta::on_transpose_op(self, dim1, dim2);
202        let layout = self.layout().transpose(dim1, dim2)?;
203        Ok(self.share_storage(layout, meta))
204    }
205
206    pub fn transpose_last(&self) -> Result<Self> {
207        self.transpose(D::Minus1, D::Minus2)
208    }
209
210    /// Returns a tensor with the same data as the input where the dimensions have been permuted.
211    /// dims must be a permutation, i.e. include each dimension index exactly once.
212    ///
213    /// ```rust
214    /// use lumen_core::Tensor;
215    /// let tensor = Tensor::<u32>::arange(0u32, 120u32).unwrap().reshape((2, 3, 4, 5)).unwrap();
216    /// assert_eq!(tensor.dims(), &[2, 3, 4, 5]);
217    /// let tensor = tensor.permute((2, 3, 1, 0)).unwrap();
218    /// assert_eq!(tensor.dims(), &[4, 5, 3, 2]);
219    /// ```
220    pub fn permute<D: Dims>(&self, dims: D) -> Result<Self> {
221        let dims = dims.to_indexes(self.shape(), "permute")?;
222        // O(n^2) permutation check but these arrays are small.
223        let is_permutation =
224            dims.len() == self.rank() && (0..dims.len()).all(|i| dims.contains(&i));
225        if !is_permutation {
226            crate::bail!(
227                "dimension mismatch in permute, tensor {:?}, dims: {:?}",
228                self.dims(),
229                dims
230            )
231        }
232        // let op = BackpropOp::new1(self, |t| Op::Permute(t, dims.clone()));
233        let layout = self.layout().permute(&dims)?;
234        let meta = T::AutogradMeta::on_permute_op(self, dims);
235        Ok(self.share_storage(layout, meta))
236    }
237
238    /// Concatenates two or more tensors along a particular dimension.
239    ///
240    /// All tensors must of the same rank, and the output will have
241    /// the same rank
242    ///
243    /// ```rust
244    /// use lumen_core::Tensor;
245    /// let a = Tensor::<f32>::zeros((2, 3)).unwrap();
246    /// let b = Tensor::<f32>::zeros((2, 3)).unwrap();
247    ///
248    /// let c = Tensor::cat(&[&a, &b], 0).unwrap();
249    /// assert_eq!(c.dims(), &[4, 3]);
250    ///
251    /// let c = Tensor::cat(&[&a, &b], 1).unwrap();
252    /// assert_eq!(c.dims(), &[2, 6]);
253    /// ```
254    pub fn cat<A: AsRef<Tensor<T>>, D: Dim>(arrs: &[A], dim: D) -> Result<Self> {
255        // check shape
256        if arrs.is_empty() {
257            Err(Error::OpRequiresAtLeastOneTensor { op: "cat" })?
258        }
259    
260        // first arr's infomation
261        let arr0 = &arrs[0];
262        let rank0 = arr0.as_ref().rank();
263
264        // cat_dim must be valid!
265        let cat_dim = dim.to_index(arr0.as_ref().shape(), "cat")?;
266        let mut target_dims = arr0.as_ref().dims().to_vec();
267        target_dims[cat_dim] = 0;
268        let mut dim_offsets = vec![];
269
270        for (_arr_index, arr) in arrs.iter().enumerate() {
271            // check shape 
272            let rank = arr.as_ref().rank();
273            if rank != rank0 {
274                Err(Error::UnexpectedNumberOfDims {
275                    expected: rank,
276                    got: arr.as_ref().rank(),
277                    shape: arr.as_ref().shape().clone(),
278                })?
279            }
280
281            // zip arr0's dims and arr's dims
282            for (dim_index, (v1, v2)) in arr0.as_ref().dims().iter()
283                                                                    .zip(arr.as_ref().dims().iter())
284                                                                    .enumerate()
285            {
286                // accumalte the cat dim
287                if dim_index == cat_dim {
288                    dim_offsets.push(target_dims[cat_dim]);
289                    target_dims[cat_dim] += v2;
290                }
291
292                // all other dims should be same
293                if dim_index != cat_dim && v1 != v2 {
294                    Err(Error::ShapeMismatchCat {
295                        dim: dim_index,
296                        first_shape: arr0.as_ref().shape().clone(),
297                        n: dim_index + 1,
298                        nth_shape: arr0.as_ref().shape().clone(),
299                    })?
300                }
301            }
302        }
303        
304        // Now, all arr in arrs has same rank, and except `cat_dim`, all dims are equal
305        // [ (a, n1, b, c), (a, n2, b, c), ... , (a, nk, b, c).... ]
306        // target_dims = (a, n1+n2+...+nk, b, c)
307
308        let target_shape: Shape = target_dims.into();
309        
310        // Create a new storgae and copy
311        let mut dst: Vec<T> = Vec::with_capacity(target_shape.element_count());
312        unsafe { dst.set_len(target_shape.element_count()) };
313        
314        let meta = T::AutogradMeta::on_cat_op(arrs, cat_dim);
315        let res_arr = Self::from_storage(Storage::new(dst), target_shape, meta);
316
317        for (arr_index, arr) in arrs.iter().enumerate() {
318            // Take sub Tensor 
319            let sub_res_arr = res_arr.narrow(cat_dim, dim_offsets[arr_index], arr.as_ref().dims()[cat_dim])?;
320            assert_eq!(sub_res_arr.shape(), arr.as_ref().shape());
321            sub_res_arr.impl_copy_(arr.as_ref())?;
322        }
323
324        Ok(res_arr)
325    }
326
327    /// Stacks two or more tensors along a particular dimension.
328    ///
329    /// All tensors must have the same rank, and the output has one additional rank
330    ///
331    /// ```rust
332    /// use lumen_core::Tensor;
333    /// let a = Tensor::<f32>::zeros((2, 3)).unwrap();
334    /// let b = Tensor::<f32>::zeros((2, 3)).unwrap();
335    ///
336    /// let c = Tensor::stack(&[&a, &b], 0).unwrap();
337    /// assert_eq!(c.dims(), &[2, 2, 3]);
338    ///
339    /// let c = Tensor::stack(&[&a, &b], 2).unwrap();
340    /// assert_eq!(c.dims(), &[2, 3, 2]);
341    /// ```
342    pub fn stack<A: AsRef<Tensor<T>>, D: Dim>(args: &[A], dim: D) -> Result<Self> {
343        if args.is_empty() {
344            Err(Error::OpRequiresAtLeastOneTensor { op: "stack" })?
345        }
346        let dim = dim.to_index_plus_one(args[0].as_ref().shape(), "stack")?;
347        let args = args
348            .iter()
349            .map(|t| t.as_ref().unsqueeze(dim))
350            .collect::<Result<Vec<_>>>()?;
351        Self::cat(&args, dim)
352    }
353
354    /// Splits a tensor along a specified dimension into multiple sub-tensors.
355    ///
356    /// The tensor is split along the given `dim` into as many sub-tensors as
357    /// the size of that dimension. Each sub-tensor has the same shape as the
358    /// original tensor, except the size along `dim` becomes 1.
359    ///
360    /// ```rust
361    /// use lumen_core::Tensor;
362    ///
363    /// let a = Tensor::new(&[[1, 2], [3, 4], [5, 6]]).unwrap();
364    ///
365    /// // Split along axis 0 (rows)
366    /// let splits = a.split(0).unwrap();
367    /// assert_eq!(splits.len(), 3);
368    /// assert_eq!(splits[0].to_vec().unwrap(), [1, 2]);
369    /// assert_eq!(splits[1].to_vec().unwrap(), [3, 4]);
370    /// assert_eq!(splits[2].to_vec().unwrap(), [5, 6]);
371    ///
372    /// // Split along axis 1 (columns)
373    /// let splits = a.split(1).unwrap();
374    /// assert_eq!(splits.len(), 2);
375    /// assert_eq!(splits[0].to_vec().unwrap(), [1, 3, 5]);
376    /// assert_eq!(splits[1].to_vec().unwrap(), [2, 4, 6]);
377    ///
378    /// // 1D array
379    /// let b = Tensor::new(&[10, 20, 30]).unwrap();
380    /// let splits = b.split(0).unwrap();
381    /// assert_eq!(splits.len(), 3);
382    /// assert_eq!(splits[0].to_vec().unwrap(), [10]);
383    /// assert_eq!(splits[1].to_vec().unwrap(), [20]);
384    /// assert_eq!(splits[2].to_vec().unwrap(), [30]);
385    /// ```
386    pub fn split<D: Dim>(&self, dim: D) -> Result<Vec<Self>> {
387        let split_index = dim.to_index(self.shape(), "split")?;
388        let split_dim_size = self.dims()[split_index];
389        let mut splited_shape = self.dims().to_vec();
390        splited_shape.remove(split_index);
391
392        let mut vec = vec![];  
393        for i in 0..split_dim_size {
394            let sub_tensor = self.narrow(split_index, i, 1)?.squeeze(split_index)?;
395            vec.push(sub_tensor);
396        }
397
398        Ok(vec)
399    }
400
401    /// Split a tensor into the specified number of chunks, this may return less chunks than
402    /// specified.
403    pub fn chunk<D: Dim>(&self, chunks: usize, dim: D) -> Result<Vec<Self>> {
404        let dim = dim.to_index(self.shape(), "chunk")?;
405        let size = self.dim(dim)?;
406        if size < chunks {
407            (0..size).map(|i| self.narrow(dim, i, 1)).collect()
408        } else {
409            let chunk_size = size / chunks;
410            let cnt_additional = size % chunks;
411            let mut tensors = vec![];
412            let mut sum_chunk_size = 0;
413            for i in 0..chunks {
414                let chunk_size = if i < cnt_additional {
415                    chunk_size + 1
416                } else {
417                    chunk_size
418                };
419                let tensor = self.narrow(dim, sum_chunk_size, chunk_size)?;
420                tensors.push(tensor);
421                sum_chunk_size += chunk_size
422            }
423            Ok(tensors)
424        }
425    }
426
427    /// Flattens the input tensor on the dimension indexes from `start_dim` to `end_dim` (both
428    /// inclusive).
429    pub fn flatten<D1: Dim, D2: Dim>(&self, start_dim: D1, end_dim: D2) -> Result<Self> {
430        self.flatten_(Some(start_dim), Some(end_dim))
431    }
432
433    /// Flattens the input tensor on the dimension indexes from `0` to `end_dim` (inclusive).
434    pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Self> {
435        self.flatten_(None::<usize>, Some(end_dim))
436    }
437
438    /// Flattens the input tensor on the dimension indexes from `start_dim` (inclusive) to the last
439    /// dimension.
440    pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Self> {
441        self.flatten_(Some(start_dim), None::<usize>)
442    }
443
444    /// Flattens the input tensor by reshaping it into a one dimension tensor.
445    /// 
446    /// ```rust
447    /// use lumen_core::Tensor;
448    /// let arr = Tensor::new(&[[0f32, 1.], [2., 3.], [4., 5.]]).unwrap();
449    /// let arr = arr.flatten_all().unwrap();
450    /// let len = arr.dims1().unwrap();
451    /// assert_eq!(len, 6);
452    /// assert_eq!(arr.to_vec().unwrap(), [0., 1., 2., 3., 4., 5.]);
453    /// ```
454    pub fn flatten_all(&self) -> Result<Self> {
455        self.flatten_(None::<usize>, None::<usize>)
456    }
457
458    /// Repeat this tensor along the specified dimensions.
459    pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
460        let repeats: Shape = shape.into();
461        let mut repeats = repeats.dims().to_vec();
462
463        if repeats.len() > self.rank() {
464            Err(Error::RepeatRankOutOfRange { repeats: repeats.clone().into(), shape: self.shape().into() })?;
465        } else if repeats.len() > self.rank() {
466            for _ in 0..(repeats.len() - self.rank()) {
467                repeats.push(1);
468            }
469        }
470
471        let mut arr = self.clone();
472
473        for (idx, &repeat) in repeats.iter().enumerate() {
474            if repeat > 1 {
475                arr = Tensor::cat(&vec![&arr; repeat], idx)?
476            }
477        }
478        Ok(arr)
479    }
480
481    /// Repeat this tensor along the specified dimension with specified times
482    pub fn repeat_dim<D: Dim>(&self, dim: D, times: usize) -> Result<Self> {
483        if times == 0 {
484            self.squeeze(dim)
485        } else if times == 1 {
486            Ok(self.clone())
487        } else {
488            Tensor::cat(&vec![self; times], dim)
489        }
490    }
491
492    fn flatten_<D1: Dim, D2: Dim>(
493        &self,
494        start_dim: Option<D1>,
495        end_dim: Option<D2>,
496    ) -> Result<Self> {
497        if self.rank() == 0 {
498            self.reshape(1)
499        } else {
500            let start_dim = match start_dim {
501                None => 0,
502                Some(dim) => dim.to_index(self.shape(), "flatten")?,
503            };
504            let end_dim = match end_dim {
505                None => self.rank() - 1,
506                Some(dim) => dim.to_index(self.shape(), "flatten")?,
507            };
508            if start_dim < end_dim {
509                let dims = self.dims();
510                let mut dst_dims = dims[..start_dim].to_vec();
511                dst_dims.push(dims[start_dim..end_dim + 1].iter().product::<usize>());
512                if end_dim + 1 < dims.len() {
513                    dst_dims.extend(&dims[end_dim + 1..]);
514                }
515                self.reshape(dst_dims)
516            } else {
517                Ok(self.clone())
518            }
519        }
520    }
521}
522
523pub trait ShapeWithHolder {
524    fn into_shape(self, shape: &Shape) -> crate::Result<Shape>;
525}
526
527impl<S: Into<Shape>> ShapeWithHolder for S {
528    fn into_shape(self, origin_shape: &Shape) -> crate::Result<Shape> {
529        let shape = self.into();
530        if shape.element_count() != origin_shape.element_count() {
531            return Err(Error::ElementCountMismatchInReshape {
532                origin: origin_shape.clone(),
533                target: shape,
534            })?;
535        }
536        Ok(shape)
537    }
538}
539
540fn hole_size(shape: &Shape, prod_d: usize, s: &dyn std::fmt::Debug) -> Result<usize> {
541    let el_count = shape.element_count();
542    if prod_d == 0 {
543        crate::bail!("cannot reshape tensor of {el_count} elements to {s:?}")
544    }
545    if el_count % prod_d != 0 {
546        crate::bail!("cannot reshape tensor with {el_count} elements to {s:?}")
547    }
548    Ok(el_count / prod_d)
549}
550
551impl ShapeWithHolder for ((), usize) {
552    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
553        let ((), d1) = self;
554        Ok((hole_size(shape, d1, &self)?, d1).into())
555    }
556}
557
558impl ShapeWithHolder for (usize, ()) {
559    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
560        let (d1, ()) = self;
561        Ok((d1, hole_size(shape, d1, &self)?).into())
562    }
563}
564
565impl ShapeWithHolder for ((), usize, usize) {
566    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
567        let ((), d1, d2) = self;
568        Ok((hole_size(shape, d1 * d2, &self)?, d1, d2).into())
569    }
570}
571
572impl ShapeWithHolder for (usize, (), usize) {
573    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
574        let (d1, (), d2) = self;
575        Ok((d1, hole_size(shape, d1 * d2, &self)?, d2).into())
576    }
577}
578
579impl ShapeWithHolder for (usize, usize, ()) {
580    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
581        let (d1, d2, ()) = self;
582        Ok((d1, d2, hole_size(shape, d1 * d2, &self)?).into())
583    }
584}
585
586impl ShapeWithHolder for ((), usize, usize, usize) {
587    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
588        let ((), d1, d2, d3) = self;
589        let d = hole_size(shape, d1 * d2 * d3, &self)?;
590        Ok((d, d1, d2, d3).into())
591    }
592}
593
594impl ShapeWithHolder for (usize, (), usize, usize) {
595    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
596        let (d1, (), d2, d3) = self;
597        let d = hole_size(shape, d1 * d2 * d3, &self)?;
598        Ok((d1, d, d2, d3).into())
599    }
600}
601
602impl ShapeWithHolder for (usize, usize, (), usize) {
603    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
604        let (d1, d2, (), d3) = self;
605        let d = hole_size(shape, d1 * d2 * d3, &self)?;
606        Ok((d1, d2, d, d3).into())
607    }
608}
609
610impl ShapeWithHolder for (usize, usize, usize, ()) {
611    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
612        let (d1, d2, d3, ()) = self;
613        let d = hole_size(shape, d1 * d2 * d3, &self)?;
614        Ok((d1, d2, d3, d).into())
615    }
616}
617
618impl ShapeWithHolder for ((), usize, usize, usize, usize) {
619    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
620        let ((), d1, d2, d3, d4) = self;
621        let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
622        Ok((d, d1, d2, d3, d4).into())
623    }
624}
625
626impl ShapeWithHolder for (usize, (), usize, usize, usize) {
627    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
628        let (d1, (), d2, d3, d4) = self;
629        let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
630        Ok((d1, d, d2, d3, d4).into())
631    }
632}
633
634impl ShapeWithHolder for (usize, usize, (), usize, usize) {
635    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
636        let (d1, d2, (), d3, d4) = self;
637        let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
638        Ok((d1, d2, d, d3, d4).into())
639    }
640}
641
642impl ShapeWithHolder for (usize, usize, usize, (), usize) {
643    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
644        let (d1, d2, d3, (), d4) = self;
645        let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
646        Ok((d1, d2, d3, d, d4).into())
647    }
648}
649
650impl ShapeWithHolder for (usize, usize, usize, usize, ()) {
651    fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
652        let (d1, d2, d3, d4, ()) = self;
653        let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
654        Ok((d1, d2, d3, d4, d).into())
655    }
656}
657
658impl<T: WithDType> AsRef<Tensor<T>> for Tensor<T> {
659    fn as_ref(&self) -> &Tensor<T> {
660        self
661    }
662}
663
664#[cfg(test)]
665#[allow(unused)]
666mod test {
667    use super::*;
668
669    #[test]
670    fn test_unsqueeze_basic() -> Result<()> {
671        let t = Tensor::<i32>::zeros((2, 3))?;
672        
673        // Unsqueeze axis 0
674        let unsq0 = t.unsqueeze(0)?;
675        assert_eq!(unsq0.dims(), &[1, 2, 3]);
676        assert_eq!(unsq0.to_vec()?, t.to_vec()?);
677
678        // Unsqueeze axis 1
679        let unsq1 = t.unsqueeze(1)?;
680        assert_eq!(unsq1.dims(), &[2, 1, 3]);
681        assert_eq!(unsq1.to_vec()?, t.to_vec()?);
682
683        // Unsqueeze last axis
684        let unsq2 = t.unsqueeze(2)?;
685        assert_eq!(unsq2.dims(), &[2, 3, 1]);
686        assert_eq!(unsq2.to_vec()?, t.to_vec()?);
687
688        Ok(())
689    }
690
691    #[test]
692    fn test_squeeze_basic() -> Result<()> {
693        let t = Tensor::<i32>::zeros((2, 1, 3))?;
694        
695        let sq = t.squeeze(1)?;
696        assert_eq!(sq.dims(), &[2, 3]);
697        assert_eq!(sq.to_vec()?, t.to_vec()?);
698        
699        // Squeezing a non-1 dimension usually remains unchanged or returns error
700        // depending on implementation. Assuming strict behavior (error) or 
701        // identity if your API allows "squeeze if 1". 
702        // Here we test the successful case of squeezing a singleton.
703        let t2 = Tensor::<i32>::zeros((1, 5))?;
704        let sq2 = t2.squeeze(0)?;
705        assert_eq!(sq2.dims(), &[5]);
706
707        Ok(())
708    }
709
710    #[test]
711    fn test_squeeze_unsqueeze_consistency() -> Result<()> {
712        let t = Tensor::new(&[[1, 2, 3], [4, 5, 6]])?; // shape [2, 3]
713        
714        let unsq = t.unsqueeze(0)?; // [1, 2, 3]
715        let sq = unsq.squeeze(0)?;  // [2, 3]
716        
717        assert_eq!(t.dims(), sq.dims());
718        assert_eq!(t.to_vec()?, sq.to_vec()?);
719        Ok(())
720    }
721
722    #[test]
723    fn test_unsqueeze() -> Result<()> {
724        let t = Tensor::<i32>::zeros((2, 1, 3))?;
725        let sq = t.squeeze(1)?;
726        println!("{}", sq);
727        assert_eq!(sq.dims(), vec![2, 3]);
728
729        let unsq = sq.unsqueeze(0)?;
730        println!("{}", unsq);
731        assert_eq!(unsq.dims(), vec![1, 2, 3]);
732
733        Ok(())
734    }
735    
736    #[test]
737    fn test_cat_3d() -> Result<()> {
738        let a = Tensor::full((2, 2, 2), 1)?;
739        let b = Tensor::full((2, 2, 2), 2)?;
740    
741        let c = Tensor::cat(&[a, b], 0)?;
742        assert_eq!(c.dims(), [4, 2, 2]);
743    
744        let c2 = Tensor::cat(&[c.clone(), c.clone()], 1)?;
745        assert_eq!(c2.dims(), [4, 4, 2]);
746    
747        Ok(())
748    }
749
750    #[test]
751    fn test_cat_1d() -> Result<()> {
752        let a = Tensor::new(&[1, 2, 3])?;
753        let b = Tensor::new(&[4, 5, 6])?;
754        let c = Tensor::new(&[7])?;
755    
756        let res = Tensor::cat(&[a, b, c], 0)?;
757        assert_eq!(res.dims(), &[7]);
758        assert_eq!(res.to_vec()?, &[1, 2, 3, 4, 5, 6, 7]);
759    
760        Ok(())
761    }
762    
763    #[test]
764    fn test_cat_2d_axis0() -> Result<()> {
765        let a = Tensor::new(&[[1, 2], [3, 4]])?; // [2, 2]
766        let b = Tensor::new(&[[5, 6]])?;         // [1, 2]
767    
768        let c = Tensor::cat(&[a, b], 0)?;
769        assert_eq!(c.dims(), &[3, 2]);
770        assert_eq!(c.to_vec()?, &[1, 2, 3, 4, 5, 6]);
771    
772        Ok(())
773    }
774    
775    #[test]
776    fn test_cat_2d_axis1() -> Result<()> {
777        let a = Tensor::new(&[[1, 2], [3, 4]])?; // [2, 2]
778        let b = Tensor::new(&[[5], [6]])?;       // [2, 1]
779    
780        let c = Tensor::cat(&[a, b], 1)?;
781        assert_eq!(c.dims(), &[2, 3]);
782        // Row 1: 1, 2, 5. Row 2: 3, 4, 6.
783        assert_eq!(c.to_vec()?, &[1, 2, 5, 3, 4, 6]);
784    
785        Ok(())
786    }
787
788    #[test]
789    fn test_cat_shape_mismatch() {
790        // Mismatch on non-cat axis
791        let a = Tensor::new(&[[1, 2], [3, 4]]).unwrap(); // [2, 2]
792        let b = Tensor::new(&[[1, 2, 3]]).unwrap();      // [1, 3]
793    
794        // Try to cat on axis 0, axis 1 mismatch (2 vs 3)
795        let res = Tensor::cat(&[a, b], 0);
796        assert!(res.is_err());
797    }
798
799    #[test]
800    fn test_cat_empty_list_error() {
801        let res = Tensor::<i32>::cat::<Tensor<i32>, usize>(&[], 0);
802        assert!(res.is_err(), "Concatenating an empty list should return an error");
803    }
804    
805    #[test]
806    fn test_cat_bool() -> Result<()> {
807        let a = Tensor::new(&[[true, false]])?;
808        let b = Tensor::new(&[[false, true]])?;
809    
810        let c = Tensor::cat(&[a, b], 0)?;
811        assert_eq!(c.dims(), [2, 2]);
812        assert_eq!(c.to_vec().unwrap(), [true, false, false, true]);
813    
814        Ok(())
815    }
816    
817    #[test]
818    fn test_stack_1d_axis0() -> Result<()> {
819        let a = Tensor::new(&[1, 2, 3])?;
820        let b = Tensor::new(&[4, 5, 6])?;
821    
822        let c = Tensor::stack(&[a, b], 0)?;
823        assert_eq!(c.dims(), [2, 3]); 
824        assert_eq!(c.to_vec().unwrap(), [1, 2, 3, 4, 5, 6]);
825    
826        Ok(())
827    }
828    
829    #[test]
830    fn test_stack_1d_axis1() -> Result<()> {
831        let a = Tensor::new(&[1, 2, 3])?;
832        let b = Tensor::new(&[4, 5, 6])?;
833    
834        let c = Tensor::stack(&[a, b], 1)?;
835        assert_eq!(c.dims(), [3, 2]);
836        assert_eq!(c.to_vec().unwrap(), [1, 4, 2, 5, 3, 6]);
837    
838        Ok(())
839    }
840    
841    #[test]
842    fn test_stack_2d_axis0() -> Result<()> {
843        let a = Tensor::new(&[[1, 2], [3, 4]])?;
844        let b = Tensor::new(&[[5, 6], [7, 8]])?;
845    
846        let c = Tensor::stack(&[a, b], 0)?;
847        assert_eq!(c.dims(), [2, 2, 2]);
848        assert_eq!(c.to_vec().unwrap(), [1, 2, 3, 4, 5, 6, 7, 8]);
849    
850        Ok(())
851    }
852    
853    #[test]
854    fn test_stack_2d_axis1() -> Result<()> {
855        let a = Tensor::new(&[[1, 2], [3, 4]])?;
856        let b = Tensor::new(&[[5, 6], [7, 8]])?;
857    
858        let c = Tensor::stack(&[a, b], 1)?;
859        assert_eq!(c.dims(), [2, 2, 2]);
860        assert_eq!(c.to_vec().unwrap(), [1, 2, 5, 6, 3, 4, 7, 8]);
861    
862        Ok(())
863    }
864    
865    #[test]
866    fn test_stack_2d_axis2() -> Result<()> {
867        let a = Tensor::new(&[[1, 2], [3, 4]])?;
868        let b = Tensor::new(&[[5, 6], [7, 8]])?;
869    
870        let c = Tensor::stack(&[a, b], 2)?;
871        assert_eq!(c.dims(), [2, 2, 2]);
872        assert_eq!(c.to_vec().unwrap(), [1, 5, 2, 6, 3, 7, 4, 8]);
873    
874        Ok(())
875    }
876    
877    #[test]
878    fn test_stack_shape_mismatch() {
879        let a = Tensor::new(&[1, 2, 3]).unwrap();
880        let b = Tensor::new(&[4, 5]).unwrap();
881    
882        let res = Tensor::stack(&[a, b], 0);
883        assert!(res.is_err());
884    }
885    
886    #[test]
887    fn test_split_1d() -> Result<()> {
888        let a = Tensor::new(&[10, 20, 30, 40])?;
889        let splits = a.split(0)?; // axis 0
890    
891        assert_eq!(splits.len(), 4);
892        assert_eq!(splits[0].to_vec().unwrap(), [10]);
893        assert_eq!(splits[1].to_vec().unwrap(), [20]);
894        assert_eq!(splits[2].to_vec().unwrap(), [30]);
895        assert_eq!(splits[3].to_vec().unwrap(), [40]);
896    
897        Ok(())
898    }
899
900    #[test]
901    fn test_split_2d_axis0() -> Result<()> {
902        let a = Tensor::new(&[[1, 2], [3, 4], [5, 6], [7, 8]])?;
903        let splits = a.split(0)?;
904        
905        assert_eq!(splits.len(), 4);
906        assert_eq!(splits[0].to_vec().unwrap(), [1, 2]);
907        assert_eq!(splits[1].to_vec().unwrap(), [3, 4]);
908        assert_eq!(splits[2].to_vec().unwrap(), [5, 6]);
909        assert_eq!(splits[3].to_vec().unwrap(), [7, 8]);
910        
911        Ok(())
912    }
913    
914    #[test]
915    fn test_split_2d_axis1() -> Result<()> {
916        let a = Tensor::new(&[[1, 2, 3], [4, 5, 6]])?;
917        let splits = a.split(1)?;
918        
919        assert_eq!(splits.len(), 3);
920        assert_eq!(splits[0].to_vec().unwrap(), [1, 4]); 
921        assert_eq!(splits[1].to_vec().unwrap(), [2, 5]); 
922        assert_eq!(splits[2].to_vec().unwrap(), [3, 6]); 
923        
924        Ok(())
925    }
926    
927    #[test]
928    fn test_split_3d_axis2() -> Result<()> {
929        let a = Tensor::new(&[
930            [[1, 2], [3, 4]],
931            [[5, 6], [7, 8]]
932        ])?;
933        let splits = a.split(2)?;
934        
935        assert_eq!(splits.len(), 2);
936        assert_eq!(splits[0].to_vec().unwrap(), [1, 3, 5, 7]); 
937        assert_eq!(splits[1].to_vec().unwrap(), [2, 4, 6, 8]); 
938        
939        Ok(())
940    }
941    
942    #[test]
943    fn test_split_single_element() -> Result<()> {
944        let a = Tensor::new(&[42])?;
945        let splits = a.split(0)?;
946        
947        assert_eq!(splits.len(), 1);
948        assert_eq!(splits[0].to_vec().unwrap(), [42]);
949        
950        Ok(())
951    }
952    
953    #[test]
954    fn test_split_empty_array() -> Result<()> {
955        let a = Tensor::<i32>::zeros((0, 2))?;
956        let splits = a.split(0)?;
957        
958        assert!(splits.is_empty()); 
959        Ok(())
960    }
961
962    #[test]
963    fn test_repeat_1d() -> Result<()> {
964        let a = Tensor::new(&[1, 2, 3])?;
965        let b = a.repeat(3)?; // repeat each dimension 3 times
966        assert_eq!(b.dims(), [3 * 3]); // shape: [9]
967        assert_eq!(b.to_vec().unwrap(), [1, 2, 3, 1, 2, 3, 1, 2, 3]);
968        Ok(())
969    }
970
971    #[test]
972    fn test_reshape_with_holder() -> Result<()> {
973        let a = Tensor::<f32>::zeros((3, 3, 3))?;
974        let b = a.reshape(((), 3))?;
975        assert_eq!(b.dims2()?, (9, 3));
976
977        let a = Tensor::<f32>::zeros((9, 12, 3))?;
978        let b = a.reshape((1, 1, ()))?;
979        assert_eq!(b.dims3()?, (1, 1, 9 * 12 * 3));
980        Ok(())
981    }
982
983    #[test]
984    fn test_repeat_2d() -> Result<()> {
985        let a = Tensor::new(&[[1, 2], [3, 4]])?;
986        let b = a.repeat((2, 3))?; // repeat 2 times along axis 0, 3 times along axis 1
987        assert_eq!(b.dims(), [4, 6]);
988        assert_eq!(
989            b.to_vec().unwrap(),
990            [
991                1, 2, 1, 2, 1, 2,
992                3, 4, 3, 4, 3, 4,
993                1, 2, 1, 2, 1, 2,
994                3, 4, 3, 4, 3, 4
995            ]
996        );
997        Ok(())
998    }
999
1000    #[test]
1001    fn test_repeat_dim() -> Result<()> {
1002        let a = Tensor::new(&[1, 2, 3])?;
1003        let b = a.repeat_dim(0, 2)?; // repeat along axis 0 two times
1004        assert_eq!(b.dims(), [6]);
1005        assert_eq!(b.to_vec().unwrap(), [1, 2, 3, 1, 2, 3]);
1006
1007        let c = a.repeat_dim(0, 1)?; // repeat once -> same as clone
1008        assert_eq!(c.dims(), [3]);
1009        assert_eq!(c.to_vec().unwrap(), [1, 2, 3]);
1010
1011        Ok(())
1012    }
1013
1014    #[test]
1015    fn test_repeat_high_dim() -> Result<()> {
1016        let a = Tensor::new(&[[1, 2], [3, 4]])?;
1017        let b = a.repeat((2, 3))?; // more dims than array, extra dims should be treated as 1
1018        assert_eq!(b.dims(), [4, 6]);
1019        Ok(())
1020    }
1021
1022    #[test]
1023    fn test_narrow_1d_basic() -> Result<()> {
1024        let a = Tensor::new(&[0, 1, 2, 3, 4, 5])?;
1025        
1026        let b = a.narrow(0, 2, 3)?;
1027        
1028        assert_eq!(b.dims(), &[3]);
1029        assert_eq!(b.to_vec().unwrap(), &[2, 3, 4]);
1030
1031        let b = Tensor::randn(0.0, 1.0, (5, 5))?;
1032        println!("{:?}", b);
1033        Ok(())
1034    }
1035
1036    #[test]
1037    fn test_narrow_2d_rows() -> Result<()> {
1038        // Shape: [3, 3]
1039        // [ 0,  1,  2 ]
1040        // [ 3,  4,  5 ]
1041        // [ 6,  7,  8 ]
1042        let a = Tensor::new(&[
1043            [0, 1, 2], 
1044            [3, 4, 5], 
1045            [6, 7, 8]
1046        ])?;
1047
1048        let b = a.narrow(0, 1, 1)?;
1049
1050        assert_eq!(b.dims(), &[1, 3]);
1051        assert_eq!(b.to_vec().unwrap(), &[3, 4, 5]);
1052        Ok(())
1053    }
1054}