use numrs2::array::Array;
use numrs2::gpu::linalg;
use numrs2::gpu::{new_context, GpuArray};
#[tokio::test]
async fn test_matmul_basic() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0]).reshape(&[2, 2]);
let b = Array::from_vec(vec![5.0f32, 6.0, 7.0, 8.0]).reshape(&[2, 2]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let b_gpu = GpuArray::from_array_with_context(&b, context.clone())
.expect("Failed to create GPU array");
let c_gpu = linalg::matmul(&a_gpu, &b_gpu).expect("Failed to perform matmul");
let c = c_gpu.to_array().expect("Failed to convert to CPU array");
assert_eq!(c.shape(), &[2, 2]);
assert!((c.get(&[0, 0]).expect("Invalid index") - 19.0).abs() < 1e-5);
assert!((c.get(&[0, 1]).expect("Invalid index") - 22.0).abs() < 1e-5);
assert!((c.get(&[1, 0]).expect("Invalid index") - 43.0).abs() < 1e-5);
assert!((c.get(&[1, 1]).expect("Invalid index") - 50.0).abs() < 1e-5);
}
#[tokio::test]
async fn test_matmul_rectangular() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[3, 2]);
let b = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]).reshape(&[2, 4]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let b_gpu = GpuArray::from_array_with_context(&b, context.clone())
.expect("Failed to create GPU array");
let c_gpu = linalg::matmul(&a_gpu, &b_gpu).expect("Failed to perform matmul");
let c = c_gpu.to_array().expect("Failed to convert to CPU array");
assert_eq!(c.shape(), &[3, 4]);
}
#[tokio::test]
async fn test_matmul_incompatible_shapes() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[2, 3]);
let b = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[2, 3]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let b_gpu = GpuArray::from_array_with_context(&b, context.clone())
.expect("Failed to create GPU array");
let result = linalg::matmul(&a_gpu, &b_gpu);
assert!(result.is_err());
}
#[tokio::test]
async fn test_dot_product() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0]).reshape(&[4]);
let b = Array::from_vec(vec![5.0f32, 6.0, 7.0, 8.0]).reshape(&[4]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let b_gpu = GpuArray::from_array_with_context(&b, context.clone())
.expect("Failed to create GPU array");
let result = linalg::dot(&a_gpu, &b_gpu).expect("Failed to compute dot product");
assert!((result - 70.0).abs() < 1e-5);
}
#[tokio::test]
async fn test_dot_incompatible_shapes() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0]).reshape(&[3]);
let b = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0]).reshape(&[4]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let b_gpu = GpuArray::from_array_with_context(&b, context.clone())
.expect("Failed to create GPU array");
let result = linalg::dot(&a_gpu, &b_gpu);
assert!(result.is_err());
}
#[tokio::test]
async fn test_norm_l2() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![3.0f32, 4.0]).reshape(&[2]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let norm = linalg::norm_l2(&a_gpu).expect("Failed to compute L2 norm");
assert!((norm - 5.0).abs() < 1e-5);
}
#[tokio::test]
async fn test_matvec() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[2, 3]);
let x = Array::from_vec(vec![1.0f32, 2.0, 3.0]).reshape(&[3]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let x_gpu = GpuArray::from_array_with_context(&x, context.clone())
.expect("Failed to create GPU array");
let y_gpu = linalg::matvec(&a_gpu, &x_gpu).expect("Failed to compute matvec");
let y = y_gpu.to_array().expect("Failed to convert to CPU array");
assert_eq!(y.shape(), &[2]);
assert!((y.get(&[0]).expect("Invalid index") - 14.0).abs() < 1e-5);
assert!((y.get(&[1]).expect("Invalid index") - 32.0).abs() < 1e-5);
}
#[tokio::test]
async fn test_vecmat() {
let context = new_context().expect("Failed to create GPU context");
let x = Array::from_vec(vec![1.0f32, 2.0]).reshape(&[2]);
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[2, 3]);
let x_gpu = GpuArray::from_array_with_context(&x, context.clone())
.expect("Failed to create GPU array");
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let y_gpu = linalg::vecmat(&x_gpu, &a_gpu).expect("Failed to compute vecmat");
let y = y_gpu.to_array().expect("Failed to convert to CPU array");
assert_eq!(y.shape(), &[3]);
assert!((y.get(&[0]).expect("Invalid index") - 9.0).abs() < 1e-5);
assert!((y.get(&[1]).expect("Invalid index") - 12.0).abs() < 1e-5);
assert!((y.get(&[2]).expect("Invalid index") - 15.0).abs() < 1e-5);
}
#[tokio::test]
async fn test_matmul_identity() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0]).reshape(&[2, 2]);
let i = Array::from_vec(vec![1.0f32, 0.0, 0.0, 1.0]).reshape(&[2, 2]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let i_gpu = GpuArray::from_array_with_context(&i, context.clone())
.expect("Failed to create GPU array");
let c_gpu = linalg::matmul(&a_gpu, &i_gpu).expect("Failed to perform matmul");
let c = c_gpu.to_array().expect("Failed to convert to CPU array");
assert_eq!(c.shape(), &[2, 2]);
for i in 0..2 {
for j in 0..2 {
let expected = a.get(&[i, j]).expect("Invalid index");
let actual = c.get(&[i, j]).expect("Invalid index");
assert!((actual - expected).abs() < 1e-5);
}
}
}
#[tokio::test]
async fn test_reshape() {
let context = new_context().expect("Failed to create GPU context");
let a = Array::from_vec(vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0]).reshape(&[6]);
let a_gpu = GpuArray::from_array_with_context(&a, context.clone())
.expect("Failed to create GPU array");
let a_2d = a_gpu.reshape(&[2, 3]).expect("Failed to reshape");
assert_eq!(a_2d.shape(), &[2, 3]);
let a_3x2 = a_gpu.reshape(&[3, 2]).expect("Failed to reshape");
assert_eq!(a_3x2.shape(), &[3, 2]);
let result = a_gpu.reshape(&[2, 2]);
assert!(result.is_err());
}