Files
rustytorch/demos/rtx-neural-operator-demo/examples/benchmark_solvers.rs
T
2026-03-04 00:08:42 +00:00

208 lines
6.8 KiB
Rust

//! PDE Solver Benchmark Demo
//!
//! This example demonstrates the benchmark system comparing:
//! - FNO (Fourier Neural Operator) - learned PDE solver
//! - FDM (Finite Difference Method) - classical numerical method
//! - FEM (Finite Element Method) - classical numerical method
//!
//! Usage:
//! ```bash
//! cargo run --package rtx-neural-operator-demo --example benchmark_solvers
//! cargo run --package rtx-neural-operator-demo --example benchmark_solvers -- --quick
//! cargo run --package rtx-neural-operator-demo --example benchmark_solvers -- --comprehensive
//! ```
use rtx_neural_operator_demo::{BenchmarkConfig, BenchmarkRunner};
use std::env;
fn main() {
println!("\n{:=<80}", "");
println!("Neural Operator PDE Benchmark Demo");
println!("Comparing FNO, FDM, and FEM solvers");
println!("{:=<80}\n", "");
// Parse command line arguments
let args: Vec<String> = env::args().collect();
let config = if args.contains(&"--quick".to_string()) {
println!("Running quick benchmark (small resolutions, few trials)...\n");
BenchmarkConfig::quick()
} else if args.contains(&"--comprehensive".to_string()) {
println!("Running comprehensive benchmark (all resolutions, many trials)...\n");
BenchmarkConfig::comprehensive()
} else {
println!("Running standard benchmark...");
println!("Use --quick for faster testing or --comprehensive for full analysis\n");
BenchmarkConfig::default()
};
// Display configuration
println!("Configuration:");
println!(" PDE Type: {}", config.pde_type.name());
println!(" Resolutions: {:?}", config.resolutions);
println!(" Problems per resolution: {}", config.n_problems);
println!(" Reference resolution: {}", config.reference_resolution);
println!();
// Run benchmarks
println!("Running benchmarks...\n");
let runner = BenchmarkRunner::new(config);
let results = runner.run_classical_benchmarks();
// Display results
BenchmarkRunner::print_results(&results);
// Compute and display summary statistics
println!("\nRunning 3 trials for statistical analysis...\n");
let summaries = runner.run_with_statistics(3);
BenchmarkRunner::print_summary(&summaries);
// Analyze speedup between methods
print_speedup_analysis(&summaries);
// Show accuracy comparison
print_accuracy_analysis(&results);
// Memory efficiency analysis
print_memory_analysis(&results);
println!("\n{:=<80}", "");
println!("Benchmark Complete!");
println!("{:=<80}\n", "");
println!("Note: To benchmark FNO, train a model first and use the");
println!(" benchmark_fno() method with a trained model.");
}
fn print_speedup_analysis(summaries: &[rtx_neural_operator_demo::BenchmarkSummary]) {
println!("\n{:=<80}", "");
println!("Speedup Analysis");
println!("{:=<80}\n", "");
// Group by resolution
let mut by_resolution: std::collections::HashMap<
usize,
Vec<&rtx_neural_operator_demo::BenchmarkSummary>,
> = std::collections::HashMap::new();
for summary in summaries {
by_resolution
.entry(summary.resolution)
.or_default()
.push(summary);
}
for (resolution, res_summaries) in by_resolution.iter() {
println!("Resolution: {}x{}", resolution, resolution);
println!("{:-<40}", "");
let fdm_summary = res_summaries.iter().find(|s| s.method.contains("FDM"));
let fem_summary = res_summaries.iter().find(|s| s.method.contains("FEM"));
if let (Some(fdm), Some(fem)) = (fdm_summary, fem_summary) {
let speedup = fem.speedup_vs(fdm);
if speedup > 1.0 {
println!(
" FDM is {:.2}x faster than FEM ({:.2}ms vs {:.2}ms)",
speedup, fdm.avg_time_ms, fem.avg_time_ms
);
} else {
println!(
" FEM is {:.2}x faster than FDM ({:.2}ms vs {:.2}ms)",
1.0 / speedup,
fem.avg_time_ms,
fdm.avg_time_ms
);
}
}
println!();
}
}
fn print_accuracy_analysis(results: &[rtx_neural_operator_demo::BenchmarkResult]) {
println!("\n{:=<80}", "");
println!("Accuracy Analysis (L2 Error vs Analytical Solution)");
println!("{:=<80}\n", "");
// Group by resolution
let mut by_resolution: std::collections::HashMap<
usize,
Vec<&rtx_neural_operator_demo::BenchmarkResult>,
> = std::collections::HashMap::new();
for result in results {
by_resolution
.entry(result.resolution)
.or_default()
.push(result);
}
for (resolution, res_results) in by_resolution.iter() {
println!("Resolution: {}x{}", resolution, resolution);
println!("{:-<40}", "");
for result in res_results {
if let Some(l2_error) = result.l2_error {
println!(" {:<12} L2 error: {:.6e}", result.method, l2_error);
}
}
println!();
}
println!("Note: Lower L2 error indicates better accuracy.");
println!(" Both FDM and FEM converge to the exact solution as resolution increases.");
}
fn print_memory_analysis(results: &[rtx_neural_operator_demo::BenchmarkResult]) {
println!("\n{:=<80}", "");
println!("Memory Efficiency Analysis");
println!("{:=<80}\n", "");
// Group by method
let mut by_method: std::collections::HashMap<
String,
Vec<&rtx_neural_operator_demo::BenchmarkResult>,
> = std::collections::HashMap::new();
for result in results {
by_method
.entry(result.method.clone())
.or_default()
.push(result);
}
for (method, method_results) in by_method.iter() {
println!("Method: {}", method);
println!("{:-<40}", "");
let mut sorted_results = method_results.to_vec();
sorted_results.sort_by_key(|r| r.resolution);
for result in &sorted_results {
println!(
" {}x{}: {:.2} MB",
result.resolution, result.resolution, result.memory_mb
);
}
// Compute scaling factor
if sorted_results.len() >= 2 {
let first = sorted_results[0];
let last = sorted_results[sorted_results.len() - 1];
let res_ratio = (last.resolution as f64 / first.resolution as f64).powi(2);
let mem_ratio = last.memory_mb / first.memory_mb;
println!(
" Scaling: {:.2}x memory for {:.1}x grid points (O(N²) expected)",
mem_ratio, res_ratio
);
}
println!();
}
println!("Note: Memory should scale as O(N²) for 2D problems.");
println!(" FEM uses more memory than FDM due to CG solver vectors.");
}