use anyhow::Result;
use std::collections::HashMap;
use std::env;
use trustformers_debug::{
ActivationVisualizer, AttentionType, AttentionVisualizer, ExportFormat, GraphVisualizer,
NetronExporter, StabilityChecker, TensorBoardWriter,
};
fn main() -> Result<()> {
println!("=== TrustformeRS Debug Visualization Demo ===\n");
let temp_dir = env::temp_dir().join("trustformers_debug_demo");
std::fs::create_dir_all(&temp_dir)?;
println!("1. TensorBoard Integration");
println!("--------------------------");
let tensorboard_dir = temp_dir.join("tensorboard_logs");
let mut writer = TensorBoardWriter::new(&tensorboard_dir)?;
for step in 0..100 {
let loss = 1.0 - (step as f64 / 100.0);
let accuracy = step as f64 / 100.0;
writer.add_scalar("loss/train", loss, step)?;
writer.add_scalar("accuracy/train", accuracy, step)?;
}
let weights: Vec<f64> = (0..1000).map(|i| (i as f64 / 1000.0) * 2.0 - 1.0).collect();
writer.add_histogram("layer.0.weight", &weights, 50)?;
writer.add_text(
"experiment/description",
"Testing new visualization features",
0,
)?;
writer.flush()?;
println!("✓ TensorBoard logs written to: {:?}", tensorboard_dir);
println!(
" Run: tensorboard --logdir={}\n",
tensorboard_dir.display()
);
println!("2. Netron/ONNX Export");
println!("---------------------");
let mut exporter = NetronExporter::new("bert-tiny", "Tiny BERT model for demonstration")
.with_format(ExportFormat::Json);
exporter.set_author("TrustformeRS");
exporter.set_version("0.1.0");
exporter.add_property("framework", "TrustformeRS");
exporter.add_input("input_ids", "int64", vec![1, 128]);
exporter.add_input("attention_mask", "int64", vec![1, 128]);
exporter.add_output("logits", "float32", vec![1, 128, 30522]);
let mut attrs = HashMap::new();
attrs.insert(
"vocab_size".to_string(),
trustformers_debug::AttributeValue::Int(30522),
);
attrs.insert(
"hidden_size".to_string(),
trustformers_debug::AttributeValue::Int(768),
);
exporter.add_node(
"embeddings",
"Embedding",
vec!["input_ids".to_string()],
vec!["hidden_states".to_string()],
attrs.clone(),
);
attrs.clear();
attrs.insert(
"num_heads".to_string(),
trustformers_debug::AttributeValue::Int(12),
);
attrs.insert(
"head_dim".to_string(),
trustformers_debug::AttributeValue::Int(64),
);
exporter.add_node(
"layer.0.attention",
"MultiHeadAttention",
vec!["hidden_states".to_string()],
vec!["attention_output".to_string()],
attrs,
);
let netron_path = temp_dir.join("model.json");
exporter.export(&netron_path)?;
println!("✓ Model exported to: {:?}", netron_path);
println!(" View at: https://netron.app/\n");
println!("3. Activation Visualization");
println!("---------------------------");
let mut act_viz = ActivationVisualizer::new();
let layer1_activations: Vec<f32> = (0..1000).map(|i| (i as f32 / 1000.0) * 2.0 - 1.0).collect();
act_viz.register("layer1.relu", layer1_activations, vec![1, 1000])?;
let layer2_activations: Vec<f32> = (0..1000)
.map(|i| {
let x = i as f32 / 1000.0;
(x * std::f32::consts::TAU).sin()
})
.collect();
act_viz.register("layer2.gelu", layer2_activations, vec![1, 1000])?;
println!("{}", act_viz.print_summary()?);
let histogram = act_viz.plot_distribution_ascii("layer1.relu")?;
println!("{}", histogram);
let stats_path = temp_dir.join("activation_stats.json");
act_viz.export_statistics("layer1.relu", &stats_path)?;
println!("✓ Activation statistics exported to: {:?}\n", stats_path);
println!("4. Attention Visualization");
println!("--------------------------");
let mut att_viz = AttentionVisualizer::new();
let tokens = ["The", "cat", "sat", "on", "the", "mat"];
let num_tokens = tokens.len();
let mut weights = Vec::new();
let mut head0 = Vec::new();
for i in 0..num_tokens {
let mut row = vec![0.1; num_tokens];
row[i] = 0.6; head0.push(row);
}
weights.push(head0);
let mut head1 = Vec::new();
for _i in 0..num_tokens {
let mut row = vec![0.15; num_tokens];
row[0] = 0.3; row[num_tokens - 1] = 0.3; head1.push(row);
}
weights.push(head1);
let token_strings: Vec<String> = tokens.iter().map(|&s| s.to_string()).collect();
att_viz.register(
"layer.0.attention",
weights,
token_strings.clone(),
token_strings,
AttentionType::SelfAttention,
)?;
println!("{}", att_viz.summary());
let analysis = att_viz.analyze("layer.0.attention")?;
println!("Entropy per head: {:?}", analysis.entropy_per_head);
println!("Sparsity per head: {:?}", analysis.sparsity_per_head);
let heatmap = att_viz.plot_heatmap_ascii("layer.0.attention", 0)?;
println!("\n{}", heatmap);
let bertviz_path = temp_dir.join("attention.html");
att_viz.export_to_bertviz("layer.0.attention", &bertviz_path)?;
println!("✓ BertViz HTML exported to: {:?}\n", bertviz_path);
println!("5. Numerical Stability Checking");
println!("--------------------------------");
let mut checker = StabilityChecker::new();
let normal_values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
checker.check_tensor("layer1.output", &normal_values)?;
println!("✓ Normal values: No issues detected");
let problematic_values = vec![
1.0,
f64::NAN,
2.0,
f64::INFINITY,
1e-20,
1e20,
f64::NEG_INFINITY,
];
checker.check_tensor("layer2.output", &problematic_values)?;
println!("\n{}", checker.report());
let issues_path = temp_dir.join("stability_issues.json");
checker.export_to_json(&issues_path)?;
println!("✓ Stability issues exported to: {:?}\n", issues_path);
println!("6. Computation Graph Visualization");
println!("-----------------------------------");
let mut graph_viz = GraphVisualizer::new("simple_transformer");
graph_viz.add_node(
"input",
"Input",
"Input",
Some(vec![1, 128]),
Some("int64".to_string()),
HashMap::new(),
);
graph_viz.add_node(
"embeddings",
"Embeddings",
"Embedding",
Some(vec![1, 128, 768]),
Some("float32".to_string()),
HashMap::new(),
);
let mut attn_attrs = HashMap::new();
attn_attrs.insert("num_heads".to_string(), "12".to_string());
graph_viz.add_node(
"attention",
"Self-Attention",
"Attention",
Some(vec![1, 128, 768]),
Some("float32".to_string()),
attn_attrs,
);
graph_viz.add_node(
"ffn",
"Feed Forward",
"Linear",
Some(vec![1, 128, 768]),
Some("float32".to_string()),
HashMap::new(),
);
graph_viz.add_node(
"output",
"Output",
"Output",
Some(vec![1, 128, 30522]),
Some("float32".to_string()),
HashMap::new(),
);
graph_viz.add_edge("input", "embeddings", None, Some(vec![1, 128]));
graph_viz.add_edge("embeddings", "attention", None, Some(vec![1, 128, 768]));
graph_viz.add_edge("attention", "ffn", None, Some(vec![1, 128, 768]));
graph_viz.add_edge("ffn", "output", None, Some(vec![1, 128, 768]));
graph_viz.mark_input("input");
graph_viz.mark_output("output");
println!("{}", graph_viz.summary());
let dot_path = temp_dir.join("graph.dot");
graph_viz.export_to_dot(&dot_path)?;
println!("✓ Graph exported to: {:?}", dot_path);
println!(
" Convert to PNG: dot -Tpng {} -o graph.png",
dot_path.display()
);
let graph_json_path = temp_dir.join("graph.json");
graph_viz.export_to_json(&graph_json_path)?;
println!("✓ Graph JSON exported to: {:?}\n", graph_json_path);
println!("\n=== Demo Complete ===");
println!("\nAll artifacts saved to: {:?}", temp_dir);
println!("\nNext steps:");
println!(
"1. View TensorBoard: tensorboard --logdir={}",
tensorboard_dir.display()
);
println!(
"2. View Netron export: Open {} at https://netron.app/",
netron_path.display()
);
println!(
"3. View attention patterns: Open {} in a browser",
bertviz_path.display()
);
println!(
"4. Convert graph to image: dot -Tpng {} -o graph.png",
dot_path.display()
);
Ok(())
}