struct ContractFacts {
equation_ids: Vec<String>,
formulas: Vec<String>,
invariants: Vec<String>,
phase_names: Vec<String>,
obligation_types: Vec<String>,
obligation_props: Vec<String>,
}
fn extract_facts(contract: &str) -> ContractFacts {
let yaml = std::fs::read_to_string(fixture_path(contract)).unwrap();
let doc: serde_yaml::Value = serde_yaml::from_str(&yaml).unwrap();
let equations = doc["equations"].as_mapping().unwrap();
let mut equation_ids = Vec::new();
let mut formulas = Vec::new();
let mut invariants = Vec::new();
for (id, eq) in equations {
equation_ids.push(id.as_str().unwrap().to_string());
formulas.push(eq["formula"].as_str().unwrap().to_string());
if let Some(invs) = eq.get("invariants").and_then(|v| v.as_sequence()) {
for inv in invs {
invariants.push(inv.as_str().unwrap().to_string());
}
}
}
let mut phase_names = Vec::new();
if let Some(ks) = doc.get("kernel_structure") {
if let Some(phases) = ks.get("phases").and_then(|v| v.as_sequence()) {
for phase in phases {
phase_names.push(phase["name"].as_str().unwrap().to_string());
}
}
}
let mut obligation_types = Vec::new();
let mut obligation_props = Vec::new();
if let Some(obs) = doc.get("proof_obligations").and_then(|v| v.as_sequence()) {
for ob in obs {
obligation_types.push(ob["type"].as_str().unwrap().to_string());
obligation_props.push(ob["property"].as_str().unwrap().to_string());
}
}
ContractFacts {
equation_ids,
formulas,
invariants,
phase_names,
obligation_types,
obligation_props,
}
}
fn verify_provability(contract: &str) {
let facts = extract_facts(contract);
let text = run_equations(contract, "text");
let latex = run_equations(contract, "latex");
let ptx = run_equations(contract, "ptx");
let asm = run_equations(contract, "asm");
for id in &facts.equation_ids {
assert!(text.contains(id), "text missing equation ID '{id}'");
assert!(latex.contains(id), "latex missing equation ID '{id}'");
}
for formula in &facts.formulas {
assert!(text.contains(formula), "text missing formula '{formula}'");
}
for inv in &facts.invariants {
assert!(text.contains(inv), "text missing invariant '{inv}'");
}
for (i, name) in facts.phase_names.iter().enumerate() {
let phase_label = format!("Phase {}: {name}", i + 1);
assert!(
ptx.contains(&phase_label),
"ptx missing phase '{phase_label}'"
);
assert!(
asm.contains(&phase_label),
"asm missing phase '{phase_label}'"
);
}
for prop in &facts.obligation_props {
assert!(ptx.contains(prop), "ptx missing obligation '{prop}'");
assert!(asm.contains(prop), "asm missing obligation '{prop}'");
}
for otype in &facts.obligation_types {
let tag = format!("[{otype}]");
assert!(ptx.contains(&tag), "ptx missing obligation type '{tag}'");
assert!(asm.contains(&tag), "asm missing obligation type '{tag}'");
}
let ptx_ob_count = ptx.lines().filter(|l| l.contains("// [")).count();
let asm_ob_count = asm.lines().filter(|l| l.contains("// [")).count();
assert_eq!(
ptx_ob_count, asm_ob_count,
"PTX ({ptx_ob_count}) and ASM ({asm_ob_count}) obligation count mismatch"
);
if latex.contains("\\in") || latex.contains("\\mathbb{R}") {
assert!(
!latex.contains('∈'),
"latex still contains raw ∈ (should be \\in)"
);
assert!(
!latex.contains('ℝ'),
"latex still contains raw ℝ (should be \\mathbb{{R}})"
);
}
if latex.contains("\\geq") || latex.contains("\\leq") {
assert!(
!latex.contains('≥'),
"latex still contains raw ≥ (should be \\geq)"
);
assert!(
!latex.contains('≤'),
"latex still contains raw ≤ (should be \\leq)"
);
}
assert!(ptx.contains(".version 8.5"), "ptx missing .version");
assert!(ptx.contains(".target sm_90"), "ptx missing .target");
assert!(ptx.contains(".visible .entry"), "ptx missing .entry");
assert!(ptx.contains("ret;"), "ptx missing ret");
assert!(asm.contains(".intel_syntax noprefix"), "asm missing syntax");
assert!(asm.contains(".globl"), "asm missing .globl");
assert!(asm.contains("push rbp"), "asm missing prologue");
assert!(asm.contains("pop rbp"), "asm missing epilogue");
assert!(asm.contains("ret"), "asm missing ret");
}
#[test]
fn provability_relu() {
verify_provability("relu-kernel-v1.yaml");
}
#[test]
fn provability_clamp() {
verify_provability("clamp-kernel-v1.yaml");
}
#[test]
fn provability_dot() {
verify_provability("dot-kernel-v1.yaml");
}
#[test]
fn provability_scale() {
verify_provability("scale-kernel-v1.yaml");
}
#[test]
fn provability_l2norm() {
verify_provability("l2norm-kernel-v1.yaml");
}
#[test]
fn latex_conversions_relu() {
let latex = run_equations("relu-kernel-v1.yaml", "latex");
assert!(
latex.contains("\\geq"),
"relu latex: ≥ not converted to \\geq"
);
assert!(
latex.contains("\\in"),
"relu latex: ∈ not converted to \\in"
);
assert!(latex.contains("\\mathbb{R}"), "relu latex: ℝ not converted");
}
#[test]
fn latex_conversions_clamp() {
let latex = run_equations("clamp-kernel-v1.yaml", "latex");
assert!(
latex.contains("\\leq"),
"clamp latex: ≤ not converted to \\leq"
);
assert!(
latex.contains("\\to"),
"clamp latex: → not converted to \\to"
);
}
#[test]
fn latex_conversions_dot() {
let latex = run_equations("dot-kernel-v1.yaml", "latex");
assert!(
latex.contains("\\sum"),
"dot latex: Σ not converted to \\sum"
);
assert!(
latex.contains("\\alpha"),
"dot latex: α not converted to \\alpha"
);
}
#[test]
fn latex_conversions_scale() {
let latex = run_equations("scale-kernel-v1.yaml", "latex");
assert!(latex.contains("\\alpha"), "scale latex: α not converted");
assert!(latex.contains("\\beta"), "scale latex: β not converted");
}
#[test]
fn latex_conversions_l2norm() {
let latex = run_equations("l2norm-kernel-v1.yaml", "latex");
assert!(
latex.contains("\\sqrt{"),
"l2norm latex: sqrt not converted"
);
assert!(latex.contains("\\sum"), "l2norm latex: Σ not converted");
assert!(latex.contains("\\geq"), "l2norm latex: ≥ not converted");
assert!(latex.contains("\\alpha"), "l2norm latex: α not converted");
}
#[test]
fn asm_isa_detection() {
for contract in &[
"relu-kernel-v1.yaml",
"clamp-kernel-v1.yaml",
"dot-kernel-v1.yaml",
"scale-kernel-v1.yaml",
"l2norm-kernel-v1.yaml",
] {
let asm = run_equations(contract, "asm");
assert!(
asm.contains("AVX2"),
"{contract}: asm should detect AVX2 ISA"
);
assert!(
asm.contains("ymm"),
"{contract}: asm should use ymm registers for AVX2"
);
assert!(
asm.contains("_avx2:"),
"{contract}: asm function label should end with _avx2"
);
}
}
#[test]
fn ptx_kernel_names() {
let cases: &[(&str, &str)] = &[
("relu-kernel-v1.yaml", ".visible .entry relu("),
("clamp-kernel-v1.yaml", ".visible .entry clamp("),
("dot-kernel-v1.yaml", ".visible .entry dot("),
("scale-kernel-v1.yaml", ".visible .entry scale("),
("l2norm-kernel-v1.yaml", ".visible .entry l2norm("),
];
for &(contract, expected_entry) in cases {
let ptx = run_equations(contract, "ptx");
assert!(
ptx.contains(expected_entry),
"{contract}: expected PTX entry '{expected_entry}'"
);
}
}
#[test]
fn asm_function_labels() {
let cases: &[(&str, &str)] = &[
("relu-kernel-v1.yaml", "relu_avx2:"),
("clamp-kernel-v1.yaml", "clamp_avx2:"),
("dot-kernel-v1.yaml", "dot_avx2:"),
("scale-kernel-v1.yaml", "scale_avx2:"),
("l2norm-kernel-v1.yaml", "l2norm_avx2:"),
];
for &(contract, expected_label) in cases {
let asm = run_equations(contract, "asm");
assert!(
asm.contains(expected_label),
"{contract}: expected ASM label '{expected_label}'"
);
}
}
#[test]
fn phase_count_consistency() {
let cases: &[(&str, usize)] = &[
("relu-kernel-v1.yaml", 2),
("clamp-kernel-v1.yaml", 3),
("dot-kernel-v1.yaml", 2),
("scale-kernel-v1.yaml", 1),
("l2norm-kernel-v1.yaml", 3),
];
for &(contract, expected_phases) in cases {
let ptx = run_equations(contract, "ptx");
let asm = run_equations(contract, "asm");
let ptx_phases = ptx.lines().filter(|l| l.contains("// Phase ")).count();
let asm_phases = asm.lines().filter(|l| l.contains("// Phase ")).count();
assert_eq!(
ptx_phases, expected_phases,
"{contract}: PTX has {ptx_phases} phases, expected {expected_phases}"
);
assert_eq!(
asm_phases, expected_phases,
"{contract}: ASM has {asm_phases} phases, expected {expected_phases}"
);
}
}