use super::*;
#[test]
fn test_conv1d_identity_kernel() {
let mut raw_weights = vec![0.0f32; 16];
for i in 0..4 {
raw_weights[i * 4 + i] = 1.0;
}
let mut weights =
AlignedVec::new(16, 0.0f32).expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::transpose_conv1d_interleaved_4wide(
&raw_weights,
&mut weights,
4, 4, 1, );
let conv = Conv1d::<4, 4, 1> {
weights,
bias: AlignedVec::from_vec(vec![0.0; 4])
.expect("allocation should succeed for test-sized buffers"),
do_bias: false,
dilation: 1,
};
let layer_buffer = vec![1.0, 2.0, 3.0, 4.0];
let mut block = vec![0.0; 4];
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 0, 1);
}
assert_eq!(block, vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_conv1d_with_bias() {
let mut raw_weights = vec![0.0f32; 16];
for i in 0..4 {
raw_weights[i * 4 + i] = 1.0;
}
let mut weights =
AlignedVec::new(16, 0.0f32).expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::transpose_conv1d_interleaved_4wide(
&raw_weights,
&mut weights,
4, 4, 1, );
let conv = Conv1d::<4, 4, 1> {
weights,
bias: AlignedVec::from_vec(vec![0.5; 4])
.expect("allocation should succeed for test-sized buffers"),
do_bias: true, dilation: 1,
};
let layer_buffer = vec![1.0, 2.0, 3.0, 4.0];
let mut block = vec![0.0; 4];
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 0, 1);
}
assert_eq!(block, vec![1.5, 2.5, 3.5, 4.5]);
}
#[test]
fn test_conv1d_dilation() {
let raw_weights = vec![1.0f32; 2 * 3 * 2];
let mut weights =
AlignedVec::new(24, 0.0f32).expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::transpose_conv1d_interleaved_4wide(
&raw_weights,
&mut weights,
2, 2, 3, );
let conv = Conv1d::<2, 2, 3> {
weights,
bias: AlignedVec::from_vec(vec![0.0; 2])
.expect("allocation should succeed for test-sized buffers"),
do_bias: false,
dilation: 2,
};
let mut layer_buffer = vec![0.0; 6 * 2];
layer_buffer[0] = 1.0;
layer_buffer[1] = 2.0; layer_buffer[2] = 10.0;
layer_buffer[3] = 20.0; layer_buffer[4] = 3.0;
layer_buffer[5] = 4.0; layer_buffer[6] = 30.0;
layer_buffer[7] = 40.0; layer_buffer[8] = 5.0;
layer_buffer[9] = 6.0;
let mut block = vec![0.0; 2];
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 4, 1);
}
assert_eq!(block[0], 21.0);
assert_eq!(block[1], 21.0);
}
#[test]
fn test_conv1d_zero_input() {
let raw_weights = vec![100.0f32; 2 * 3 * 2];
let mut weights =
AlignedVec::new(24, 0.0f32).expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::transpose_conv1d_interleaved_4wide(
&raw_weights,
&mut weights,
2, 2, 3, );
let mut conv = Conv1d::<2, 2, 3> {
weights: weights.clone(),
bias: AlignedVec::from_vec(vec![0.0; 2])
.expect("allocation should succeed for test-sized buffers"),
do_bias: false,
dilation: 1,
};
let layer_buffer = vec![0.0; 4 * 2];
let mut block = vec![0.0; 2];
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 2, 1);
}
assert_eq!(block, vec![0.0, 0.0]);
conv.do_bias = true;
conv.bias = AlignedVec::from_vec(vec![7.5, 8.5])
.expect("allocation should succeed for test-sized buffers");
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 2, 1);
}
assert_eq!(block, vec![7.5, 8.5]);
}
#[test]
fn test_conv1d_known_output() {
let raw_weights = vec![
0.5, 1.5, 1.0, 2.0, -0.5, -1.5, -1.0, -2.0, ];
let mut weights =
AlignedVec::new(16, 0.0f32).expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::transpose_conv1d_interleaved_4wide(
&raw_weights,
&mut weights,
2, 2, 2, );
let conv = Conv1d::<2, 2, 2> {
weights,
bias: AlignedVec::from_vec(vec![1.0, -1.0])
.expect("allocation should succeed for test-sized buffers"),
do_bias: true,
dilation: 1,
};
let layer_buffer = vec![2.0, 3.0, 4.0, 5.0];
let mut block = vec![0.0; 2];
unsafe {
conv.process_block::<crate::math::common::Avx2Math>(&layer_buffer, &mut block, 1, 1);
}
assert_eq!(block[0], 21.0);
assert_eq!(block[1], -21.0);
}
#[test]
fn test_conv1d_ch8_wide_interleaving() {
use crate::math::common::AlignedVec;
const CH: usize = 8;
const K: usize = 2;
let mut raw = vec![0.0f32; CH * K * CH];
for out_c in 0..CH {
for k in 0..K {
for in_c in 0..CH {
let idx = (out_c * CH + in_c) * K + k;
raw[idx] = (out_c + 1) as f32 * 0.5;
}
}
}
let mut weights = AlignedVec::new(CH * K * CH, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::layout::transpose_conv1d_interleaved_8wide(
&raw,
&mut weights,
CH,
CH,
K,
);
let conv = Conv1d::<CH, CH, K> {
weights: weights.clone(),
bias: AlignedVec::from_vec(vec![0.1f32; CH])
.expect("allocation should succeed for test-sized buffers"),
do_bias: true,
dilation: 1,
};
let mut layer_buffer = vec![0.0f32; CH * 10];
layer_buffer.fill(1.0);
let mut simd_out = vec![0.0f32; CH];
unsafe {
conv.process_single_frame::<crate::math::common::Avx2Math>(&layer_buffer, &mut simd_out, 5);
}
let mut scalar_out = [0.0f32; CH];
for (oc, s) in scalar_out.iter_mut().enumerate() {
let mut sum = 0.1; for k in 0..K {
let offset = (k as isize) + 1 - (K as isize);
let in_idx = ((5isize + offset) as usize) * CH;
for ic in 0..CH {
let w_idx = k * CH * CH + ic * CH + oc;
sum += layer_buffer[in_idx + ic] * weights[w_idx];
}
}
*s = sum;
}
for oc in 0..CH {
let diff = (simd_out[oc] - scalar_out[oc]).abs();
assert!(
diff < 1e-5,
"CH=8 ch{} mismatch: simd={}, scalar={}, diff={}",
oc,
simd_out[oc],
scalar_out[oc],
diff
);
}
}
#[test]
fn test_conv1d_ch16_wide_interleaving() {
use crate::math::common::AlignedVec;
const CH: usize = 16;
const K: usize = 2;
let mut raw = vec![0.0f32; CH * K * CH];
for out_c in 0..CH {
for k in 0..K {
for in_c in 0..CH {
let idx = (out_c * CH + in_c) * K + k;
raw[idx] = (out_c + 1) as f32 * 0.5;
}
}
}
let mut weights = AlignedVec::new(CH * K * CH, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::layout::transpose_conv1d_interleaved_16wide(
&raw,
&mut weights,
CH,
CH,
K,
);
let conv = Conv1d::<CH, CH, K> {
weights: weights.clone(),
bias: AlignedVec::from_vec(vec![0.1f32; CH])
.expect("allocation should succeed for test-sized buffers"),
do_bias: true,
dilation: 1,
};
let mut layer_buffer = vec![0.0f32; CH * 10];
layer_buffer.fill(1.0);
let mut simd_out = vec![0.0f32; CH];
unsafe {
conv.process_single_frame::<crate::math::common::Avx2Math>(&layer_buffer, &mut simd_out, 5);
}
let mut scalar_out = [0.0f32; CH];
for (oc, s) in scalar_out.iter_mut().enumerate() {
let mut sum = 0.1; for k in 0..K {
let offset = (k as isize) + 1 - (K as isize);
let in_idx = ((5isize + offset) as usize) * CH;
for ic in 0..CH {
let w_idx = k * CH * CH + ic * CH + oc;
sum += layer_buffer[in_idx + ic] * weights[w_idx];
}
}
*s = sum;
}
for oc in 0..CH {
let diff = (simd_out[oc] - scalar_out[oc]).abs();
assert!(
diff < 1e-5,
"CH=16 ch{} mismatch: simd={}, scalar={}, diff={}",
oc,
simd_out[oc],
scalar_out[oc],
diff
);
}
}
#[test]
fn test_4wide_to_16wide_transpose_correctness() {
use crate::math::common::AlignedVec;
const CH: usize = 16;
const K: usize = 2;
let mut raw = vec![0.0f32; CH * K * CH];
for out_c in 0..CH {
for k in 0..K {
for in_c in 0..CH {
let idx = (out_c * CH + in_c) * K + k;
raw[idx] = (out_c + 1) as f32 * 0.5 + (k as f32) * 0.1;
}
}
}
let padded_4 = (CH.div_ceil(4)) * 4 * CH * K;
let mut weights_4wide = AlignedVec::new(padded_4, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::layout::transpose_conv1d_interleaved_4wide(
&raw,
&mut weights_4wide,
CH,
CH,
K,
);
let mut weights_16wide_via_4to16 = AlignedVec::new(CH * K * CH, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::layout::transpose_4wide_to_16wide(
&weights_4wide,
&mut weights_16wide_via_4to16,
CH,
CH,
K,
);
let mut weights_16wide_direct = AlignedVec::new(CH * K * CH, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
crate::loader::dispatcher::wavenet::layout::transpose_conv1d_interleaved_16wide(
&raw,
&mut weights_16wide_direct,
CH,
CH,
K,
);
for (i, (&a, &b)) in weights_16wide_via_4to16
.iter()
.zip(weights_16wide_direct.iter())
.enumerate()
{
assert!(
(a - b).abs() < 1e-6,
"4wide→16wide vs direct→16wide mismatch at idx {i}: {a} vs {b}"
);
}
}
#[test]
#[should_panic(expected = "Conv1d weights buffer is too small")]
fn test_conv1d_from_parts_subdimensioned_weights() {
use crate::loader::dispatcher::wavenet::layout::select_interleave_width;
use crate::loader::dispatcher::wavenet::traits::ConvWeightsOutput;
use crate::math::common::AlignedVec;
const IN: usize = 4;
const OUT: usize = 8;
const K: usize = 2;
let interleave_width = select_interleave_width(OUT);
let num_blocks = OUT.div_ceil(interleave_width);
let padded_total = num_blocks * interleave_width * IN * K;
let undersized = padded_total / 2;
let weights = AlignedVec::new(undersized, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
let bias =
AlignedVec::new(OUT, 0.0f32).expect("allocation should succeed for test-sized buffers");
Conv1d::<IN, OUT, K>::from_parts(weights, bias, false, 1, IN, OUT, K);
}