Skip to main content

neuro_divergent_models/
errors.rs

1//! Error types for neuro-divergent models
2//!
3//! This module provides comprehensive error handling for all neural forecasting operations.
4
5use thiserror::Error;
6use ruv_fann::{NetworkError, TrainingError};
7
8/// Result type for neuro-divergent operations
9pub type NeuroDivergentResult<T> = Result<T, NeuroDivergentError>;
10
11/// Comprehensive error type for neuro-divergent models
12#[derive(Error, Debug)]
13pub enum NeuroDivergentError {
14    #[error("Model configuration error: {0}")]
15    ConfigError(String),
16    
17    #[error("Data validation error: {0}")]
18    DataError(String),
19    
20    #[error("Training error: {0}")]
21    TrainingError(String),
22    
23    #[error("Prediction error: {0}")]
24    PredictionError(String),
25    
26    #[error("Network error: {0}")]
27    NetworkError(#[from] NetworkError),
28    
29    #[error("Internal ruv-FANN training error: {0}")]
30    RuvFannTrainingError(#[from] TrainingError),
31    
32    #[error("Time series error: {0}")]
33    TimeSeriesError(String),
34    
35    #[error("Sequence processing error: {0}")]
36    SequenceError(String),
37    
38    #[error("State management error: {0}")]
39    StateError(String),
40    
41    #[error("Dimension mismatch: expected {expected}, got {actual}")]
42    DimensionMismatch { expected: usize, actual: usize },
43    
44    #[error("Invalid sequence length: {0}")]
45    InvalidSequenceLength(usize),
46    
47    #[error("Missing required feature: {0}")]
48    MissingFeature(String),
49    
50    #[error("I/O error: {0}")]
51    IoError(#[from] std::io::Error),
52    
53    #[error("Serialization error: {0}")]
54    SerializationError(String),
55    
56    #[cfg(feature = "polars")]
57    #[error("Polars error: {0}")]
58    PolarsError(#[from] polars::error::PolarsError),
59    
60    #[error("Custom error: {0}")]
61    Custom(String),
62}
63
64impl NeuroDivergentError {
65    /// Create a custom error with a message
66    pub fn custom<T: Into<String>>(message: T) -> Self {
67        Self::Custom(message.into())
68    }
69    
70    /// Create a configuration error
71    pub fn config<T: Into<String>>(message: T) -> Self {
72        Self::ConfigError(message.into())
73    }
74    
75    /// Create a data validation error
76    pub fn data<T: Into<String>>(message: T) -> Self {
77        Self::DataError(message.into())
78    }
79    
80    /// Create a training error
81    pub fn training<T: Into<String>>(message: T) -> Self {
82        Self::TrainingError(message.into())
83    }
84    
85    /// Create a prediction error
86    pub fn prediction<T: Into<String>>(message: T) -> Self {
87        Self::PredictionError(message.into())
88    }
89    
90    /// Create a time series error
91    pub fn time_series<T: Into<String>>(message: T) -> Self {
92        Self::TimeSeriesError(message.into())
93    }
94    
95    /// Create a sequence processing error
96    pub fn sequence<T: Into<String>>(message: T) -> Self {
97        Self::SequenceError(message.into())
98    }
99    
100    /// Create a state management error
101    pub fn state<T: Into<String>>(message: T) -> Self {
102        Self::StateError(message.into())
103    }
104    
105    /// Create a dimension mismatch error
106    pub fn dimension_mismatch(expected: usize, actual: usize) -> Self {
107        Self::DimensionMismatch { expected, actual }
108    }
109    
110    /// Create an invalid sequence length error
111    pub fn invalid_sequence_length(length: usize) -> Self {
112        Self::InvalidSequenceLength(length)
113    }
114    
115    /// Create a missing feature error
116    pub fn missing_feature<T: Into<String>>(feature: T) -> Self {
117        Self::MissingFeature(feature.into())
118    }
119    
120    /// Check if this is a recoverable error
121    pub fn is_recoverable(&self) -> bool {
122        match self {
123            Self::ConfigError(_) => false,
124            Self::DataError(_) => false,
125            Self::TrainingError(_) => true,
126            Self::PredictionError(_) => true,
127            Self::NetworkError(_) => false,
128            Self::RuvFannTrainingError(_) => true,
129            Self::TimeSeriesError(_) => false,
130            Self::SequenceError(_) => true,
131            Self::StateError(_) => true,
132            Self::DimensionMismatch { .. } => false,
133            Self::InvalidSequenceLength(_) => false,
134            Self::MissingFeature(_) => false,
135            Self::IoError(_) => false,
136            Self::SerializationError(_) => false,
137            #[cfg(feature = "polars")]
138            Self::PolarsError(_) => false,
139            Self::Custom(_) => true,
140        }
141    }
142    
143    /// Get error category for logging and monitoring
144    pub fn category(&self) -> ErrorCategory {
145        match self {
146            Self::ConfigError(_) => ErrorCategory::Configuration,
147            Self::DataError(_) => ErrorCategory::Data,
148            Self::TrainingError(_) => ErrorCategory::Training,
149            Self::PredictionError(_) => ErrorCategory::Prediction,
150            Self::NetworkError(_) => ErrorCategory::Network,
151            Self::RuvFannTrainingError(_) => ErrorCategory::Training,
152            Self::TimeSeriesError(_) => ErrorCategory::Data,
153            Self::SequenceError(_) => ErrorCategory::Processing,
154            Self::StateError(_) => ErrorCategory::State,
155            Self::DimensionMismatch { .. } => ErrorCategory::Validation,
156            Self::InvalidSequenceLength(_) => ErrorCategory::Validation,
157            Self::MissingFeature(_) => ErrorCategory::Configuration,
158            Self::IoError(_) => ErrorCategory::IO,
159            Self::SerializationError(_) => ErrorCategory::IO,
160            #[cfg(feature = "polars")]
161            Self::PolarsError(_) => ErrorCategory::Data,
162            Self::Custom(_) => ErrorCategory::Other,
163        }
164    }
165}
166
167/// Error categories for classification and handling
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub enum ErrorCategory {
170    Configuration,
171    Data,
172    Training,
173    Prediction,
174    Network,
175    Processing,
176    State,
177    Validation,
178    IO,
179    Other,
180}
181
182impl std::fmt::Display for ErrorCategory {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        match self {
185            Self::Configuration => write!(f, "Configuration"),
186            Self::Data => write!(f, "Data"),
187            Self::Training => write!(f, "Training"),
188            Self::Prediction => write!(f, "Prediction"),
189            Self::Network => write!(f, "Network"),
190            Self::Processing => write!(f, "Processing"),
191            Self::State => write!(f, "State"),
192            Self::Validation => write!(f, "Validation"),
193            Self::IO => write!(f, "IO"),
194            Self::Other => write!(f, "Other"),
195        }
196    }
197}
198
199/// Context for error reporting and debugging
200#[derive(Debug, Clone)]
201pub struct ErrorContext {
202    pub operation: String,
203    pub model_name: Option<String>,
204    pub epoch: Option<usize>,
205    pub batch: Option<usize>,
206    pub additional_info: std::collections::HashMap<String, String>,
207}
208
209impl ErrorContext {
210    pub fn new<T: Into<String>>(operation: T) -> Self {
211        Self {
212            operation: operation.into(),
213            model_name: None,
214            epoch: None,
215            batch: None,
216            additional_info: std::collections::HashMap::new(),
217        }
218    }
219    
220    pub fn with_model<T: Into<String>>(mut self, model_name: T) -> Self {
221        self.model_name = Some(model_name.into());
222        self
223    }
224    
225    pub fn with_epoch(mut self, epoch: usize) -> Self {
226        self.epoch = Some(epoch);
227        self
228    }
229    
230    pub fn with_batch(mut self, batch: usize) -> Self {
231        self.batch = Some(batch);
232        self
233    }
234    
235    pub fn with_info<K, V>(mut self, key: K, value: V) -> Self 
236    where 
237        K: Into<String>,
238        V: Into<String>,
239    {
240        self.additional_info.insert(key.into(), value.into());
241        self
242    }
243}
244
245/// Trait for adding context to errors
246pub trait ErrorContextExt<T> {
247    fn with_context(self, context: ErrorContext) -> NeuroDivergentResult<T>;
248}
249
250impl<T> ErrorContextExt<T> for NeuroDivergentResult<T> {
251    fn with_context(self, context: ErrorContext) -> NeuroDivergentResult<T> {
252        self.map_err(|e| {
253            let context_info = format!(
254                "Operation: {}, Model: {}, Epoch: {}, Batch: {}",
255                context.operation,
256                context.model_name.unwrap_or_else(|| "Unknown".to_string()),
257                context.epoch.map(|e| e.to_string()).unwrap_or_else(|| "N/A".to_string()),
258                context.batch.map(|b| b.to_string()).unwrap_or_else(|| "N/A".to_string())
259            );
260            
261            match e {
262                NeuroDivergentError::Custom(msg) => {
263                    NeuroDivergentError::custom(format!("{} | Context: {}", msg, context_info))
264                }
265                other => other,
266            }
267        })
268    }
269}