Files
rustytorch/crates/specialized/rtx-fea/src/utils/mod.rs
T
2026-03-04 00:08:42 +00:00

413 lines
11 KiB
Rust

// Copyright (c) 2024 RustyTorch++ Team
// Licensed under the Apache License, Version 2.0
//! Utility functions and helpers for finite element analysis.
pub mod io;
pub mod math;
pub mod profiling;
pub mod visualization;
#[cfg(test)]
mod math_tests;
use nalgebra::Vector3;
use std::time::{Duration, Instant};
pub use io::*;
pub use math::*;
pub use profiling::*;
pub use visualization::*;
/// Coordinate system transformations.
pub struct CoordinateTransforms;
impl CoordinateTransforms {
/// Convert Cartesian to cylindrical coordinates.
pub fn cartesian_to_cylindrical(point: &Vector3<f64>) -> Vector3<f64> {
let r = (point.x * point.x + point.y * point.y).sqrt();
let theta = point.y.atan2(point.x);
let z = point.z;
Vector3::new(r, theta, z)
}
/// Convert cylindrical to Cartesian coordinates.
pub fn cylindrical_to_cartesian(point: &Vector3<f64>) -> Vector3<f64> {
let x = point.x * point.y.cos();
let y = point.x * point.y.sin();
let z = point.z;
Vector3::new(x, y, z)
}
/// Convert Cartesian to spherical coordinates.
pub fn cartesian_to_spherical(point: &Vector3<f64>) -> Vector3<f64> {
let r = point.norm();
let theta = (point.z / r).acos();
let phi = point.y.atan2(point.x);
Vector3::new(r, theta, phi)
}
/// Convert spherical to Cartesian coordinates.
pub fn spherical_to_cartesian(point: &Vector3<f64>) -> Vector3<f64> {
let x = point.x * point.y.sin() * point.z.cos();
let y = point.x * point.y.sin() * point.z.sin();
let z = point.x * point.y.cos();
Vector3::new(x, y, z)
}
}
/// Memory usage utilities.
pub struct MemoryUtils;
impl MemoryUtils {
/// Get current memory usage in bytes.
pub fn current_usage() -> usize {
// Simplified implementation - would use proper memory tracking
0
}
/// Get peak memory usage in bytes.
pub fn peak_usage() -> usize {
// Simplified implementation
0
}
/// Format memory size in human-readable format.
pub fn format_bytes(bytes: usize) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
let mut size = bytes as f64;
let mut unit_index = 0;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
}
format!("{:.2} {}", size, UNITS[unit_index])
}
/// Estimate memory requirement for sparse matrix.
pub fn estimate_sparse_matrix_memory(
nnz: usize,
index_size: usize,
value_size: usize,
) -> usize {
nnz * (index_size + value_size) + index_size * 2 // Row pointers
}
/// Estimate memory requirement for dense matrix.
pub fn estimate_dense_matrix_memory(rows: usize, cols: usize, element_size: usize) -> usize {
rows * cols * element_size
}
}
/// Performance benchmarking utilities.
pub struct BenchmarkUtils;
impl BenchmarkUtils {
/// Measure execution time of a function.
pub fn time_function<F, R>(f: F) -> (R, Duration)
where
F: FnOnce() -> R,
{
let start = Instant::now();
let result = f();
let duration = start.elapsed();
(result, duration)
}
/// Run benchmark multiple times and get statistics.
pub fn benchmark_function<F>(f: F, iterations: usize) -> BenchmarkStats
where
F: Fn(),
{
let mut times = Vec::with_capacity(iterations);
for _ in 0..iterations {
let ((), duration) = Self::time_function(&f);
times.push(duration);
}
BenchmarkStats::from_times(times)
}
/// Create a simple progress bar.
pub fn create_progress_bar(total: usize) -> ProgressBar {
ProgressBar::new(total)
}
}
/// Benchmark statistics.
#[derive(Debug, Clone)]
pub struct BenchmarkStats {
pub mean: Duration,
pub std_dev: Duration,
pub min: Duration,
pub max: Duration,
pub iterations: usize,
}
impl BenchmarkStats {
/// Create statistics from timing measurements.
pub fn from_times(times: Vec<Duration>) -> Self {
if times.is_empty() {
return Self {
mean: Duration::new(0, 0),
std_dev: Duration::new(0, 0),
min: Duration::new(0, 0),
max: Duration::new(0, 0),
iterations: 0,
};
}
let iterations = times.len();
let total_nanos: u64 = times.iter().map(|d| d.as_nanos() as u64).sum();
let mean_nanos = total_nanos / iterations as u64;
let mean = Duration::from_nanos(mean_nanos);
let min = times.iter().min().copied().unwrap_or_default();
let max = times.iter().max().copied().unwrap_or_default();
// Calculate standard deviation
let variance: f64 = times
.iter()
.map(|d| {
let diff = d.as_nanos() as f64 - mean_nanos as f64;
diff * diff
})
.sum::<f64>()
/ iterations as f64;
let std_dev = Duration::from_nanos(variance.sqrt() as u64);
Self {
mean,
std_dev,
min,
max,
iterations,
}
}
}
impl std::fmt::Display for BenchmarkStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Benchmark Statistics ({} iterations):", self.iterations)?;
writeln!(f, " Mean: {:.3} ms", self.mean.as_secs_f64() * 1000.0)?;
writeln!(
f,
" Std Dev: {:.3} ms",
self.std_dev.as_secs_f64() * 1000.0
)?;
writeln!(f, " Min: {:.3} ms", self.min.as_secs_f64() * 1000.0)?;
writeln!(f, " Max: {:.3} ms", self.max.as_secs_f64() * 1000.0)?;
Ok(())
}
}
/// Simple progress bar for console output.
#[derive(Debug)]
pub struct ProgressBar {
total: usize,
current: usize,
width: usize,
start_time: Instant,
}
impl ProgressBar {
/// Create a new progress bar.
pub fn new(total: usize) -> Self {
Self {
total,
current: 0,
width: 50,
start_time: Instant::now(),
}
}
/// Set the width of the progress bar.
pub fn set_width(&mut self, width: usize) {
self.width = width;
}
/// Update progress and display.
pub fn update(&mut self, current: usize) {
self.current = current;
self.display();
}
/// Increment progress by one.
pub fn increment(&mut self) {
self.current += 1;
self.display();
}
/// Display the progress bar.
fn display(&self) {
let percentage = if self.total > 0 {
(self.current as f64 / self.total as f64 * 100.0).min(100.0)
} else {
0.0
};
let filled = (percentage / 100.0 * self.width as f64) as usize;
let empty = self.width - filled;
let elapsed = self.start_time.elapsed();
let rate = if elapsed.as_secs() > 0 {
self.current as f64 / elapsed.as_secs_f64()
} else {
0.0
};
let eta = if rate > 0.0 && self.current < self.total {
Duration::from_secs_f64((self.total - self.current) as f64 / rate)
} else {
Duration::new(0, 0)
};
print!(
"\r[{}{}] {:.1}% ({}/{}) Rate: {:.1}/s ETA: {:.0}s",
"█".repeat(filled),
"░".repeat(empty),
percentage,
self.current,
self.total,
rate,
eta.as_secs_f64()
);
use std::io::{self, Write};
io::stdout().flush().unwrap();
if self.current >= self.total {
println!(); // New line when complete
}
}
/// Mark as complete.
pub fn finish(&mut self) {
self.current = self.total;
self.display();
}
}
/// String utilities for FEA.
pub struct StringUtils;
impl StringUtils {
/// Convert scientific notation to readable format.
pub fn format_scientific(value: f64, precision: usize) -> String {
if value.abs() < 1e-15 {
"0".to_string()
} else if value.abs() >= 1e6 || value.abs() < 1e-3 {
format!("{value:.precision$e}")
} else {
format!("{value:.precision$}")
}
}
/// Format duration in human-readable format.
pub fn format_duration(duration: Duration) -> String {
let total_secs = duration.as_secs_f64();
if total_secs < 1.0 {
format!("{:.1} ms", total_secs * 1000.0)
} else if total_secs < 60.0 {
format!("{total_secs:.2} s")
} else if total_secs < 3600.0 {
let minutes = (total_secs / 60.0) as u32;
let seconds = total_secs % 60.0;
format!("{minutes}m {seconds:.1}s")
} else {
let hours = (total_secs / 3600.0) as u32;
let minutes = ((total_secs % 3600.0) / 60.0) as u32;
format!("{hours}h {minutes}m")
}
}
/// Generate unique identifier.
pub fn generate_id() -> String {
use std::time::{SystemTime, UNIX_EPOCH};
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
format!("fea_{timestamp}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coordinate_transforms() {
let cartesian = Vector3::new(1.0, 1.0, 1.0);
let cylindrical = CoordinateTransforms::cartesian_to_cylindrical(&cartesian);
let back_to_cartesian = CoordinateTransforms::cylindrical_to_cartesian(&cylindrical);
assert!((cartesian - back_to_cartesian).norm() < 1e-10);
}
#[test]
fn test_memory_utils() {
let formatted = MemoryUtils::format_bytes(1024 * 1024);
assert_eq!(formatted, "1.00 MB");
let formatted = MemoryUtils::format_bytes(1536);
assert_eq!(formatted, "1.50 KB");
}
#[test]
fn test_benchmark_stats() {
let times = vec![
Duration::from_millis(10),
Duration::from_millis(12),
Duration::from_millis(11),
Duration::from_millis(13),
Duration::from_millis(9),
];
let stats = BenchmarkStats::from_times(times);
assert_eq!(stats.iterations, 5);
assert!(stats.mean.as_millis() > 0);
assert!(stats.min <= stats.mean);
assert!(stats.max >= stats.mean);
}
#[test]
fn test_progress_bar() {
let mut progress = ProgressBar::new(100);
progress.set_width(10);
progress.update(50);
assert_eq!(progress.current, 50);
assert_eq!(progress.total, 100);
}
#[test]
fn test_string_utils() {
let scientific = StringUtils::format_scientific(1.23e-6, 2);
assert!(scientific.contains("e"));
let duration = StringUtils::format_duration(Duration::from_secs(65));
assert!(duration.contains("m"));
let id = StringUtils::generate_id();
assert!(id.starts_with("fea_"));
}
#[test]
fn test_benchmark_function() {
let stats = BenchmarkUtils::benchmark_function(
|| {
std::thread::sleep(Duration::from_millis(1));
},
3,
);
assert_eq!(stats.iterations, 3);
assert!(stats.mean.as_millis() >= 1);
}
}