use rlx_ir::{Graph, NodeId};
use rlx_runtime::{CompileOptions, Device, Session};
use std::collections::HashMap;
pub fn expose_all_ops(graph: &Graph) -> Graph {
let mut g = Graph::new(&graph.name);
let mut id_map: HashMap<NodeId, NodeId> = HashMap::new();
let mut exposed = Vec::new();
for node in graph.nodes() {
let inputs: Vec<NodeId> = node.inputs.iter().map(|i| id_map[i]).collect();
let new_id = g.add_node(node.op.clone(), inputs, node.shape.clone());
id_map.insert(node.id, new_id);
if !node.inputs.is_empty() {
exposed.push(new_id); }
}
g.set_outputs(exposed);
g
}
fn run(
graph: Graph,
inputs: &[(&str, &[f32])],
params: &[(String, Vec<f32>)],
skip_fusion: bool,
) -> Vec<Vec<f32>> {
let mut opts = CompileOptions::default();
opts.fusion_opts.skip_fusion = skip_fusion;
let mut c = Session::new(Device::Cpu).compile_with(graph, &opts);
for (n, d) in params {
c.set_param(n, d);
}
c.run(inputs)
}
#[derive(Clone, Debug)]
pub struct ParityReport {
pub ops_checked: usize,
pub max_abs: f32,
pub max_rel: f32,
pub worst_op: usize,
}
impl ParityReport {
pub fn exact(&self) -> bool {
self.max_abs == 0.0
}
pub fn within(&self, tol: f32) -> bool {
self.max_rel <= tol
}
}
fn compare(a: &[Vec<f32>], b: &[Vec<f32>]) -> ParityReport {
let n = a.len().min(b.len());
let (mut max_abs, mut max_rel, mut worst) = (0f32, 0f32, 0usize);
for i in 0..n {
if a[i].len() != b[i].len() {
continue;
}
for (x, y) in a[i].iter().zip(&b[i]) {
let ae = (x - y).abs();
if ae > max_abs {
max_abs = ae;
worst = i;
}
max_rel = max_rel.max(ae / x.abs().max(1e-6));
}
}
ParityReport {
ops_checked: n,
max_abs,
max_rel,
worst_op: worst,
}
}
pub fn op_level_parity(
original: &Graph,
transformed: &Graph,
inputs: &[(&str, &[f32])],
params: &[(String, Vec<f32>)],
) -> ParityReport {
let a = run(expose_all_ops(original), inputs, params, true);
let b = run(expose_all_ops(transformed), inputs, params, true);
compare(&a, &b)
}
pub fn fusion_output_parity(
graph: &Graph,
inputs: &[(&str, &[f32])],
params: &[(String, Vec<f32>)],
) -> ParityReport {
let fused = run(graph.clone(), inputs, params, false);
let unfused = run(graph.clone(), inputs, params, true);
compare(
&fused[..1.min(fused.len())],
&unfused[..1.min(unfused.len())],
)
}