use anyhow::Result;
use std::collections::{HashMap, VecDeque};
use super::types::{
ConvergenceStatus, LayerActivationStats, ModelPerformanceMetrics, TrainingDynamics,
};
#[derive(Debug)]
pub struct AutoDebugger {
config: AutoDebugConfig,
performance_history: VecDeque<ModelPerformanceMetrics>,
layer_history: HashMap<String, VecDeque<LayerActivationStats>>,
dynamics_history: VecDeque<TrainingDynamics>,
#[allow(dead_code)]
issue_patterns: IssuePatternDatabase,
session_state: DebuggingSession,
}
#[derive(Debug, Clone)]
pub struct AutoDebugConfig {
pub max_history_size: usize,
pub min_samples_for_analysis: usize,
pub recommendation_confidence_threshold: f64,
pub enable_advanced_patterns: bool,
pub enable_hyperparameter_suggestions: bool,
pub enable_architectural_recommendations: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DebuggingSession {
pub session_start: chrono::DateTime<chrono::Utc>,
pub identified_issues: Vec<IdentifiedIssue>,
pub recommendations: Vec<DebuggingRecommendation>,
pub session_stats: SessionStatistics,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct IdentifiedIssue {
pub category: IssueCategory,
pub description: String,
pub severity: IssueSeverity,
pub confidence: f64,
pub evidence: Vec<String>,
pub potential_causes: Vec<String>,
pub identified_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum IssueCategory {
LearningRate,
GradientFlow,
Overfitting,
Underfitting,
DataQuality,
Architecture,
Memory,
Convergence,
NumericalStability,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
pub enum IssueSeverity {
Minor,
Moderate,
Major,
Critical,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DebuggingRecommendation {
pub category: RecommendationCategory,
pub title: String,
pub description: String,
pub actions: Vec<String>,
pub expected_impact: String,
pub confidence: f64,
pub priority: AutoDebugRecommendationPriority,
pub hyperparameter_suggestions: Vec<HyperparameterSuggestion>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum RecommendationCategory {
HyperparameterTuning,
ArchitecturalModification,
DataPreprocessing,
TrainingStrategy,
DebuggingAndMonitoring,
ResourceOptimization,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)]
pub enum AutoDebugRecommendationPriority {
Low,
Medium,
High,
Urgent,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct HyperparameterSuggestion {
pub parameter_name: String,
pub current_value: Option<f64>,
pub suggested_value: f64,
pub reasoning: String,
pub expected_effect: String,
}
#[derive(Debug, Clone)]
pub struct IssuePatternDatabase {
pub learning_rate_patterns: Vec<IssuePattern>,
pub gradient_patterns: Vec<IssuePattern>,
pub convergence_patterns: Vec<IssuePattern>,
pub layer_patterns: Vec<IssuePattern>,
}
#[derive(Debug, Clone)]
pub struct IssuePattern {
pub name: String,
pub description: String,
pub conditions: Vec<PatternCondition>,
pub issue_category: IssueCategory,
pub confidence_weight: f64,
pub solutions: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct PatternCondition {
pub metric: String,
pub operator: ComparisonOperator,
pub threshold: f64,
pub consecutive_count: usize,
}
#[derive(Debug, Clone)]
pub enum ComparisonOperator {
GreaterThan,
LessThan,
EqualTo,
Increasing,
Decreasing,
Oscillating,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SessionStatistics {
pub total_issues: usize,
pub issues_by_category: HashMap<IssueCategory, usize>,
pub total_recommendations: usize,
pub avg_recommendation_confidence: f64,
pub analysis_duration: chrono::Duration,
}
impl Default for AutoDebugConfig {
fn default() -> Self {
Self {
max_history_size: 1000,
min_samples_for_analysis: 10,
recommendation_confidence_threshold: 0.7,
enable_advanced_patterns: true,
enable_hyperparameter_suggestions: true,
enable_architectural_recommendations: true,
}
}
}
impl AutoDebugger {
pub fn new() -> Self {
Self {
config: AutoDebugConfig::default(),
performance_history: VecDeque::new(),
layer_history: HashMap::new(),
dynamics_history: VecDeque::new(),
issue_patterns: IssuePatternDatabase::new(),
session_state: DebuggingSession::new(),
}
}
pub fn with_config(config: AutoDebugConfig) -> Self {
Self {
config,
performance_history: VecDeque::new(),
layer_history: HashMap::new(),
dynamics_history: VecDeque::new(),
issue_patterns: IssuePatternDatabase::new(),
session_state: DebuggingSession::new(),
}
}
pub fn record_performance_metrics(&mut self, metrics: ModelPerformanceMetrics) {
self.performance_history.push_back(metrics);
while self.performance_history.len() > self.config.max_history_size {
self.performance_history.pop_front();
}
}
pub fn record_layer_stats(&mut self, stats: LayerActivationStats) {
let layer_name = stats.layer_name.clone();
let layer_history = self.layer_history.entry(layer_name).or_default();
layer_history.push_back(stats);
while layer_history.len() > self.config.max_history_size {
layer_history.pop_front();
}
}
pub fn record_training_dynamics(&mut self, dynamics: TrainingDynamics) {
self.dynamics_history.push_back(dynamics);
while self.dynamics_history.len() > self.config.max_history_size {
self.dynamics_history.pop_front();
}
}
pub fn perform_analysis(&mut self) -> Result<DebuggingReport> {
let analysis_start = chrono::Utc::now();
if self.performance_history.len() < self.config.min_samples_for_analysis {
return Err(anyhow::anyhow!("Insufficient data for analysis"));
}
self.session_state = DebuggingSession::new();
self.analyze_learning_rate_issues()?;
self.analyze_convergence_issues()?;
self.analyze_gradient_flow_issues()?;
self.analyze_layer_health_issues()?;
self.analyze_memory_issues()?;
self.analyze_overfitting_underfitting()?;
self.generate_recommendations()?;
self.session_state.session_stats.analysis_duration = chrono::Utc::now() - analysis_start;
self.update_session_statistics();
Ok(DebuggingReport {
session_info: self.session_state.clone(),
identified_issues: self.session_state.identified_issues.clone(),
recommendations: self.session_state.recommendations.clone(),
summary: self.generate_analysis_summary(),
})
}
fn analyze_learning_rate_issues(&mut self) -> Result<()> {
let recent_metrics: Vec<_> = self.performance_history.iter().rev().take(20).collect();
if recent_metrics.len() < 10 {
return Ok(());
}
let mut issues_to_add = Vec::new();
let recent_losses: Vec<f64> = recent_metrics.iter().map(|m| m.loss).collect();
if let Some(max_loss) = recent_losses
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
{
if let Some(min_loss) = recent_losses
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
{
if max_loss / min_loss > 10.0 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::LearningRate,
description: "Learning rate too high - loss explosion detected".to_string(),
severity: IssueSeverity::Critical,
confidence: 0.9,
evidence: vec![
format!("Loss ratio: {:.2}", max_loss / min_loss),
"Rapid loss increase observed".to_string(),
],
potential_causes: vec![
"Learning rate set too high".to_string(),
"Gradient clipping disabled".to_string(),
"Numerical instability".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
}
let loss_variance = self.calculate_variance(&recent_losses);
let recent_metrics_len = recent_metrics.len();
if loss_variance < 1e-6 && recent_metrics_len >= 15 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::LearningRate,
description: "Learning rate too low - training stagnation".to_string(),
severity: IssueSeverity::Major,
confidence: 0.8,
evidence: vec![
format!("Loss variance: {:.2e}", loss_variance),
"No learning progress in recent steps".to_string(),
],
potential_causes: vec![
"Learning rate set too low".to_string(),
"Learning rate decay too aggressive".to_string(),
"Model has converged".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
for issue in issues_to_add {
self.add_issue(issue);
}
Ok(())
}
fn analyze_convergence_issues(&mut self) -> Result<()> {
if let Some(latest_dynamics) = self.dynamics_history.back() {
match latest_dynamics.convergence_status {
ConvergenceStatus::Diverging => {
self.add_issue(IdentifiedIssue {
category: IssueCategory::Convergence,
description: "Training is diverging".to_string(),
severity: IssueSeverity::Critical,
confidence: 0.95,
evidence: vec!["Convergence status: Diverging".to_string()],
potential_causes: vec![
"Learning rate too high".to_string(),
"Gradient explosion".to_string(),
"Numerical instability".to_string(),
],
identified_at: chrono::Utc::now(),
});
},
ConvergenceStatus::Plateau => {
if let Some(plateau_info) = &latest_dynamics.plateau_detection {
if plateau_info.duration_steps > 100 {
self.add_issue(IdentifiedIssue {
category: IssueCategory::Convergence,
description: "Training has plateaued".to_string(),
severity: IssueSeverity::Moderate,
confidence: 0.8,
evidence: vec![
format!(
"Plateau duration: {} steps",
plateau_info.duration_steps
),
format!("Plateau value: {:.4}", plateau_info.plateau_value),
],
potential_causes: vec![
"Learning rate too low".to_string(),
"Model capacity insufficient".to_string(),
"Local minimum reached".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
},
ConvergenceStatus::Oscillating => {
self.add_issue(IdentifiedIssue {
category: IssueCategory::NumericalStability,
description: "Training is oscillating".to_string(),
severity: IssueSeverity::Moderate,
confidence: 0.7,
evidence: vec!["Convergence status: Oscillating".to_string()],
potential_causes: vec![
"Learning rate too high".to_string(),
"Batch size too small".to_string(),
"Momentum settings suboptimal".to_string(),
],
identified_at: chrono::Utc::now(),
});
},
_ => {},
}
}
Ok(())
}
fn analyze_gradient_flow_issues(&mut self) -> Result<()> {
let mut issues_to_add = Vec::new();
for (layer_name, layer_history) in &self.layer_history {
if let Some(latest_stats) = layer_history.back() {
if latest_stats.dead_neurons_ratio > 0.5 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::GradientFlow,
description: format!("High dead neuron ratio in layer {}", layer_name),
severity: IssueSeverity::Major,
confidence: 0.85,
evidence: vec![
format!(
"Dead neurons: {:.1}%",
latest_stats.dead_neurons_ratio * 100.0
),
format!("Layer: {}", layer_name),
],
potential_causes: vec![
"Dying ReLU problem".to_string(),
"Poor weight initialization".to_string(),
"Learning rate too high".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
if latest_stats.saturated_neurons_ratio > 0.3 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::GradientFlow,
description: format!("High activation saturation in layer {}", layer_name),
severity: IssueSeverity::Moderate,
confidence: 0.8,
evidence: vec![
format!(
"Saturated neurons: {:.1}%",
latest_stats.saturated_neurons_ratio * 100.0
),
format!("Layer: {}", layer_name),
],
potential_causes: vec![
"Vanishing gradient problem".to_string(),
"Poor activation function choice".to_string(),
"Input normalization issues".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
}
for issue in issues_to_add {
self.add_issue(issue);
}
Ok(())
}
fn analyze_layer_health_issues(&mut self) -> Result<()> {
let mut issues_to_add = Vec::new();
for (layer_name, layer_history) in &self.layer_history {
if layer_history.len() >= 5 {
let recent_stats: Vec<_> = layer_history.iter().rev().take(5).collect();
let variances: Vec<f64> = recent_stats.iter().map(|s| s.std_activation).collect();
let avg_variance = variances.iter().sum::<f64>() / variances.len() as f64;
if avg_variance < 0.01 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::Architecture,
description: format!("Low activation variance in layer {}", layer_name),
severity: IssueSeverity::Minor,
confidence: 0.6,
evidence: vec![
format!("Average variance: {:.4}", avg_variance),
format!("Layer: {}", layer_name),
],
potential_causes: vec![
"Poor weight initialization".to_string(),
"Input normalization too aggressive".to_string(),
"Activation function saturation".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
}
for issue in issues_to_add {
self.add_issue(issue);
}
Ok(())
}
fn analyze_memory_issues(&mut self) -> Result<()> {
if self.performance_history.len() >= 10 {
let recent_memory: Vec<f64> = self
.performance_history
.iter()
.rev()
.take(10)
.map(|m| m.memory_usage_mb)
.collect();
let memory_trend = self.calculate_trend(&recent_memory);
if memory_trend > 10.0 {
self.add_issue(IdentifiedIssue {
category: IssueCategory::Memory,
description: "Memory leak detected".to_string(),
severity: IssueSeverity::Critical,
confidence: 0.9,
evidence: vec![
format!("Memory growth rate: {:.2} MB/step", memory_trend),
"Increasing memory usage trend".to_string(),
],
potential_causes: vec![
"Gradient accumulation without clearing".to_string(),
"Cached tensors not being released".to_string(),
"Memory fragmentation".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
if let Some(max_memory) = recent_memory
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
{
if *max_memory > 16384.0 {
self.add_issue(IdentifiedIssue {
category: IssueCategory::Memory,
description: "Excessive memory usage detected".to_string(),
severity: IssueSeverity::Major,
confidence: 0.8,
evidence: vec![
format!("Peak memory: {:.0} MB", max_memory),
"High memory consumption".to_string(),
],
potential_causes: vec![
"Batch size too large".to_string(),
"Model too large for available memory".to_string(),
"Inefficient memory allocation".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
}
Ok(())
}
fn analyze_overfitting_underfitting(&mut self) -> Result<()> {
let mut issues_to_add = Vec::new();
if let Some(latest_dynamics) = self.dynamics_history.back() {
for indicator in &latest_dynamics.overfitting_indicators {
if let super::types::OverfittingIndicator::TrainValidationGap { gap } = indicator {
if *gap > 0.1 {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::Overfitting,
description: "Large training-validation gap detected".to_string(),
severity: IssueSeverity::Major,
confidence: 0.85,
evidence: vec![
format!("Train-validation gap: {:.3}", gap),
"Overfitting indicator present".to_string(),
],
potential_causes: vec![
"Model complexity too high".to_string(),
"Insufficient regularization".to_string(),
"Training set too small".to_string(),
],
identified_at: chrono::Utc::now(),
});
}
}
}
for indicator in &latest_dynamics.underfitting_indicators {
match indicator {
super::types::UnderfittingIndicator::HighTrainingLoss { loss, threshold } => {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::Underfitting,
description: "High training loss indicates underfitting".to_string(),
severity: IssueSeverity::Moderate,
confidence: 0.7,
evidence: vec![
format!("Training loss: {:.3}", loss),
format!("Threshold: {:.3}", threshold),
],
potential_causes: vec![
"Model capacity too low".to_string(),
"Learning rate too low".to_string(),
"Insufficient training time".to_string(),
],
identified_at: chrono::Utc::now(),
});
},
super::types::UnderfittingIndicator::SlowConvergence {
steps_taken,
expected,
} => {
issues_to_add.push(IdentifiedIssue {
category: IssueCategory::Underfitting,
description: "Slow convergence detected".to_string(),
severity: IssueSeverity::Minor,
confidence: 0.6,
evidence: vec![
format!("Steps taken: {}", steps_taken),
format!("Expected: {}", expected),
],
potential_causes: vec![
"Learning rate too conservative".to_string(),
"Optimizer choice suboptimal".to_string(),
"Poor initialization".to_string(),
],
identified_at: chrono::Utc::now(),
});
},
_ => {},
}
}
}
for issue in issues_to_add {
self.add_issue(issue);
}
Ok(())
}
fn generate_recommendations(&mut self) -> Result<()> {
for issue in &self.session_state.identified_issues {
let recommendations = self.generate_recommendations_for_issue(issue);
self.session_state.recommendations.extend(recommendations);
}
self.session_state.recommendations.sort_by(|a, b| {
b.priority
.partial_cmp(&a.priority)
.unwrap_or(std::cmp::Ordering::Equal)
.then(b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal))
});
Ok(())
}
fn generate_recommendations_for_issue(
&self,
issue: &IdentifiedIssue,
) -> Vec<DebuggingRecommendation> {
match issue.category {
IssueCategory::LearningRate => {
if issue.description.contains("too high") {
vec![DebuggingRecommendation {
category: RecommendationCategory::HyperparameterTuning,
title: "Reduce Learning Rate".to_string(),
description: "Lower the learning rate to stabilize training".to_string(),
actions: vec![
"Reduce learning rate by factor of 2-10".to_string(),
"Enable gradient clipping".to_string(),
"Consider learning rate scheduling".to_string(),
],
expected_impact: "Stabilized training with reduced loss oscillations"
.to_string(),
confidence: 0.9,
priority: AutoDebugRecommendationPriority::High,
hyperparameter_suggestions: vec![HyperparameterSuggestion {
parameter_name: "learning_rate".to_string(),
current_value: None,
suggested_value: 0.0001,
reasoning: "Reduce to prevent loss explosion".to_string(),
expected_effect: "More stable training".to_string(),
}],
}]
} else if issue.description.contains("too low") {
vec![DebuggingRecommendation {
category: RecommendationCategory::HyperparameterTuning,
title: "Increase Learning Rate".to_string(),
description: "Increase learning rate to improve convergence speed"
.to_string(),
actions: vec![
"Increase learning rate by factor of 2-5".to_string(),
"Use learning rate warmup".to_string(),
"Consider adaptive learning rate methods".to_string(),
],
expected_impact: "Faster convergence and better final performance"
.to_string(),
confidence: 0.8,
priority: AutoDebugRecommendationPriority::Medium,
hyperparameter_suggestions: vec![HyperparameterSuggestion {
parameter_name: "learning_rate".to_string(),
current_value: None,
suggested_value: 0.001,
reasoning: "Increase to improve learning speed".to_string(),
expected_effect: "Faster convergence".to_string(),
}],
}]
} else {
Vec::new()
}
},
IssueCategory::Memory => {
vec![DebuggingRecommendation {
category: RecommendationCategory::ResourceOptimization,
title: "Optimize Memory Usage".to_string(),
description: "Implement memory optimization strategies".to_string(),
actions: vec![
"Reduce batch size".to_string(),
"Enable gradient checkpointing".to_string(),
"Clear cached tensors regularly".to_string(),
"Use mixed precision training".to_string(),
],
expected_impact: "Reduced memory consumption and stable training".to_string(),
confidence: 0.85,
priority: AutoDebugRecommendationPriority::High,
hyperparameter_suggestions: vec![HyperparameterSuggestion {
parameter_name: "batch_size".to_string(),
current_value: None,
suggested_value: 16.0,
reasoning: "Reduce to lower memory usage".to_string(),
expected_effect: "Lower memory consumption".to_string(),
}],
}]
},
IssueCategory::Overfitting => {
vec![DebuggingRecommendation {
category: RecommendationCategory::TrainingStrategy,
title: "Address Overfitting".to_string(),
description: "Implement regularization strategies to reduce overfitting"
.to_string(),
actions: vec![
"Add dropout layers".to_string(),
"Increase weight decay".to_string(),
"Use data augmentation".to_string(),
"Reduce model complexity".to_string(),
"Implement early stopping".to_string(),
],
expected_impact: "Better generalization and validation performance".to_string(),
confidence: 0.8,
priority: AutoDebugRecommendationPriority::Medium,
hyperparameter_suggestions: vec![HyperparameterSuggestion {
parameter_name: "dropout_rate".to_string(),
current_value: None,
suggested_value: 0.1,
reasoning: "Add regularization to reduce overfitting".to_string(),
expected_effect: "Better generalization".to_string(),
}],
}]
},
IssueCategory::GradientFlow => {
vec![DebuggingRecommendation {
category: RecommendationCategory::ArchitecturalModification,
title: "Improve Gradient Flow".to_string(),
description: "Address gradient flow issues in the network".to_string(),
actions: vec![
"Use different activation functions (e.g., Leaky ReLU, Swish)".to_string(),
"Add batch normalization".to_string(),
"Implement residual connections".to_string(),
"Adjust weight initialization".to_string(),
],
expected_impact: "Better gradient flow and training stability".to_string(),
confidence: 0.75,
priority: AutoDebugRecommendationPriority::Medium,
hyperparameter_suggestions: Vec::new(),
}]
},
_ => Vec::new(),
}
}
fn add_issue(&mut self, issue: IdentifiedIssue) {
self.session_state.identified_issues.push(issue);
}
fn calculate_variance(&self, values: &[f64]) -> f64 {
if values.len() < 2 {
return 0.0;
}
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance =
values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (values.len() - 1) as f64;
variance
}
fn calculate_trend(&self, values: &[f64]) -> f64 {
if values.len() < 2 {
return 0.0;
}
let n = values.len() as f64;
let x_mean = (n - 1.0) / 2.0;
let y_mean = values.iter().sum::<f64>() / n;
let numerator: f64 = values
.iter()
.enumerate()
.map(|(i, &y)| (i as f64 - x_mean) * (y - y_mean))
.sum();
let denominator: f64 = (0..values.len()).map(|i| (i as f64 - x_mean).powi(2)).sum();
if denominator == 0.0 {
0.0
} else {
numerator / denominator
}
}
fn update_session_statistics(&mut self) {
let mut issues_by_category = HashMap::new();
for issue in &self.session_state.identified_issues {
*issues_by_category.entry(issue.category.clone()).or_insert(0) += 1;
}
let avg_confidence = if self.session_state.recommendations.is_empty() {
0.0
} else {
self.session_state.recommendations.iter().map(|r| r.confidence).sum::<f64>()
/ self.session_state.recommendations.len() as f64
};
self.session_state.session_stats = SessionStatistics {
total_issues: self.session_state.identified_issues.len(),
issues_by_category,
total_recommendations: self.session_state.recommendations.len(),
avg_recommendation_confidence: avg_confidence,
analysis_duration: self.session_state.session_stats.analysis_duration,
};
}
fn generate_analysis_summary(&self) -> String {
let critical_issues = self
.session_state
.identified_issues
.iter()
.filter(|i| i.severity == IssueSeverity::Critical)
.count();
let major_issues = self
.session_state
.identified_issues
.iter()
.filter(|i| i.severity == IssueSeverity::Major)
.count();
let high_priority_recommendations = self
.session_state
.recommendations
.iter()
.filter(|r| r.priority == AutoDebugRecommendationPriority::High)
.count();
format!(
"Auto-debugging analysis completed. Found {} critical issues, {} major issues. \
Generated {} recommendations with {} high-priority actions. \
Average recommendation confidence: {:.2}",
critical_issues,
major_issues,
self.session_state.recommendations.len(),
high_priority_recommendations,
self.session_state.session_stats.avg_recommendation_confidence
)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DebuggingReport {
pub session_info: DebuggingSession,
pub identified_issues: Vec<IdentifiedIssue>,
pub recommendations: Vec<DebuggingRecommendation>,
pub summary: String,
}
impl IssuePatternDatabase {
pub fn new() -> Self {
Self {
learning_rate_patterns: Self::create_learning_rate_patterns(),
gradient_patterns: Self::create_gradient_patterns(),
convergence_patterns: Self::create_convergence_patterns(),
layer_patterns: Self::create_layer_patterns(),
}
}
fn create_learning_rate_patterns() -> Vec<IssuePattern> {
vec![IssuePattern {
name: "Loss Explosion".to_string(),
description: "Rapid increase in loss indicating learning rate too high".to_string(),
conditions: vec![PatternCondition {
metric: "loss".to_string(),
operator: ComparisonOperator::Increasing,
threshold: 2.0,
consecutive_count: 3,
}],
issue_category: IssueCategory::LearningRate,
confidence_weight: 0.9,
solutions: vec![
"Reduce learning rate by factor of 10".to_string(),
"Enable gradient clipping".to_string(),
],
}]
}
fn create_gradient_patterns() -> Vec<IssuePattern> {
vec![]
}
fn create_convergence_patterns() -> Vec<IssuePattern> {
vec![]
}
fn create_layer_patterns() -> Vec<IssuePattern> {
vec![]
}
}
impl DebuggingSession {
fn new() -> Self {
Self {
session_start: chrono::Utc::now(),
identified_issues: Vec::new(),
recommendations: Vec::new(),
session_stats: SessionStatistics {
total_issues: 0,
issues_by_category: HashMap::new(),
total_recommendations: 0,
avg_recommendation_confidence: 0.0,
analysis_duration: chrono::Duration::zero(),
},
}
}
}
impl Default for AutoDebugger {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auto_debugger_creation() {
let debugger = AutoDebugger::new();
assert_eq!(debugger.performance_history.len(), 0);
assert_eq!(debugger.layer_history.len(), 0);
}
#[test]
fn test_issue_identification() {
let mut debugger = AutoDebugger::new();
let issue = IdentifiedIssue {
category: IssueCategory::LearningRate,
description: "Test issue".to_string(),
severity: IssueSeverity::Major,
confidence: 0.8,
evidence: vec!["Test evidence".to_string()],
potential_causes: vec!["Test cause".to_string()],
identified_at: chrono::Utc::now(),
};
debugger.add_issue(issue);
assert_eq!(debugger.session_state.identified_issues.len(), 1);
}
#[test]
fn test_variance_calculation() {
let debugger = AutoDebugger::new();
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let variance = debugger.calculate_variance(&values);
assert!(variance > 0.0);
}
#[test]
fn test_trend_calculation() {
let debugger = AutoDebugger::new();
let increasing_values = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let trend = debugger.calculate_trend(&increasing_values);
assert!(trend > 0.0);
}
}