use std::sync::Arc;
use crate::{AutogradMetaT, Dim, Dims, Error, Layout, Result, Shape, Storage, WithDType, D};
use crate::{Tensor, TensorId, TensorImpl, Slice};
impl<T: WithDType> Tensor<T> {
pub fn squeeze<D: Dim>(&self, dim: D) -> Result<Self> {
let dims = self.dims();
let dim = dim.to_index(self.shape(), "squeeze")?;
if dims[dim] == 1 {
let mut dims = dims.to_vec();
let mut strides = self.layout().stride().to_vec();
dims.remove(dim);
strides.remove(dim);
let tensor_ = TensorImpl {
id: TensorId::new(),
storage: self.0.storage.clone(),
layout: Layout::new(dims, strides, self.layout().start_offset()),
meta: T::AutogradMeta::on_reshape_op(self)
};
Ok(Self(Arc::new(tensor_)))
} else {
Err( Error::SqueezeDimNot1 { shape: self.shape().clone(), dim } )?
}
}
pub fn unsqueeze<D: Dim>(&self, dim: D) -> Result<Self> {
let mut dims = self.dims().to_vec();
let mut strides = self.layout().stride().to_vec();
let dim = dim.to_index_plus_one(self.shape(), "unsqueeze")?;
dims.insert(dim, 1);
let stride = if dim < strides.len() { strides[dim] } else { 1 };
strides.insert(dim, stride);
let tensor_ = TensorImpl {
id: TensorId::new(),
storage: self.0.storage.clone(),
layout: Layout::new(dims, strides, self.layout().start_offset()),
meta: T::AutogradMeta::on_reshape_op(self),
};
Ok(Self(Arc::new(tensor_)))
}
pub fn narrow<D: Dim>(&self, dim: D, start: usize, len: usize) -> Result<Self> {
let dims = self.dims();
let dim = dim.to_index(self.shape(), "narrow")?;
let err = |msg| {
Err::<(), _>(Error::NarrowInvalidArgs {
shape: self.shape().clone(),
dim,
start,
len,
msg,
})
};
if start > dims[dim] {
err("start > dim_len")?;
}
if start.saturating_add(len) > dims[dim] {
err("start + len > dim_len")?
}
if start == 0 && dims[dim] == len {
Ok(self.clone())
} else {
let meta = T::AutogradMeta::on_narrow_op(self, dim, start, len);
let layout = self.layout().narrow(dim, start, len)?;
let tensor_ = TensorImpl {
id: TensorId::new(),
storage: self.0.storage.clone(),
layout,
meta
};
Ok(Self(Arc::new(tensor_)))
}
}
pub fn slice<D: Dim>(&self, dim: D, slice: &Slice) -> Result<Self> {
let dims = self.dims();
let dim = dim.to_index(self.shape(), "narrow")?;
let err = |msg| {
Err::<(), _>(Error::SliceInvalidArgs {
shape: self.shape().clone(),
dim,
slice: slice.clone(),
msg,
})
};
let end = match slice.end {
Some(end) if end >= 0 => end as usize,
Some(end) => {
let dis = -end as usize;
if dis > dims[dim] {
0
} else {
dims[dim] - dis
}
}
None => dims[dim],
};
if slice.start > dims[dim] {
err("start > dim_len")?;
}
if end > dims[dim] {
err("end > dim_len")?
}
if slice.start == 0 && dims[dim] == end && slice.step == 1 {
Ok(self.clone())
} else {
let meta = T::AutogradMeta::on_slice_op(self, dim, slice.start, end, slice.step);
let layout = self.layout().slice(dim, slice.start, end, slice.step)?;
Ok(self.share_storage(layout, meta))
}
}
pub fn reshape<S: ShapeWithHolder>(&self, shape: S) -> Result<Self> {
let shape = shape.into_shape(self.shape())?;
let meta = T::AutogradMeta::on_reshape_op(self);
if self.is_contiguous() {
let layout = Layout::contiguous_with_offset(shape, self.layout().start_offset());
Ok(self.share_storage(layout, meta))
} else {
let storage = self.storage_read()?.copy(self.layout());
Ok(Self::from_storage(storage, shape, meta))
}
}
pub fn transpose<D1: Dim, D2: Dim>(&self, dim1: D1, dim2: D2) -> Result<Self> {
let dim1 = dim1.to_index(self.shape(), "transpose")?;
let dim2 = dim2.to_index(self.shape(), "transpose")?;
if dim1 == dim2 {
return Ok(self.clone());
}
let meta = T::AutogradMeta::on_transpose_op(self, dim1, dim2);
let layout = self.layout().transpose(dim1, dim2)?;
Ok(self.share_storage(layout, meta))
}
pub fn transpose_last(&self) -> Result<Self> {
self.transpose(D::Minus1, D::Minus2)
}
pub fn permute<D: Dims>(&self, dims: D) -> Result<Self> {
let dims = dims.to_indexes(self.shape(), "permute")?;
let is_permutation =
dims.len() == self.rank() && (0..dims.len()).all(|i| dims.contains(&i));
if !is_permutation {
crate::bail!(
"dimension mismatch in permute, tensor {:?}, dims: {:?}",
self.dims(),
dims
)
}
let layout = self.layout().permute(&dims)?;
let meta = T::AutogradMeta::on_permute_op(self, dims);
Ok(self.share_storage(layout, meta))
}
pub fn cat<A: AsRef<Tensor<T>>, D: Dim>(arrs: &[A], dim: D) -> Result<Self> {
if arrs.is_empty() {
Err(Error::OpRequiresAtLeastOneTensor { op: "cat" })?
}
let arr0 = &arrs[0];
let rank0 = arr0.as_ref().rank();
let cat_dim = dim.to_index(arr0.as_ref().shape(), "cat")?;
let mut target_dims = arr0.as_ref().dims().to_vec();
target_dims[cat_dim] = 0;
let mut dim_offsets = vec![];
for (_arr_index, arr) in arrs.iter().enumerate() {
let rank = arr.as_ref().rank();
if rank != rank0 {
Err(Error::UnexpectedNumberOfDims {
expected: rank,
got: arr.as_ref().rank(),
shape: arr.as_ref().shape().clone(),
})?
}
for (dim_index, (v1, v2)) in arr0.as_ref().dims().iter()
.zip(arr.as_ref().dims().iter())
.enumerate()
{
if dim_index == cat_dim {
dim_offsets.push(target_dims[cat_dim]);
target_dims[cat_dim] += v2;
}
if dim_index != cat_dim && v1 != v2 {
Err(Error::ShapeMismatchCat {
dim: dim_index,
first_shape: arr0.as_ref().shape().clone(),
n: dim_index + 1,
nth_shape: arr0.as_ref().shape().clone(),
})?
}
}
}
let target_shape: Shape = target_dims.into();
let mut dst: Vec<T> = Vec::with_capacity(target_shape.element_count());
unsafe { dst.set_len(target_shape.element_count()) };
let meta = T::AutogradMeta::on_cat_op(arrs, cat_dim);
let res_arr = Self::from_storage(Storage::new(dst), target_shape, meta);
for (arr_index, arr) in arrs.iter().enumerate() {
let sub_res_arr = res_arr.narrow(cat_dim, dim_offsets[arr_index], arr.as_ref().dims()[cat_dim])?;
assert_eq!(sub_res_arr.shape(), arr.as_ref().shape());
sub_res_arr.impl_copy_(arr.as_ref())?;
}
Ok(res_arr)
}
pub fn stack<A: AsRef<Tensor<T>>, D: Dim>(args: &[A], dim: D) -> Result<Self> {
if args.is_empty() {
Err(Error::OpRequiresAtLeastOneTensor { op: "stack" })?
}
let dim = dim.to_index_plus_one(args[0].as_ref().shape(), "stack")?;
let args = args
.iter()
.map(|t| t.as_ref().unsqueeze(dim))
.collect::<Result<Vec<_>>>()?;
Self::cat(&args, dim)
}
pub fn split<D: Dim>(&self, dim: D) -> Result<Vec<Self>> {
let split_index = dim.to_index(self.shape(), "split")?;
let split_dim_size = self.dims()[split_index];
let mut splited_shape = self.dims().to_vec();
splited_shape.remove(split_index);
let mut vec = vec![];
for i in 0..split_dim_size {
let sub_tensor = self.narrow(split_index, i, 1)?.squeeze(split_index)?;
vec.push(sub_tensor);
}
Ok(vec)
}
pub fn chunk<D: Dim>(&self, chunks: usize, dim: D) -> Result<Vec<Self>> {
let dim = dim.to_index(self.shape(), "chunk")?;
let size = self.dim(dim)?;
if size < chunks {
(0..size).map(|i| self.narrow(dim, i, 1)).collect()
} else {
let chunk_size = size / chunks;
let cnt_additional = size % chunks;
let mut tensors = vec![];
let mut sum_chunk_size = 0;
for i in 0..chunks {
let chunk_size = if i < cnt_additional {
chunk_size + 1
} else {
chunk_size
};
let tensor = self.narrow(dim, sum_chunk_size, chunk_size)?;
tensors.push(tensor);
sum_chunk_size += chunk_size
}
Ok(tensors)
}
}
pub fn flatten<D1: Dim, D2: Dim>(&self, start_dim: D1, end_dim: D2) -> Result<Self> {
self.flatten_(Some(start_dim), Some(end_dim))
}
pub fn flatten_to<D: Dim>(&self, end_dim: D) -> Result<Self> {
self.flatten_(None::<usize>, Some(end_dim))
}
pub fn flatten_from<D: Dim>(&self, start_dim: D) -> Result<Self> {
self.flatten_(Some(start_dim), None::<usize>)
}
pub fn flatten_all(&self) -> Result<Self> {
self.flatten_(None::<usize>, None::<usize>)
}
pub fn repeat<S: Into<Shape>>(&self, shape: S) -> Result<Self> {
let repeats: Shape = shape.into();
let mut repeats = repeats.dims().to_vec();
if repeats.len() > self.rank() {
Err(Error::RepeatRankOutOfRange { repeats: repeats.clone().into(), shape: self.shape().into() })?;
} else if repeats.len() > self.rank() {
for _ in 0..(repeats.len() - self.rank()) {
repeats.push(1);
}
}
let mut arr = self.clone();
for (idx, &repeat) in repeats.iter().enumerate() {
if repeat > 1 {
arr = Tensor::cat(&vec![&arr; repeat], idx)?
}
}
Ok(arr)
}
pub fn repeat_dim<D: Dim>(&self, dim: D, times: usize) -> Result<Self> {
if times == 0 {
self.squeeze(dim)
} else if times == 1 {
Ok(self.clone())
} else {
Tensor::cat(&vec![self; times], dim)
}
}
fn flatten_<D1: Dim, D2: Dim>(
&self,
start_dim: Option<D1>,
end_dim: Option<D2>,
) -> Result<Self> {
if self.rank() == 0 {
self.reshape(1)
} else {
let start_dim = match start_dim {
None => 0,
Some(dim) => dim.to_index(self.shape(), "flatten")?,
};
let end_dim = match end_dim {
None => self.rank() - 1,
Some(dim) => dim.to_index(self.shape(), "flatten")?,
};
if start_dim < end_dim {
let dims = self.dims();
let mut dst_dims = dims[..start_dim].to_vec();
dst_dims.push(dims[start_dim..end_dim + 1].iter().product::<usize>());
if end_dim + 1 < dims.len() {
dst_dims.extend(&dims[end_dim + 1..]);
}
self.reshape(dst_dims)
} else {
Ok(self.clone())
}
}
}
}
pub trait ShapeWithHolder {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape>;
}
impl<S: Into<Shape>> ShapeWithHolder for S {
fn into_shape(self, origin_shape: &Shape) -> crate::Result<Shape> {
let shape = self.into();
if shape.element_count() != origin_shape.element_count() {
return Err(Error::ElementCountMismatchInReshape {
origin: origin_shape.clone(),
target: shape,
})?;
}
Ok(shape)
}
}
fn hole_size(shape: &Shape, prod_d: usize, s: &dyn std::fmt::Debug) -> Result<usize> {
let el_count = shape.element_count();
if prod_d == 0 {
crate::bail!("cannot reshape tensor of {el_count} elements to {s:?}")
}
if el_count % prod_d != 0 {
crate::bail!("cannot reshape tensor with {el_count} elements to {s:?}")
}
Ok(el_count / prod_d)
}
impl ShapeWithHolder for ((), usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let ((), d1) = self;
Ok((hole_size(shape, d1, &self)?, d1).into())
}
}
impl ShapeWithHolder for (usize, ()) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, ()) = self;
Ok((d1, hole_size(shape, d1, &self)?).into())
}
}
impl ShapeWithHolder for ((), usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let ((), d1, d2) = self;
Ok((hole_size(shape, d1 * d2, &self)?, d1, d2).into())
}
}
impl ShapeWithHolder for (usize, (), usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, (), d2) = self;
Ok((d1, hole_size(shape, d1 * d2, &self)?, d2).into())
}
}
impl ShapeWithHolder for (usize, usize, ()) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, ()) = self;
Ok((d1, d2, hole_size(shape, d1 * d2, &self)?).into())
}
}
impl ShapeWithHolder for ((), usize, usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let ((), d1, d2, d3) = self;
let d = hole_size(shape, d1 * d2 * d3, &self)?;
Ok((d, d1, d2, d3).into())
}
}
impl ShapeWithHolder for (usize, (), usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, (), d2, d3) = self;
let d = hole_size(shape, d1 * d2 * d3, &self)?;
Ok((d1, d, d2, d3).into())
}
}
impl ShapeWithHolder for (usize, usize, (), usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, (), d3) = self;
let d = hole_size(shape, d1 * d2 * d3, &self)?;
Ok((d1, d2, d, d3).into())
}
}
impl ShapeWithHolder for (usize, usize, usize, ()) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, d3, ()) = self;
let d = hole_size(shape, d1 * d2 * d3, &self)?;
Ok((d1, d2, d3, d).into())
}
}
impl ShapeWithHolder for ((), usize, usize, usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let ((), d1, d2, d3, d4) = self;
let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
Ok((d, d1, d2, d3, d4).into())
}
}
impl ShapeWithHolder for (usize, (), usize, usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, (), d2, d3, d4) = self;
let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
Ok((d1, d, d2, d3, d4).into())
}
}
impl ShapeWithHolder for (usize, usize, (), usize, usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, (), d3, d4) = self;
let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
Ok((d1, d2, d, d3, d4).into())
}
}
impl ShapeWithHolder for (usize, usize, usize, (), usize) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, d3, (), d4) = self;
let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
Ok((d1, d2, d3, d, d4).into())
}
}
impl ShapeWithHolder for (usize, usize, usize, usize, ()) {
fn into_shape(self, shape: &Shape) -> crate::Result<Shape> {
let (d1, d2, d3, d4, ()) = self;
let d = hole_size(shape, d1 * d2 * d3 * d4, &self)?;
Ok((d1, d2, d3, d4, d).into())
}
}
impl<T: WithDType> AsRef<Tensor<T>> for Tensor<T> {
fn as_ref(&self) -> &Tensor<T> {
self
}
}
#[cfg(test)]
#[allow(unused)]
mod test {
use super::*;
#[test]
fn test_unsqueeze_basic() -> Result<()> {
let t = Tensor::<i32>::zeros((2, 3))?;
let unsq0 = t.unsqueeze(0)?;
assert_eq!(unsq0.dims(), &[1, 2, 3]);
assert_eq!(unsq0.to_vec()?, t.to_vec()?);
let unsq1 = t.unsqueeze(1)?;
assert_eq!(unsq1.dims(), &[2, 1, 3]);
assert_eq!(unsq1.to_vec()?, t.to_vec()?);
let unsq2 = t.unsqueeze(2)?;
assert_eq!(unsq2.dims(), &[2, 3, 1]);
assert_eq!(unsq2.to_vec()?, t.to_vec()?);
Ok(())
}
#[test]
fn test_squeeze_basic() -> Result<()> {
let t = Tensor::<i32>::zeros((2, 1, 3))?;
let sq = t.squeeze(1)?;
assert_eq!(sq.dims(), &[2, 3]);
assert_eq!(sq.to_vec()?, t.to_vec()?);
let t2 = Tensor::<i32>::zeros((1, 5))?;
let sq2 = t2.squeeze(0)?;
assert_eq!(sq2.dims(), &[5]);
Ok(())
}
#[test]
fn test_squeeze_unsqueeze_consistency() -> Result<()> {
let t = Tensor::new(&[[1, 2, 3], [4, 5, 6]])?;
let unsq = t.unsqueeze(0)?; let sq = unsq.squeeze(0)?;
assert_eq!(t.dims(), sq.dims());
assert_eq!(t.to_vec()?, sq.to_vec()?);
Ok(())
}
#[test]
fn test_unsqueeze() -> Result<()> {
let t = Tensor::<i32>::zeros((2, 1, 3))?;
let sq = t.squeeze(1)?;
println!("{}", sq);
assert_eq!(sq.dims(), vec![2, 3]);
let unsq = sq.unsqueeze(0)?;
println!("{}", unsq);
assert_eq!(unsq.dims(), vec![1, 2, 3]);
Ok(())
}
#[test]
fn test_cat_3d() -> Result<()> {
let a = Tensor::full((2, 2, 2), 1)?;
let b = Tensor::full((2, 2, 2), 2)?;
let c = Tensor::cat(&[a, b], 0)?;
assert_eq!(c.dims(), [4, 2, 2]);
let c2 = Tensor::cat(&[c.clone(), c.clone()], 1)?;
assert_eq!(c2.dims(), [4, 4, 2]);
Ok(())
}
#[test]
fn test_cat_1d() -> Result<()> {
let a = Tensor::new(&[1, 2, 3])?;
let b = Tensor::new(&[4, 5, 6])?;
let c = Tensor::new(&[7])?;
let res = Tensor::cat(&[a, b, c], 0)?;
assert_eq!(res.dims(), &[7]);
assert_eq!(res.to_vec()?, &[1, 2, 3, 4, 5, 6, 7]);
Ok(())
}
#[test]
fn test_cat_2d_axis0() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?; let b = Tensor::new(&[[5, 6]])?;
let c = Tensor::cat(&[a, b], 0)?;
assert_eq!(c.dims(), &[3, 2]);
assert_eq!(c.to_vec()?, &[1, 2, 3, 4, 5, 6]);
Ok(())
}
#[test]
fn test_cat_2d_axis1() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?; let b = Tensor::new(&[[5], [6]])?;
let c = Tensor::cat(&[a, b], 1)?;
assert_eq!(c.dims(), &[2, 3]);
assert_eq!(c.to_vec()?, &[1, 2, 5, 3, 4, 6]);
Ok(())
}
#[test]
fn test_cat_shape_mismatch() {
let a = Tensor::new(&[[1, 2], [3, 4]]).unwrap(); let b = Tensor::new(&[[1, 2, 3]]).unwrap();
let res = Tensor::cat(&[a, b], 0);
assert!(res.is_err());
}
#[test]
fn test_cat_empty_list_error() {
let res = Tensor::<i32>::cat::<Tensor<i32>, usize>(&[], 0);
assert!(res.is_err(), "Concatenating an empty list should return an error");
}
#[test]
fn test_cat_bool() -> Result<()> {
let a = Tensor::new(&[[true, false]])?;
let b = Tensor::new(&[[false, true]])?;
let c = Tensor::cat(&[a, b], 0)?;
assert_eq!(c.dims(), [2, 2]);
assert_eq!(c.to_vec().unwrap(), [true, false, false, true]);
Ok(())
}
#[test]
fn test_stack_1d_axis0() -> Result<()> {
let a = Tensor::new(&[1, 2, 3])?;
let b = Tensor::new(&[4, 5, 6])?;
let c = Tensor::stack(&[a, b], 0)?;
assert_eq!(c.dims(), [2, 3]);
assert_eq!(c.to_vec().unwrap(), [1, 2, 3, 4, 5, 6]);
Ok(())
}
#[test]
fn test_stack_1d_axis1() -> Result<()> {
let a = Tensor::new(&[1, 2, 3])?;
let b = Tensor::new(&[4, 5, 6])?;
let c = Tensor::stack(&[a, b], 1)?;
assert_eq!(c.dims(), [3, 2]);
assert_eq!(c.to_vec().unwrap(), [1, 4, 2, 5, 3, 6]);
Ok(())
}
#[test]
fn test_stack_2d_axis0() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?;
let b = Tensor::new(&[[5, 6], [7, 8]])?;
let c = Tensor::stack(&[a, b], 0)?;
assert_eq!(c.dims(), [2, 2, 2]);
assert_eq!(c.to_vec().unwrap(), [1, 2, 3, 4, 5, 6, 7, 8]);
Ok(())
}
#[test]
fn test_stack_2d_axis1() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?;
let b = Tensor::new(&[[5, 6], [7, 8]])?;
let c = Tensor::stack(&[a, b], 1)?;
assert_eq!(c.dims(), [2, 2, 2]);
assert_eq!(c.to_vec().unwrap(), [1, 2, 5, 6, 3, 4, 7, 8]);
Ok(())
}
#[test]
fn test_stack_2d_axis2() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?;
let b = Tensor::new(&[[5, 6], [7, 8]])?;
let c = Tensor::stack(&[a, b], 2)?;
assert_eq!(c.dims(), [2, 2, 2]);
assert_eq!(c.to_vec().unwrap(), [1, 5, 2, 6, 3, 7, 4, 8]);
Ok(())
}
#[test]
fn test_stack_shape_mismatch() {
let a = Tensor::new(&[1, 2, 3]).unwrap();
let b = Tensor::new(&[4, 5]).unwrap();
let res = Tensor::stack(&[a, b], 0);
assert!(res.is_err());
}
#[test]
fn test_split_1d() -> Result<()> {
let a = Tensor::new(&[10, 20, 30, 40])?;
let splits = a.split(0)?;
assert_eq!(splits.len(), 4);
assert_eq!(splits[0].to_vec().unwrap(), [10]);
assert_eq!(splits[1].to_vec().unwrap(), [20]);
assert_eq!(splits[2].to_vec().unwrap(), [30]);
assert_eq!(splits[3].to_vec().unwrap(), [40]);
Ok(())
}
#[test]
fn test_split_2d_axis0() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4], [5, 6], [7, 8]])?;
let splits = a.split(0)?;
assert_eq!(splits.len(), 4);
assert_eq!(splits[0].to_vec().unwrap(), [1, 2]);
assert_eq!(splits[1].to_vec().unwrap(), [3, 4]);
assert_eq!(splits[2].to_vec().unwrap(), [5, 6]);
assert_eq!(splits[3].to_vec().unwrap(), [7, 8]);
Ok(())
}
#[test]
fn test_split_2d_axis1() -> Result<()> {
let a = Tensor::new(&[[1, 2, 3], [4, 5, 6]])?;
let splits = a.split(1)?;
assert_eq!(splits.len(), 3);
assert_eq!(splits[0].to_vec().unwrap(), [1, 4]);
assert_eq!(splits[1].to_vec().unwrap(), [2, 5]);
assert_eq!(splits[2].to_vec().unwrap(), [3, 6]);
Ok(())
}
#[test]
fn test_split_3d_axis2() -> Result<()> {
let a = Tensor::new(&[
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
])?;
let splits = a.split(2)?;
assert_eq!(splits.len(), 2);
assert_eq!(splits[0].to_vec().unwrap(), [1, 3, 5, 7]);
assert_eq!(splits[1].to_vec().unwrap(), [2, 4, 6, 8]);
Ok(())
}
#[test]
fn test_split_single_element() -> Result<()> {
let a = Tensor::new(&[42])?;
let splits = a.split(0)?;
assert_eq!(splits.len(), 1);
assert_eq!(splits[0].to_vec().unwrap(), [42]);
Ok(())
}
#[test]
fn test_split_empty_array() -> Result<()> {
let a = Tensor::<i32>::zeros((0, 2))?;
let splits = a.split(0)?;
assert!(splits.is_empty());
Ok(())
}
#[test]
fn test_repeat_1d() -> Result<()> {
let a = Tensor::new(&[1, 2, 3])?;
let b = a.repeat(3)?; assert_eq!(b.dims(), [3 * 3]); assert_eq!(b.to_vec().unwrap(), [1, 2, 3, 1, 2, 3, 1, 2, 3]);
Ok(())
}
#[test]
fn test_reshape_with_holder() -> Result<()> {
let a = Tensor::<f32>::zeros((3, 3, 3))?;
let b = a.reshape(((), 3))?;
assert_eq!(b.dims2()?, (9, 3));
let a = Tensor::<f32>::zeros((9, 12, 3))?;
let b = a.reshape((1, 1, ()))?;
assert_eq!(b.dims3()?, (1, 1, 9 * 12 * 3));
Ok(())
}
#[test]
fn test_repeat_2d() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?;
let b = a.repeat((2, 3))?; assert_eq!(b.dims(), [4, 6]);
assert_eq!(
b.to_vec().unwrap(),
[
1, 2, 1, 2, 1, 2,
3, 4, 3, 4, 3, 4,
1, 2, 1, 2, 1, 2,
3, 4, 3, 4, 3, 4
]
);
Ok(())
}
#[test]
fn test_repeat_dim() -> Result<()> {
let a = Tensor::new(&[1, 2, 3])?;
let b = a.repeat_dim(0, 2)?; assert_eq!(b.dims(), [6]);
assert_eq!(b.to_vec().unwrap(), [1, 2, 3, 1, 2, 3]);
let c = a.repeat_dim(0, 1)?; assert_eq!(c.dims(), [3]);
assert_eq!(c.to_vec().unwrap(), [1, 2, 3]);
Ok(())
}
#[test]
fn test_repeat_high_dim() -> Result<()> {
let a = Tensor::new(&[[1, 2], [3, 4]])?;
let b = a.repeat((2, 3))?; assert_eq!(b.dims(), [4, 6]);
Ok(())
}
#[test]
fn test_narrow_1d_basic() -> Result<()> {
let a = Tensor::new(&[0, 1, 2, 3, 4, 5])?;
let b = a.narrow(0, 2, 3)?;
assert_eq!(b.dims(), &[3]);
assert_eq!(b.to_vec().unwrap(), &[2, 3, 4]);
let b = Tensor::randn(0.0, 1.0, (5, 5))?;
println!("{:?}", b);
Ok(())
}
#[test]
fn test_narrow_2d_rows() -> Result<()> {
let a = Tensor::new(&[
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
])?;
let b = a.narrow(0, 1, 1)?;
assert_eq!(b.dims(), &[1, 3]);
assert_eq!(b.to_vec().unwrap(), &[3, 4, 5]);
Ok(())
}
}