use hermes_simd::*;
#[test]
fn test_tiled_dot_matches_dot() {
let a: Vec<f32> = (0..64).map(|i| i as f32).collect();
let b: Vec<f32> = (0..64).map(|i| (64 - i) as f32).collect();
let view_a = SimdView::<f32, Scalar, Unaligned, Unmasked, &[f32]>::new(&a).unwrap();
let view_b = SimdView::<f32, Scalar, Unaligned, Unmasked, &[f32]>::new(&b).unwrap();
let expected = dot::<f32>(&a, &b).unwrap();
let tiled_1 = tiled_dot::<f32, Scalar, Unaligned, 1>(&view_a, &view_b).unwrap();
let tiled_4 = tiled_dot::<f32, Scalar, Unaligned, 4>(&view_a, &view_b).unwrap();
assert!(
(tiled_1 - expected).abs() < 1e-3,
"TILE_M=1: {tiled_1} vs {expected}"
);
assert!(
(tiled_4 - expected).abs() < 1e-3,
"TILE_M=4: {tiled_4} vs {expected}"
);
}
#[test]
fn test_tiled_gemv_correctness() {
let a = [
1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
];
let x = [1.0f32, 2.0, 3.0, 4.0];
let mut y = [0.0f32; 4];
let a_view = SimdView::<f32, Scalar, Unaligned>::new(&a).unwrap();
let x_view = SimdView::<f32, Scalar, Unaligned>::new(&x).unwrap();
tiled_gemv::<f32, Scalar, Unaligned, 2>(&a_view, &x_view, &mut y, 4, 4).unwrap();
assert_eq!(y, [30.0, 70.0, 110.0, 150.0]);
}
#[test]
fn test_gemv_f32_column_tail_differential() {
let nrows = 11usize;
let ncols = 21usize;
let a: Vec<f32> = (0..nrows * ncols)
.map(|i| ((i % 9) as f32 - 4.0) * 0.25)
.collect();
let x: Vec<f32> = (0..ncols).map(|i| ((i % 5) as f32 - 2.0) * 0.5).collect();
let y_init: Vec<f32> = (0..nrows).map(|i| (i % 3) as f32 - 1.0).collect();
let mut y = y_init.clone();
gemv::<f32>(&a, &x, &mut y, nrows, ncols).unwrap();
let mut want = y_init;
for (row, w) in want.iter_mut().enumerate() {
let mut sum = 0.0f32;
for col in 0..ncols {
sum += a[row * ncols + col] * x[col];
}
*w += sum;
}
assert_eq!(y, want, "gemv f32 column tail diverges from reference");
}
#[test]
fn test_tiled_gemm() {
let a = vec![
1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let b = vec![
1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let mut c = vec![0.0f32; 16];
tiled_gemm(&a, &b, &mut c, 4, 4, 3).unwrap();
let expected = vec![
38.0, 44.0, 50.0, 56.0, 83.0, 98.0, 113.0, 128.0, 128.0, 152.0, 176.0, 200.0, 173.0, 206.0,
239.0, 272.0,
];
for i in 0..16 {
assert!(
(c[i] - expected[i]).abs() < 1e-4,
"At index {}, {} != {}",
i,
c[i],
expected[i]
);
}
}
#[test]
fn test_tile_matrix_multiply_bf16() {
use eunomia::{Bf16, F32};
let mut c = vec![F32(0.0); 16 * 16];
let a = vec![Bf16::from_f32(1.0); 16 * 32];
let b = vec![Bf16::from_f32(2.0); 32 * 16];
unsafe {
dispatch_tile_matmul::<Bf16, Bf16, F32>(c.as_mut_ptr(), 16, a.as_ptr(), 32, b.as_ptr(), 16);
}
for val in c {
assert_eq!(val, F32(64.0));
}
}
#[test]
fn test_tile_matrix_multiply_int8() {
let mut c = vec![0i32; 16 * 16];
let a = vec![1i8; 16 * 64];
let b = vec![2i8; 64 * 16];
unsafe {
dispatch_tile_matmul::<i8, i8, i32>(c.as_mut_ptr(), 16, a.as_ptr(), 64, b.as_ptr(), 16);
}
for val in c {
assert_eq!(val, 128);
}
}
#[test]
fn test_gemm_bf16_high_level() {
use eunomia::{Bf16, F32};
let m = 35;
let n = 20;
let k = 36;
let a = vec![Bf16::from_f32(1.5); m * k];
let b = vec![Bf16::from_f32(2.0); k * n];
let mut c = vec![F32(0.0); m * n];
unsafe {
gemm::<Bf16, Bf16, F32>(m, n, k, &a, k, &b, n, &mut c, n).unwrap();
}
for val in c {
assert_eq!(val, F32(108.0));
}
}
#[test]
fn test_gemm_int8_high_level() {
let m = 35;
let n = 20;
let k = 70;
let a = vec![2i8; m * k];
let b = vec![3i8; k * n];
let mut c = vec![0i32; m * n];
unsafe {
gemm::<i8, i8, i32>(m, n, k, &a, k, &b, n, &mut c, n).unwrap();
}
for val in c {
assert_eq!(val, 420);
}
}
#[test]
fn test_tiled_gemm_column_tail_differential() {
let m = 7usize;
let n = 45usize;
let k = 13usize;
let a: Vec<f32> = (0..m * k).map(|i| ((i % 9) as f32 - 4.0) * 0.5).collect();
let b: Vec<f32> = (0..k * n).map(|i| ((i % 7) as f32 - 3.0) * 0.5).collect();
let c_init: Vec<f32> = (0..m * n).map(|i| (i % 11) as f32 - 5.0).collect();
let mut c = c_init.clone();
tiled_gemm(&a, &b, &mut c, m, n, k).unwrap();
let mut c_ref = c_init;
for row in 0..m {
for col in 0..n {
let mut sum = 0.0f32;
for kk in 0..k {
sum += a[row * k + kk] * b[kk * n + col];
}
c_ref[row * n + col] += sum;
}
}
assert_eq!(c, c_ref, "tiled GEMM column tail diverges from reference");
}
#[test]
fn test_gemm_int8_signed_differential() {
let m = 37;
let n = 29;
let k = 130;
let a: Vec<i8> = (0..m * k)
.map(|i| ((i * 89 + 3) % 256) as u8 as i8)
.collect();
let b: Vec<i8> = (0..k * n)
.map(|i| ((i * 41 + 128) % 256) as u8 as i8)
.collect();
let c_init: Vec<i32> = (0..m * n)
.map(|i| (i as i32).wrapping_mul(7919) - 40000)
.collect();
let mut c = c_init.clone();
unsafe {
gemm::<i8, i8, i32>(m, n, k, &a, k, &b, n, &mut c, n).unwrap();
}
let mut c_ref = c_init;
for r in 0..m {
for col in 0..n {
let mut sum = 0i32;
for kk in 0..k {
sum = sum.wrapping_add((a[r * k + kk] as i32) * (b[kk * n + col] as i32));
}
c_ref[r * n + col] += sum;
}
}
assert_eq!(
c, c_ref,
"dispatched int8 GEMM diverges from scalar reference"
);
}
#[test]
fn test_gemm_bf16_size_16() {
use eunomia::{Bf16, F32};
let m = 16;
let n = 16;
let k = 16;
let a = vec![Bf16::from_f32(1.5); m * k];
let b = vec![Bf16::from_f32(2.0); k * n];
let mut c = vec![F32(0.0); m * n];
unsafe {
gemm::<Bf16, Bf16, F32>(m, n, k, &a, k, &b, n, &mut c, n).unwrap();
}
}
#[test]
fn test_tiling_strategy_trait_direct() {
let a: Vec<f32> = (0..64).map(|i| i as f32).collect();
let b: Vec<f32> = (0..64).map(|i| (64 - i) as f32).collect();
let view_a = SimdView::<f32, Scalar, Unaligned, Unmasked, &[f32]>::new(&a).unwrap();
let view_b = SimdView::<f32, Scalar, Unaligned, Unmasked, &[f32]>::new(&b).unwrap();
let expected = dot::<f32>(&a, &b).unwrap();
let tiled_res =
<TilingPolicy<4, 1> as TilingStrategy<f32, Scalar, Unaligned>>::dot(&view_a, &view_b)
.unwrap();
assert!((tiled_res - expected).abs() < 1e-3);
}