Files
rustytorch/crates/specialized/rtx-synthesis/src/synthesis_tests.rs
T
osobhandClaude Opus 4.6 02d382d5f6 style: apply rustfmt across all crates and demos
Consistent formatting pass: line wrapping, import sorting, trailing
whitespace removal, let-chain indentation, merged derive attributes,
and unsafe block reformatting.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2026-04-12 07:01:58 -07:00

552 lines
17 KiB
Rust

//! Tests for the synthesis engine
// Shape guards tests - active tests
use crate::aot::shape_guards::{
GuardCheckResult, GuardFailure, ShapeDim, ShapeGuard, ShapeGuardConfig, ShapeGuardManager,
ShapeSignature,
};
#[test]
fn test_shape_dim_concrete() {
let dim = ShapeDim::concrete(256);
assert!(dim.matches(256));
assert!(!dim.matches(128));
assert!(dim.is_concrete());
}
#[test]
fn test_shape_dim_symbolic() {
let dim = ShapeDim::symbolic("batch_size");
assert!(dim.matches(1));
assert!(dim.matches(256));
assert!(dim.matches(10000));
assert!(!dim.is_concrete());
assert_eq!(dim.symbolic_name(), Some("batch_size"));
}
#[test]
fn test_shape_dim_bounded() {
let dim = ShapeDim::bounded("seq_len", 1, 512);
assert!(dim.matches(1));
assert!(dim.matches(256));
assert!(dim.matches(512));
assert!(!dim.matches(0));
assert!(!dim.matches(513));
}
#[test]
fn test_shape_guard_check_pass() {
let guard = ShapeGuard::concrete("input_0", &[32, 256, 256]);
match guard.check(&[32, 256, 256], Some("fp32")) {
GuardCheckResult::Passed => {}
GuardCheckResult::Failed(f) => panic!("Guard should pass: {:?}", f),
}
}
#[test]
fn test_shape_guard_check_rank_mismatch() {
let guard = ShapeGuard::concrete("input_0", &[32, 256, 256]);
match guard.check(&[32, 256], None) {
GuardCheckResult::Failed(GuardFailure::RankMismatch { .. }) => {}
other => panic!("Expected RankMismatch, got {:?}", other),
}
}
#[test]
fn test_shape_guard_check_dim_mismatch() {
let guard = ShapeGuard::concrete("input_0", &[32, 256, 256]);
match guard.check(&[64, 256, 256], None) {
GuardCheckResult::Failed(GuardFailure::DimensionMismatch { dim: 0, .. }) => {}
other => panic!("Expected DimensionMismatch at dim 0, got {:?}", other),
}
}
#[test]
fn test_shape_guard_symbolic_bindings() {
let guard = ShapeGuard::new(
"input_0",
vec![
ShapeDim::symbolic("batch"),
ShapeDim::concrete(256),
ShapeDim::symbolic("seq_len"),
],
);
let bindings = guard.extract_bindings(&[32, 256, 512]);
assert_eq!(bindings.get("batch"), Some(&32));
assert_eq!(bindings.get("seq_len"), Some(&512));
assert_eq!(bindings.len(), 2);
}
#[test]
fn test_shape_signature_hash() {
let inputs1 = vec![("input_0".to_string(), vec![32, 256], "fp32".to_string())];
let inputs2 = vec![("input_0".to_string(), vec![32, 256], "fp32".to_string())];
let inputs3 = vec![("input_0".to_string(), vec![64, 256], "fp32".to_string())];
let sig1 = ShapeSignature::from_inputs(&inputs1, 12345);
let sig2 = ShapeSignature::from_inputs(&inputs2, 12345);
let sig3 = ShapeSignature::from_inputs(&inputs3, 12345);
assert_eq!(sig1.to_cache_key(), sig2.to_cache_key());
assert_ne!(sig1.to_cache_key(), sig3.to_cache_key());
}
#[test]
fn test_shape_guard_manager_stats() {
let manager = ShapeGuardManager::default_manager();
assert_eq!(manager.stats().total_checks, 0);
assert_eq!(manager.stats().cache_hits, 0);
}
#[cfg(feature = "disabled_tests")]
mod integration_tests {
use super::*;
use crate::{
SynthesisEngine,
aot::{CompiledGraph, ExecutionPlan},
templates,
};
#[tokio::test]
async fn test_synthesis_engine_creation() {
let result = SynthesisEngine::new("sm_120");
assert!(result.is_ok());
}
#[tokio::test]
async fn test_synthesis_engine_initialization() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
let result = engine.initialize().await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_json_ir_parsing() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
// Test simple JSON operation
let json_ir = r#"{
"id": "test_op",
"op_type": "gemm",
"attributes": {
"M": 256,
"N": 256,
"K": 256,
"transpose_a": false,
"transpose_b": true
},
"inputs": ["input_a", "input_b"],
"outputs": ["output_c"]
}"#;
let operations = engine
.parse_graph_ir(json_ir)
.expect("Should parse JSON successfully");
assert_eq!(operations.len(), 1);
let op = &operations[0];
assert_eq!(op.id, "test_op");
assert_eq!(op.inputs, vec!["input_a", "input_b"]);
assert_eq!(op.outputs, vec!["output_c"]);
if let templates::KernelOperation::Gemm {
m,
n,
k,
transpose_a,
transpose_b,
} = op.operation
{
assert_eq!(m, 256);
assert_eq!(n, 256);
assert_eq!(k, 256);
assert!(!transpose_a);
assert!(transpose_b);
} else {
panic!("Expected GEMM operation");
}
}
#[tokio::test]
async fn test_json_array_ir_parsing() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
// Test array of operations
let json_ir = r#"[
{
"id": "conv_op",
"op_type": "conv2d",
"attributes": {
"batch_size": 1,
"in_channels": 64,
"out_channels": 128,
"height": 224,
"width": 224,
"kernel_size": 3
}
},
{
"id": "relu_op",
"op_type": "relu",
"attributes": {
"size": 200704
}
}
]"#;
let operations = engine
.parse_graph_ir(json_ir)
.expect("Should parse JSON array successfully");
assert_eq!(operations.len(), 2);
// Check convolution
if let templates::KernelOperation::Convolution {
batch_size,
in_channels,
out_channels,
height,
width,
kernel_size,
} = operations[0].operation
{
assert_eq!(batch_size, 1);
assert_eq!(in_channels, 64);
assert_eq!(out_channels, 128);
assert_eq!(height, 224);
assert_eq!(width, 224);
assert_eq!(kernel_size, 3);
} else {
panic!("Expected Convolution operation");
}
// Check ReLU
if let templates::KernelOperation::Elementwise { operation, size } = operations[1].operation
{
assert_eq!(size, 200704);
if let templates::ElementwiseOp::ReLU = operation {
// Correct
} else {
panic!("Expected ReLU elementwise operation");
}
} else {
panic!("Expected Elementwise operation");
}
}
#[tokio::test]
async fn test_custom_dsl_parsing() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
let dsl_ir = r#"
# Test DSL format
op matrix_mult gemm
inputs: A, B
outputs: C
fusable: true
op activation add
inputs: C, bias
outputs: result
fusable: true
"#;
let operations = engine
.parse_graph_ir(dsl_ir)
.expect("Should parse DSL successfully");
assert_eq!(operations.len(), 2);
// Check first operation
let gemm_op = &operations[0];
assert_eq!(gemm_op.id, "matrix_mult");
assert_eq!(gemm_op.inputs, vec!["A", "B"]);
assert_eq!(gemm_op.outputs, vec!["C"]);
assert!(gemm_op.fusable);
// Check second operation
let add_op = &operations[1];
assert_eq!(add_op.id, "activation");
assert_eq!(add_op.inputs, vec!["C", "bias"]);
assert_eq!(add_op.outputs, vec!["result"]);
assert!(add_op.fusable);
}
#[tokio::test]
async fn test_graph_object_parsing() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
let graph_json = r#"{
"graph": {
"nodes": [
{
"id": "attention_op",
"op_type": "attention",
"attributes": {
"sequence_length": 1024,
"head_dim": 64,
"num_heads": 16
}
}
],
"edges": [
{"from": "input", "to": "attention_op"}
]
}
}"#;
let operations = engine
.parse_graph_ir(graph_json)
.expect("Should parse graph JSON successfully");
assert_eq!(operations.len(), 1);
if let templates::KernelOperation::Attention {
sequence_length,
head_dim,
num_heads,
} = operations[0].operation
{
assert_eq!(sequence_length, 1024);
assert_eq!(head_dim, 64);
assert_eq!(num_heads, 16);
} else {
panic!("Expected Attention operation");
}
}
#[test]
fn test_flops_calculation() {
let engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
// Test GEMM FLOPS
let gemm_op = templates::KernelOperation::Gemm {
m: 128,
n: 128,
k: 128,
transpose_a: false,
transpose_b: false,
};
let flops = engine.calculate_theoretical_flops(&gemm_op);
let expected = 2.0 * 128.0 * 128.0 * 128.0; // 2 * M * N * K
assert!(
(flops - expected).abs() < 1.0,
"GEMM FLOPS calculation incorrect"
);
// Test Convolution FLOPS
let conv_op = templates::KernelOperation::Convolution {
batch_size: 1,
in_channels: 64,
out_channels: 128,
height: 224,
width: 224,
kernel_size: 3,
};
let conv_flops = engine.calculate_theoretical_flops(&conv_op);
let expected_conv = 2.0 * 1.0 * 128.0 * 224.0 * 224.0 * 64.0 * 9.0; // 2 * B * OC * H * W * IC * K^2
assert!(
(conv_flops - expected_conv).abs() < 1.0,
"Convolution FLOPS calculation incorrect"
);
// Test Elementwise FLOPS
let elem_op = templates::KernelOperation::Elementwise {
operation: templates::ElementwiseOp::Add,
size: 1024,
};
let elem_flops = engine.calculate_theoretical_flops(&elem_op);
assert_eq!(elem_flops, 1024.0, "Elementwise FLOPS should equal size");
}
#[test]
fn test_memory_usage_estimation() {
let engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
// Test GEMM memory usage
let gemm_op = templates::KernelOperation::Gemm {
m: 128,
n: 128,
k: 128,
transpose_a: false,
transpose_b: false,
};
let memory = engine.estimate_memory_usage(&gemm_op);
let expected = 4.0 * ((128 * 128) + (128 * 128) + (128 * 128)) as f64; // A + B + C in FP32
assert!(
(memory - expected).abs() < 1.0,
"GEMM memory estimation incorrect"
);
// Test Elementwise memory usage
let elem_op = templates::KernelOperation::Elementwise {
operation: templates::ElementwiseOp::Add,
size: 1024,
};
let elem_memory = engine.estimate_memory_usage(&elem_op);
assert_eq!(
elem_memory,
8.0 * 1024.0,
"Elementwise memory should be 2 * size * sizeof(f32)"
);
}
#[tokio::test]
async fn test_parameter_space_creation() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
engine.initialize().await.expect("Should initialize");
let template = templates::KernelTemplate::new("test_template".to_string());
let gemm_op = templates::KernelOperation::Gemm {
m: 256,
n: 256,
k: 256,
transpose_a: false,
transpose_b: false,
};
let param_space = engine
.create_parameter_space(&template, &gemm_op)
.expect("Should create parameter space");
// Check that GEMM-specific parameters are included
assert!(param_space.dimensions.contains_key("tile_m"));
assert!(param_space.dimensions.contains_key("tile_n"));
assert!(param_space.dimensions.contains_key("tile_k"));
assert!(param_space.dimensions.contains_key("block_size_x"));
assert!(param_space.dimensions.contains_key("block_size_y"));
// Check constraints
assert!(!param_space.constraints.is_empty());
assert!(
param_space
.constraints
.iter()
.any(|c| c.contains("block_size_x * block_size_y"))
);
}
#[test]
fn test_parameter_conversion() {
let engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
let mut int_params = std::collections::HashMap::new();
int_params.insert("block_size_x".to_string(), 128);
int_params.insert("block_size_y".to_string(), 8);
int_params.insert("tile_m".to_string(), 64);
let template_params = engine
.convert_to_template_params(&int_params)
.expect("Should convert parameters");
assert_eq!(template_params["block_size_x"], "128");
assert_eq!(template_params["block_size_y"], "8");
assert_eq!(template_params["tile_m"], "64");
}
#[tokio::test]
async fn test_invalid_ir_formats() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
// Test invalid JSON
let invalid_json = r#"{ invalid json }"#;
let result = engine.parse_graph_ir(invalid_json);
assert!(result.is_err(), "Should reject invalid JSON");
// Test unknown format
let unknown_format = "this is not a known IR format";
let result = engine.parse_graph_ir(unknown_format);
assert!(result.is_err(), "Should reject unknown format");
}
#[test]
fn test_compiled_kernel_structure() {
use crate::CompiledKernel;
let compiled_graph = CompiledGraph {
kernel_sequence: vec![],
memory_plan: std::collections::HashMap::new(),
execution_plan: ExecutionPlan {
kernel_launches: vec![],
sync_points: vec![],
},
};
let kernel = CompiledKernel {
code: "test kernel code".to_string(),
compiled_graph,
};
assert_eq!(kernel.code, "test kernel code");
assert!(kernel.compiled_graph.kernel_sequence.is_empty());
}
#[tokio::test]
async fn test_tuning_config_creation() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
engine.initialize().await.expect("Should initialize");
// Test GEMM config (should have more iterations)
let gemm_op = templates::KernelOperation::Gemm {
m: 128,
n: 128,
k: 128,
transpose_a: false,
transpose_b: false,
};
let gemm_config = engine
.create_tuning_config(&gemm_op)
.expect("Should create GEMM config");
assert_eq!(gemm_config.max_iterations, 100);
// Test Attention config
let attention_op = templates::KernelOperation::Attention {
sequence_length: 512,
head_dim: 64,
num_heads: 8,
};
let attention_config = engine
.create_tuning_config(&attention_op)
.expect("Should create Attention config");
assert_eq!(attention_config.max_iterations, 80);
// Test default config for other operations
let elem_op = templates::KernelOperation::Elementwise {
operation: templates::ElementwiseOp::Add,
size: 1024,
};
let elem_config = engine
.create_tuning_config(&elem_op)
.expect("Should create default config");
assert_eq!(elem_config.max_iterations, 50);
}
#[test]
fn test_execution_time_estimation() {
let mut engine = SynthesisEngine::new("sm_120").expect("Failed to create engine");
let mock_graph = CompiledGraph {
kernel_sequence: vec![],
memory_plan: std::collections::HashMap::new(),
execution_plan: ExecutionPlan {
kernel_launches: vec![],
sync_points: vec![],
},
};
let gemm_op = templates::KernelOperation::Gemm {
m: 1024,
n: 1024,
k: 1024,
transpose_a: false,
transpose_b: false,
};
let exec_time = engine.estimate_execution_time(&gemm_op, &mock_graph);
// Should be reasonable execution time (not zero, not too large)
assert!(exec_time.as_secs_f64() > 0.0);
assert!(exec_time.as_secs_f64() < 10.0); // Should complete within 10 seconds for reasonable sizes
}
}