#![allow(clippy::useless_vec)]
mod utils;
use maidenx_core::{dtype::DType, error::Result};
use utils::{setup_grad_tensor_with_shape, setup_tensor_with_shape};
mod test_functions {
use super::*;
const TEST_DATA_F32_1D: [f32; 4] = [1.0, 2.0, 3.0, 4.0];
const TEST_DATA_F32_2D: [[f32; 3]; 2] = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
const TEST_DATA_U32_2D: [[u32; 2]; 2] = [[1, 2], [3, 4]];
pub fn sum_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, false)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![10.0]);
assert!(result.shape().is_empty());
let x = setup_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, true)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![10.0]);
assert_eq!(result.shape(), &[1]);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(0, false)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[3]);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(0, true)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[1, 3]);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(1, false)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0, 15.0]);
assert_eq!(result.shape(), &[2]);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(1, true)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0, 15.0]);
assert_eq!(result.shape(), &[2, 1]); }
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, false)?;
result.backward()?;
let result_vec = result.to_flatten_vec::<f32>()?;
assert!((result_vec[0] - 10.0).abs() < 0.1, "Expected close to 10.0, got {}", result_vec[0]);
assert!(result.shape().is_empty());
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.1,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, true)?;
result.backward()?;
let result_vec = result.to_flatten_vec::<f32>()?;
assert!((result_vec[0] - 10.0).abs() < 0.1, "Expected close to 10.0, got {}", result_vec[0]);
assert_eq!(result.shape(), &[1]);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.1,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(0, false)?;
result.backward()?;
let expected = vec![5.0, 7.0, 9.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.1,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![10.0]);
assert!(result.shape().is_empty()); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.sum(0, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![10.0]);
assert_eq!(result.shape(), &[1]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[3]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(0, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[1, 3]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(1, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0, 15.0]);
assert_eq!(result.shape(), &[2]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum(1, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0, 15.0]);
assert_eq!(result.shape(), &[2, 1]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
}
}
Ok(())
}
pub fn sum_all_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![21.0]);
}
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 21.0).abs() < 0.1, "Expected close to 21.0, got {}", result_val);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.1,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![21.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
}
}
Ok(())
}
pub fn sum_to_shape_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_to_shape(&[1, 3])?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
}
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_to_shape(&[1, 3])?;
result.backward()?;
let expected = vec![5.0, 7.0, 9.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.1,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.sum_to_shape(&[1, 3])?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
}
}
Ok(())
}
pub fn mean_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.mean(0, false)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![2.0, 3.0]);
assert_eq!(result.shape(), &[2]);
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.mean(0, true)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![2.0, 3.0]);
assert_eq!(result.shape(), &[1, 2]); }
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean(0, false)?;
result.backward()?;
let expected = vec![2.5, 3.5, 4.5];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
let expected_grad = vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5];
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean(0, true)?;
result.backward()?;
let expected = vec![2.5, 3.5, 4.5];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[1, 3]);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
let expected_grad = vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5];
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean(0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![2.5, 3.5, 4.5]);
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean(0, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![2.5, 3.5, 4.5]);
assert_eq!(result.shape(), &[1, 3]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.5, 0.5, 0.5, 0.5, 0.5, 0.5]);
}
}
}
Ok(())
}
pub fn mean_all_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![3.5]);
}
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 3.5).abs() < 0.1, "Expected close to 3.5, got {}", result_val);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
let expected_val = 1.0 / 6.0;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - expected_val).abs() < 0.01,
"Gradient at index {} expected to be close to {}, got {}",
i,
expected_val,
val
);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.mean_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![3.5]);
if let Some(g) = x.grad()? {
assert_eq!(
g.to_flatten_vec::<f32>()?,
vec![1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0]
);
}
}
}
Ok(())
}
pub fn fold_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let x = setup_tensor_with_shape(data, dtype, &[5])?;
let unfolded = x.unfold(0, 2, 1)?;
assert_eq!(unfolded.shape(), &[4, 2]);
let folded = unfolded.fold(0, 2, 1)?;
assert_eq!(folded.shape(), &[5]);
assert_eq!(folded.to_flatten_vec::<f32>()?, vec![1.0, 4.0, 6.0, 8.0, 5.0]);
}
DType::BF16 | DType::F16 => {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let x = setup_grad_tensor_with_shape(data, dtype, &[5])?;
let unfolded = x.unfold(0, 2, 1)?;
assert_eq!(unfolded.shape(), &[4, 2]);
let folded = unfolded.fold(0, 2, 1)?;
assert_eq!(folded.shape(), &[5]);
let expected = vec![1.0, 4.0, 6.0, 8.0, 5.0];
let result_vec = folded.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
let data_2d = TEST_DATA_F32_2D.iter().flatten().copied().collect();
let x_2d = setup_grad_tensor_with_shape(data_2d, dtype, &[2, 3])?;
let unfolded_2d = x_2d.unfold(1, 2, 1)?;
assert_eq!(unfolded_2d.shape(), &[2, 2, 2]);
let folded_2d = unfolded_2d.fold(1, 2, 1)?;
assert_eq!(folded_2d.shape(), &[2, 3]);
let expected = vec![1.0, 4.0, 3.0, 4.0, 10.0, 6.0];
let result_vec = folded_2d.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
folded_2d.backward()?;
if let Some(g) = unfolded_2d.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < 0.01,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
if let Some(g) = x_2d.grad()? {
let expected_grad = vec![1.0, 2.0, 1.0, 1.0, 2.0, 1.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let x = setup_grad_tensor_with_shape(data, dtype, &[5])?;
let unfolded = x.unfold(0, 2, 1)?;
assert_eq!(unfolded.shape(), &[4, 2]);
let folded = unfolded.fold(0, 2, 1)?;
assert_eq!(folded.shape(), &[5]);
assert_eq!(folded.to_flatten_vec::<f32>()?, vec![1.0, 4.0, 6.0, 8.0, 5.0]);
let data_2d = TEST_DATA_F32_2D.iter().flatten().copied().collect();
let x_2d = setup_grad_tensor_with_shape(data_2d, dtype, &[2, 3])?;
let unfolded_2d = x_2d.unfold(1, 2, 1)?;
assert_eq!(unfolded_2d.shape(), &[2, 2, 2]);
let folded_2d = unfolded_2d.fold(1, 2, 1)?;
assert_eq!(folded_2d.shape(), &[2, 3]);
assert_eq!(folded_2d.to_flatten_vec::<f32>()?, vec![1.0, 4.0, 3.0, 4.0, 10.0, 6.0]);
folded_2d.backward()?;
if let Some(g) = unfolded_2d.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
assert!(
grad_data.iter().all(|&x| (x - 1.0).abs() < 1e-5),
"Expected all gradients to be 1.0, got {:?}",
grad_data
);
}
if let Some(g) = x_2d.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 2.0, 1.0, 1.0, 2.0, 1.0]);
}
}
}
Ok(())
}
pub fn max_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.max(0, false)?;
assert_eq!(result.to_flatten_vec::<u32>()?, vec![3, 4]);
assert_eq!(result.shape(), &[2]);
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.max(0, true)?;
assert_eq!(result.to_flatten_vec::<u32>()?, vec![3, 4]);
assert_eq!(result.shape(), &[1, 2]); }
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max(0, false)?;
result.backward()?;
let expected = vec![4.0, 5.0, 6.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
let expected_grad = vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max(0, true)?;
result.backward()?;
let expected = vec![4.0, 5.0, 6.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[1, 3]);
if let Some(g) = x.grad()? {
let expected_grad = vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max(0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![4.0, 5.0, 6.0]);
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max(0, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![4.0, 5.0, 6.0]);
assert_eq!(result.shape(), &[1, 3]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.0, 0.0, 0.0, 1.0, 1.0, 1.0]);
}
}
}
Ok(())
}
pub fn max_all_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0]);
let x = setup_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.max_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![4.0]);
}
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 6.0).abs() < 0.1, "Expected close to 6.0, got {}", result_val);
if let Some(g) = x.grad()? {
let expected_grad = vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.max_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 4.0).abs() < 0.1, "Expected close to 4.0, got {}", result_val);
if let Some(g) = x.grad()? {
let expected_grad = vec![0.0, 0.0, 0.0, 1.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.max_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![6.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.max_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![4.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![0.0, 0.0, 0.0, 1.0]);
}
}
}
Ok(())
}
pub fn min_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.min(0, false)?;
assert_eq!(result.to_flatten_vec::<u32>()?, vec![1, 2]);
assert_eq!(result.shape(), &[2]);
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.min(0, true)?;
assert_eq!(result.to_flatten_vec::<u32>()?, vec![1, 2]);
assert_eq!(result.shape(), &[1, 2]); }
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min(0, false)?;
result.backward()?;
let expected = vec![1.0, 2.0, 3.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
let expected_grad = vec![1.0, 1.0, 1.0, 0.0, 0.0, 0.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min(0, true)?;
result.backward()?;
let expected = vec![1.0, 2.0, 3.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Result at index {} expected to be close to {}, got {}", i, e, a);
}
assert_eq!(result.shape(), &[1, 3]);
if let Some(g) = x.grad()? {
let expected_grad = vec![1.0, 1.0, 1.0, 0.0, 0.0, 0.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min(0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0, 2.0, 3.0]);
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 0.0, 0.0, 0.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min(0, true)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0, 2.0, 3.0]);
assert_eq!(result.shape(), &[1, 3]); if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 0.0, 0.0, 0.0]);
}
}
}
Ok(())
}
pub fn min_all_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0]);
let x = setup_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.min_all()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0]);
}
DType::BF16 | DType::F16 => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 1.0).abs() < 0.1, "Expected close to 1.0, got {}", result_val);
if let Some(g) = x.grad()? {
let expected_grad = vec![1.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.min_all()?;
result.backward()?;
let result_val = result.to_flatten_vec::<f32>()?[0];
assert!((result_val - 1.0).abs() < 0.1, "Expected close to 1.0, got {}", result_val);
if let Some(g) = x.grad()? {
let expected_grad = vec![1.0, 0.0, 0.0, 0.0];
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, (a, e)) in grad_data.iter().zip(expected_grad.iter()).enumerate() {
assert!((a - e).abs() < 0.1, "Gradient at index {} expected to be close to {}, got {}", i, e, a);
}
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.min_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.min_all()?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![1.0]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 0.0, 0.0, 0.0]);
}
}
}
Ok(())
}
pub fn norm_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(1.0, 0, false)?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[3]);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(2.0, 0, true)?;
let expected_l2_norm = vec![
(1.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt(),
(2.0_f32.powi(2) + 5.0_f32.powi(2)).sqrt(),
(3.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt(),
];
let result_vec = result.to_flatten_vec::<f32>()?;
let tolerance = 0.05;
for (a, b) in result_vec.iter().zip(expected_l2_norm.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
}
DType::BF16 | DType::F16 => {
let tolerance = 0.05;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(1.0, 0, false)?;
result.backward()?;
let expected = vec![5.0, 7.0, 9.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (i, (a, e)) in result_vec.iter().zip(expected.iter()).enumerate() {
assert!(
(a - e).abs() < tolerance,
"Result at index {} expected to be close to {}, got {}",
i,
e,
a
);
}
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < tolerance,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(2.0, 0, true)?;
result.backward()?;
let expected_l2_norm = vec![
(1.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt(),
(2.0_f32.powi(2) + 5.0_f32.powi(2)).sqrt(),
(3.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt(),
];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_l2_norm.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.norm(3.0, 0, false)?;
result.backward()?;
let expected_p3_norm = (1.0_f32.powi(3) + 2.0_f32.powi(3) + 3.0_f32.powi(3) + 4.0_f32.powi(3)).powf(1.0 / 3.0);
let result_value = result.item()?.as_f32();
assert!(
(result_value - expected_p3_norm).abs() < tolerance,
"Expected {} but got {} (tolerance: {})",
expected_p3_norm,
result_value,
tolerance
);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(2.0, -1, false)?;
assert_eq!(result.shape(), &[2]);
let expected = vec![
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2)).sqrt(),
(4.0_f32.powi(2) + 5.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt(),
];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
_ => {
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(1.0, 0, false)?;
result.backward()?;
assert_eq!(result.to_flatten_vec::<f32>()?, vec![5.0, 7.0, 9.0]);
assert_eq!(result.shape(), &[3]);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(2.0, 0, true)?;
result.backward()?;
let expected_l2_norm = vec![
(1.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt(),
(2.0_f32.powi(2) + 5.0_f32.powi(2)).sqrt(),
(3.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt(),
];
let result_vec = result.to_flatten_vec::<f32>()?;
let tolerance = 1e-5;
for (a, b) in result_vec.iter().zip(expected_l2_norm.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.norm(3.0, 0, false)?;
result.backward()?;
let expected_p3_norm = (1.0_f32.powi(3) + 2.0_f32.powi(3) + 3.0_f32.powi(3) + 4.0_f32.powi(3)).powf(1.0 / 3.0);
let result_value = result.item()?.as_f32();
assert!(
(result_value - expected_p3_norm).abs() < tolerance,
"Expected {} but got {} (tolerance: {})",
expected_p3_norm,
result_value,
tolerance
);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm(2.0, -1, false)?;
assert_eq!(result.shape(), &[2]);
let expected = vec![
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2)).sqrt(),
(4.0_f32.powi(2) + 5.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt(),
];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
}
Ok(())
}
pub fn norm_all_test(dtype: DType) -> Result<()> {
if dtype == DType::U8 || dtype == DType::I8 {
return Ok(());
}
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let tolerance = 1e-5;
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(1.0)?;
let expected_l1_norm = 21.0;
assert!(
(result.item()?.as_f32() - expected_l1_norm).abs() < tolerance,
"L1 norm: Expected {} but got {} (tolerance: {})",
expected_l1_norm,
result.item()?.as_f32(),
tolerance
);
let x = setup_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(2.0)?;
let expected_l2_norm =
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2) + 4.0_f32.powi(2) + 5.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt();
assert!(
(result.item()?.as_f32() - expected_l2_norm).abs() < tolerance,
"L2 norm: Expected {} but got {} (tolerance: {})",
expected_l2_norm,
result.item()?.as_f32(),
tolerance
);
}
DType::BF16 | DType::F16 => {
let tolerance = 0.05;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(1.0)?;
result.backward()?;
let expected_l1_norm = 21.0;
assert!(
(result.item()?.as_f32() - expected_l1_norm).abs() < tolerance,
"L1 norm: Expected {} but got {} (tolerance: {})",
expected_l1_norm,
result.item()?.as_f32(),
tolerance
);
if let Some(g) = x.grad()? {
let grad_data = g.to_flatten_vec::<f32>()?;
for (i, &val) in grad_data.iter().enumerate() {
assert!(
(val - 1.0).abs() < tolerance,
"Gradient at index {} expected to be close to 1.0, got {}",
i,
val
);
}
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(2.0)?;
result.backward()?;
let expected_l2_norm =
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2) + 4.0_f32.powi(2) + 5.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt();
assert!(
(result.item()?.as_f32() - expected_l2_norm).abs() < tolerance,
"L2 norm: Expected {} but got {} (tolerance: {})",
expected_l2_norm,
result.item()?.as_f32(),
tolerance
);
let p_values = [0.5, 1.0, 2.0, 3.0, 5.0];
let data = vec![1.0, 2.0, 3.0, 4.0];
for p in p_values {
let x = setup_grad_tensor_with_shape(data.clone(), dtype, &[4])?;
let result = x.norm_all(p)?;
let expected = if p == 1.0 {
10.0 } else if p == 2.0 {
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt()
} else {
let sum_pow = 1.0_f32.powf(p) + 2.0_f32.powf(p) + 3.0_f32.powf(p) + 4.0_f32.powf(p);
sum_pow.powf(1.0 / p)
};
let p_tolerance = if p != 1.0 && p != 2.0 { 0.3 } else { tolerance };
assert!(
(result.item()?.as_f32() - expected).abs() < p_tolerance,
"p={}, expected={}, got={} (tolerance: {})",
p,
expected,
result.item()?.as_f32(),
p_tolerance
);
}
}
_ => {
let tolerance = 1e-5;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(1.0)?;
result.backward()?;
let expected_l1_norm = 21.0;
assert!(
(result.item()?.as_f32() - expected_l1_norm).abs() < tolerance,
"L1 norm: Expected {} but got {} (tolerance: {})",
expected_l1_norm,
result.item()?.as_f32(),
tolerance
);
if let Some(g) = x.grad()? {
assert_eq!(g.to_flatten_vec::<f32>()?, vec![1.0, 1.0, 1.0, 1.0, 1.0, 1.0]);
}
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.norm_all(2.0)?;
result.backward()?;
let expected_l2_norm =
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2) + 4.0_f32.powi(2) + 5.0_f32.powi(2) + 6.0_f32.powi(2)).sqrt();
assert!(
(result.item()?.as_f32() - expected_l2_norm).abs() < tolerance,
"L2 norm: Expected {} but got {} (tolerance: {})",
expected_l2_norm,
result.item()?.as_f32(),
tolerance
);
let p_values = [0.5, 1.0, 2.0, 3.0, 5.0];
let data = vec![1.0, 2.0, 3.0, 4.0];
for p in p_values {
let x = setup_grad_tensor_with_shape(data.clone(), dtype, &[4])?;
let result = x.norm_all(p)?;
let expected = if p == 1.0 {
10.0 } else if p == 2.0 {
(1.0_f32.powi(2) + 2.0_f32.powi(2) + 3.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt()
} else {
let sum_pow = 1.0_f32.powf(p) + 2.0_f32.powf(p) + 3.0_f32.powf(p) + 4.0_f32.powf(p);
sum_pow.powf(1.0 / p)
};
assert!(
(result.item()?.as_f32() - expected).abs() < tolerance,
"p={}, expected={}, got={} (tolerance: {})",
p,
expected,
result.item()?.as_f32(),
tolerance
);
}
}
}
Ok(())
}
pub fn var_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let tolerance = 1e-5;
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.var(0, false, false)?;
let expected_var = vec![1.0, 1.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
DType::BF16 | DType::F16 => {
let tolerance = 0.1;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(0, false, false)?;
result.backward()?;
let expected_var = vec![2.2500, 2.2500, 2.2500];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(0, true, true)?;
result.backward()?;
let expected_var = vec![4.5000, 4.5000, 4.5000];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(1, false, false)?;
let expected_var = vec![2.0 / 3.0, 2.0 / 3.0];
let result_vec = result.to_flatten_vec::<f32>()?;
let dim1_tolerance = 0.2;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!(
(a - b).abs() < dim1_tolerance,
"Expected {} but got {} (tolerance: {})",
b,
a,
dim1_tolerance
);
}
assert_eq!(result.shape(), &[2]);
}
_ => {
let tolerance = 1e-5;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(0, false, false)?;
result.backward()?;
let expected_var = vec![2.2500, 2.2500, 2.2500];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(0, true, true)?;
result.backward()?;
let expected_var = vec![4.5000, 4.5000, 4.5000];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.var(1, false, false)?;
let expected_var = vec![2.0 / 3.0, 2.0 / 3.0];
let result_vec = result.to_flatten_vec::<f32>()?;
let dim1_tolerance = 1e-2;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!(
(a - b).abs() < dim1_tolerance,
"Expected {} but got {} (tolerance: {})",
b,
a,
dim1_tolerance
);
}
assert_eq!(result.shape(), &[2]);
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), DType::U32, &[2, 2])?;
let result = x.var(0, false, false)?;
let expected_var = vec![1.0, 1.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_var.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
}
Ok(())
}
pub fn std_test(dtype: DType) -> Result<()> {
match dtype {
DType::U8 | DType::U16 | DType::U32 | DType::U64 | DType::I8 | DType::I16 | DType::I32 | DType::I64 => {
let tolerance = 0.001;
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), dtype, &[2, 2])?;
let result = x.std(0, false, false)?;
let expected_std = vec![1.0, 1.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
DType::BF16 | DType::F16 => {
let tolerance = 0.1;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.std(0, false, false)?;
result.backward()?;
let expected_std = vec![1.5000, 1.5000, 1.5000];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.std(0, true, true)?;
result.backward()?;
let expected_std = vec![2.1213, 2.1213, 2.1213];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.std(0, false, true)?;
let expected = (5.0 / 3.0_f32).sqrt();
assert!(
(result.item()?.as_f32() - expected).abs() < tolerance,
"Expected {} but got {} (tolerance: {})",
expected,
result.item()?.as_f32(),
tolerance
);
}
_ => {
let tolerance = 0.001;
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.std(0, false, false)?;
result.backward()?;
let expected_std = vec![1.5000, 1.5000, 1.5000];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_2D.iter().flatten().copied().collect(), dtype, &[2, 3])?;
let result = x.std(0, true, true)?;
result.backward()?;
let expected_std = vec![2.1213, 2.1213, 2.1213];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
assert_eq!(result.shape(), &[1, 3]);
let x = setup_grad_tensor_with_shape(TEST_DATA_F32_1D.to_vec(), dtype, &[4])?;
let result = x.std(0, false, true)?;
let expected = (5.0 / 3.0_f32).sqrt();
assert!(
(result.item()?.as_f32() - expected).abs() < tolerance,
"Expected {} but got {} (tolerance: {})",
expected,
result.item()?.as_f32(),
tolerance
);
let x = setup_tensor_with_shape(TEST_DATA_U32_2D.iter().flatten().copied().collect(), DType::U32, &[2, 2])?;
let result = x.std(0, false, false)?;
let expected_std = vec![1.0, 1.0];
let result_vec = result.to_flatten_vec::<f32>()?;
for (a, b) in result_vec.iter().zip(expected_std.iter()) {
assert!((a - b).abs() < tolerance, "Expected {} but got {} (tolerance: {})", b, a, tolerance);
}
}
}
Ok(())
}
}
test_ops!([
sum,
sum_all,
sum_to_shape,
mean,
mean_all,
fold,
max,
max_all,
min,
min_all,
norm,
norm_all,
var,
std
]);