#![allow(clippy::result_large_err)]
use scirs2_core::ndarray::{ArrayD, IxDyn};
use tenflowers_core::tensor::TensorStorage;
use tenflowers_core::{Result, Tensor, TensorError};
pub fn from_ndarray<T>(array: ArrayD<T>) -> Tensor<T>
where
T: Clone + Default,
{
Tensor::from_array(array)
}
pub fn to_ndarray<T>(tensor: &Tensor<T>) -> Result<ArrayD<T>>
where
T: Clone + Default,
{
match &tensor.storage {
TensorStorage::Cpu(arr) => {
Ok(arr.clone())
}
#[cfg(feature = "gpu")]
TensorStorage::Gpu(_) => Err(TensorError::unsupported_device(
"to_ndarray",
"gpu",
true, )),
}
}
pub fn round_trip<T>(tensor: &Tensor<T>) -> Result<Tensor<T>>
where
T: Clone + Default,
{
let array = to_ndarray(tensor)?;
Ok(from_ndarray(array))
}
pub fn from_slice_with_shape<T>(data: Vec<T>, shape: &[usize]) -> Result<Tensor<T>>
where
T: Clone + Default,
{
let total: usize = shape.iter().product();
if data.len() != total {
return Err(TensorError::invalid_shape_simple(format!(
"data length {} does not match shape {:?} (expected {} elements)",
data.len(),
shape,
total
)));
}
let array = ArrayD::from_shape_vec(IxDyn(shape), data)
.map_err(|e| TensorError::invalid_shape_simple(e.to_string()))?;
Ok(Tensor::from_array(array))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_ndarray_shape_preserved() {
use scirs2_core::ndarray::Array2;
let arr = Array2::<f32>::zeros((3, 4)).into_dyn();
let tensor = from_ndarray(arr);
assert_eq!(tensor.shape().dims(), &[3, 4]);
}
#[test]
fn test_to_ndarray_shape_preserved() {
let tensor = Tensor::<f32>::ones(&[2, 5]);
let arr = to_ndarray(&tensor).expect("to_ndarray should succeed on CPU tensor");
assert_eq!(arr.shape(), &[2, 5]);
}
#[test]
fn test_to_ndarray_values_preserved() {
use scirs2_core::ndarray::Array1;
let arr = Array1::<f64>::from_vec(vec![1.0, 2.0, 3.0]).into_dyn();
let tensor = from_ndarray(arr);
let back = to_ndarray(&tensor).expect("to_ndarray should succeed");
assert_eq!(back.as_slice().expect("contiguous"), &[1.0_f64, 2.0, 3.0]);
}
#[test]
fn test_round_trip_f32() {
let original = Tensor::<f32>::zeros(&[2, 3]);
let copied = round_trip(&original).expect("round_trip should succeed");
assert_eq!(original.shape().dims(), copied.shape().dims());
}
#[test]
fn test_round_trip_f64() {
let original = Tensor::<f64>::ones(&[5, 5]);
let copied = round_trip(&original).expect("round_trip should succeed");
assert_eq!(original.shape().dims(), copied.shape().dims());
}
#[test]
fn test_from_slice_with_shape_valid() {
let data = vec![1.0_f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let tensor = from_slice_with_shape(data, &[2, 3]).expect("valid shape");
assert_eq!(tensor.shape().dims(), &[2, 3]);
}
#[test]
fn test_from_slice_with_shape_mismatch_errors() {
let data = vec![1.0_f32, 2.0];
let result = from_slice_with_shape(data, &[2, 3]);
assert!(result.is_err(), "mismatched length should return Err");
}
#[test]
fn test_from_slice_with_shape_i32() {
let data: Vec<i32> = (0..12).collect();
let tensor = from_slice_with_shape(data, &[3, 4]).expect("valid shape");
assert_eq!(tensor.shape().dims(), &[3, 4]);
}
}