#[test]
#[cfg(feature = "gpu")]
#[allow(clippy::similar_names)] fn test_imp_052_quantized_dot() {
use crate::gpu::{quantized_dot_q4, quantized_dot_q8};
let scale_a: f32 = 0.5;
let scale_b: f32 = 0.25;
let mut block_a = vec![0u8; 18];
let mut block_b = vec![0u8; 18];
let scale_a_f16 = half::f16::from_f32(scale_a);
let scale_b_f16 = half::f16::from_f32(scale_b);
block_a[0..2].copy_from_slice(&scale_a_f16.to_le_bytes());
block_b[0..2].copy_from_slice(&scale_b_f16.to_le_bytes());
for i in 2..18 {
block_a[i] = 0x99; block_b[i] = 0x99;
}
let result_q4 = quantized_dot_q4(&block_a, &block_b);
assert!(
(result_q4 - 4.0).abs() < 0.5,
"IMP-052: Q4 dot product result ({}) should be ~4.0",
result_q4
);
let mut block_a_q8 = vec![0u8; 34];
let mut block_b_q8 = vec![0u8; 34];
block_a_q8[0..2].copy_from_slice(&scale_a_f16.to_le_bytes());
block_b_q8[0..2].copy_from_slice(&scale_b_f16.to_le_bytes());
for i in 2..34 {
block_a_q8[i] = 1i8 as u8;
block_b_q8[i] = 1i8 as u8;
}
let result_q8 = quantized_dot_q8(&block_a_q8, &block_b_q8);
assert!(
(result_q8 - 4.0).abs() < 0.5,
"IMP-052: Q8 dot product result ({}) should be ~4.0",
result_q8
);
let zero_block_q4 = vec![0u8; 18];
let zero_result = quantized_dot_q4(&zero_block_q4, &zero_block_q4);
assert!(
zero_result.abs() < 1e-6,
"IMP-052: Zero blocks should give zero dot product"
);
}
#[test]
#[cfg(feature = "gpu")]
fn test_imp_053_quantized_matvec() {
use crate::gpu::{quantized_matvec_q4, quantized_matvec_q8};
let rows = 2;
let cols = 32;
let scale: f32 = 0.1;
let scale_f16 = half::f16::from_f32(scale);
let mut weights_q4 = vec![0u8; rows * 18];
for row in 0..rows {
let offset = row * 18;
weights_q4[offset..offset + 2].copy_from_slice(&scale_f16.to_le_bytes());
for i in 2..18 {
weights_q4[offset + i] = 0x99;
}
}
let input: Vec<f32> = vec![1.0; cols];
let result_q4 = quantized_matvec_q4(&weights_q4, &input, rows, cols);
assert_eq!(
result_q4.len(),
rows,
"IMP-053: Q4 matvec should produce {} outputs",
rows
);
for (i, &val) in result_q4.iter().enumerate() {
assert!(
(val - 3.2).abs() < 0.5,
"IMP-053: Q4 matvec row {} ({}) should be ~3.2",
i,
val
);
}
let mut weights_q8 = vec![0u8; rows * 34];
for row in 0..rows {
let offset = row * 34;
weights_q8[offset..offset + 2].copy_from_slice(&scale_f16.to_le_bytes());
for i in 2..34 {
weights_q8[offset + i] = 1i8 as u8;
}
}
let result_q8 = quantized_matvec_q8(&weights_q8, &input, rows, cols);
assert_eq!(
result_q8.len(),
rows,
"IMP-053: Q8 matvec should produce {} outputs",
rows
);
for (i, &val) in result_q8.iter().enumerate() {
assert!(
(val - 3.2).abs() < 0.5,
"IMP-053: Q8 matvec row {} ({}) should be ~3.2",
i,
val
);
}
}
#[test]
#[cfg(feature = "gpu")]
fn test_imp_054_mixed_precision() {
use crate::gpu::QuantizedAccumulator;
let mut acc = QuantizedAccumulator::new();
assert_eq!(
acc.sum(),
0.0,
"IMP-054: New accumulator should have zero sum"
);
acc.add_scaled(1.0, 0.5); acc.add_scaled(2.0, 0.5); acc.add_scaled(3.0, 0.5);
assert!(
(acc.sum() - 3.0).abs() < 1e-6,
"IMP-054: Accumulator sum ({}) should be 3.0",
acc.sum()
);
acc.reset();
assert_eq!(
acc.sum(),
0.0,
"IMP-054: Reset accumulator should have zero sum"
);
let block_sum: f32 = 10.0;
let block_scale: f32 = 0.1;
acc.add_block(block_sum, block_scale);
assert!(
(acc.sum() - 1.0).abs() < 1e-6,
"IMP-054: Block contribution ({}) should be 1.0",
acc.sum()
);
acc.reset();
for _ in 0..10 {
acc.add_block(5.0, 0.2); }
assert!(
(acc.sum() - 10.0).abs() < 1e-5,
"IMP-054: 10 blocks should sum to 10.0, got {}",
acc.sum()
);
}
#[test]
#[cfg(feature = "gpu")]
fn test_imp_055_double_buffer() {
use crate::gpu::DoubleBuffer;
let buffer: DoubleBuffer<f32> = DoubleBuffer::new(1024);
assert_eq!(
buffer.capacity(),
1024,
"IMP-055: Double buffer should have requested capacity"
);
let front = buffer.front();
assert_eq!(
front.len(),
1024,
"IMP-055: Front buffer should have full capacity"
);
let mut buffer = DoubleBuffer::new(256);
{
let back = buffer.back_mut();
for (i, val) in back.iter_mut().enumerate() {
*val = i as f32;
}
}
buffer.swap();
let front_after_swap = buffer.front();
assert!(
(front_after_swap[0] - 0.0).abs() < 1e-6,
"IMP-055: After swap, front[0] should be 0.0"
);
assert!(
(front_after_swap[255] - 255.0).abs() < 1e-6,
"IMP-055: After swap, front[255] should be 255.0"
);
{
let back = buffer.back_mut();
for val in back.iter_mut() {
*val = 42.0;
}
}
buffer.swap();
let front_again = buffer.front();
assert!(
(front_again[0] - 42.0).abs() < 1e-6,
"IMP-055: After second swap, front should have 42.0 values"
);
}
#[test]
#[cfg(feature = "gpu")]
fn test_imp_056_chunked_processing() {
use crate::gpu::ChunkedProcessor;
let processor = ChunkedProcessor::new(64);
assert_eq!(
processor.chunk_size(),
64,
"IMP-056: Processor should have requested chunk size"
);
assert_eq!(
processor.num_chunks(100),
2,
"IMP-056: 100 items with chunk_size=64 needs 2 chunks"
);
assert_eq!(
processor.num_chunks(64),
1,
"IMP-056: 64 items with chunk_size=64 needs 1 chunk"
);
assert_eq!(
processor.num_chunks(0),
0,
"IMP-056: 0 items needs 0 chunks"
);
let (start, end) = processor.chunk_bounds(0, 100);
assert_eq!(start, 0, "IMP-056: First chunk starts at 0");
assert_eq!(end, 64, "IMP-056: First chunk ends at chunk_size");
let (start, end) = processor.chunk_bounds(1, 100);
assert_eq!(start, 64, "IMP-056: Second chunk starts at 64");
assert_eq!(end, 100, "IMP-056: Second chunk ends at total length");
let data: Vec<f32> = (0..128).map(|x| x as f32).collect();
let sum = processor.process_chunks(&data, |chunk| chunk.iter().sum::<f32>());
assert!(
(sum - 8128.0).abs() < 1e-3,
"IMP-056: Chunked sum ({}) should equal 8128.0",
sum
);
let small_data: Vec<f32> = vec![1.0, 2.0, 3.0];
let small_sum = processor.process_chunks(&small_data, |chunk| chunk.iter().sum::<f32>());
assert!(
(small_sum - 6.0).abs() < 1e-6,
"IMP-056: Small chunked sum ({}) should equal 6.0",
small_sum
);
}
#[test]
#[cfg(feature = "gpu")]
fn test_imp_057_pipeline_stages() {
use crate::gpu::{GpuPipelineStage, InferencePipeline};
let embed = GpuPipelineStage::Embed;
let attention = GpuPipelineStage::Attention;
let ffn = GpuPipelineStage::FFN;
let output = GpuPipelineStage::Output;
assert!(
(embed as u8) < (attention as u8),
"IMP-057: Embed should come before Attention"
);
assert!(
(attention as u8) < (ffn as u8),
"IMP-057: Attention should come before FFN"
);
assert!(
(ffn as u8) < (output as u8),
"IMP-057: FFN should come before Output"
);
let mut pipeline = InferencePipeline::new(4); assert_eq!(
pipeline.num_stages(),
4,
"IMP-057: Pipeline should have 4 stages"
);
pipeline.record_stage_time(GpuPipelineStage::Embed, 1.0);
pipeline.record_stage_time(GpuPipelineStage::Attention, 5.0);
pipeline.record_stage_time(GpuPipelineStage::FFN, 3.0);
pipeline.record_stage_time(GpuPipelineStage::Output, 0.5);
let total = pipeline.total_latency();
assert!(
(total - 9.5).abs() < 1e-6,
"IMP-057: Total latency ({}) should be 9.5ms",
total
);
let breakdown = pipeline.stage_breakdown();
assert!(
(breakdown[&GpuPipelineStage::Attention] - 5.0).abs() < 1e-6,
"IMP-057: Attention stage should be 5.0ms"
);
pipeline.reset();
assert!(
pipeline.total_latency() < 1e-6,
"IMP-057: Reset pipeline should have zero latency"
);
}