neuro_divergent_training/
lib.rs

1//! # Neuro-Divergent Training Infrastructure
2//!
3//! Comprehensive training system for neural forecasting models with advanced optimization,
4//! loss functions, and training strategies specifically designed for time series forecasting.
5//!
6//! ## Features
7//!
8//! - **Advanced Loss Functions**: Specialized forecasting losses (MAPE, SMAPE, MASE, CRPS, etc.)
9//! - **Modern Optimizers**: Adam, AdamW, SGD, RMSprop with forecasting optimizations
10//! - **Learning Rate Schedulers**: Exponential, step, cosine, plateau schedulers
11//! - **Unified Training Loop**: Batch processing, gradient clipping, mixed precision
12//! - **Validation Framework**: Cross-validation and model evaluation for time series
13//! - **Training Callbacks**: Early stopping, checkpointing, progress tracking
14//! - **ruv-FANN Integration**: Seamless integration with ruv-FANN neural networks
15//!
16//! ## Example Usage
17//!
18//! ```rust
19//! use neuro_divergent_training::*;
20//! use ruv_fann::Network;
21//!
22//! // Create a trainer with Adam optimizer and MAPE loss
23//! let mut trainer = TrainerBuilder::new()
24//!     .optimizer(AdamOptimizer::new(0.001, 0.9, 0.999))
25//!     .loss_function(MAPELoss::new())
26//!     .scheduler(ExponentialScheduler::new(0.001, 0.95))
27//!     .build();
28//!
29//! // Train the model
30//! let result = trainer.train(&mut network, &training_data, &config)?;
31//! ```
32
33use num_traits::Float;
34use std::collections::HashMap;
35use thiserror::Error;
36
37// Re-export ruv-FANN types for convenience
38pub use ruv_fann::{Network, NetworkError};
39
40// Core training modules
41pub mod loss;
42pub mod optimizer;
43pub mod scheduler;
44pub mod metrics;
45
46// Re-export main types for convenience
47pub use loss::*;
48pub use optimizer::*;
49pub use scheduler::*;
50pub use metrics::*;
51
52/// Error types for training operations
53#[derive(Error, Debug)]
54pub enum TrainingError {
55    #[error("Invalid training configuration: {0}")]
56    InvalidConfig(String),
57    
58    #[error("Training data error: {0}")]
59    DataError(String),
60    
61    #[error("Optimizer error: {0}")]
62    OptimizerError(String),
63    
64    #[error("Loss function error: {0}")]
65    LossError(String),
66    
67    #[error("Scheduler error: {0}")]
68    SchedulerError(String),
69    
70    #[error("Validation error: {0}")]
71    ValidationError(String),
72    
73    #[error("Callback error: {0}")]
74    CallbackError(String),
75    
76    #[error("Network integration error: {0}")]
77    NetworkError(#[from] NetworkError),
78    
79    #[error("I/O error: {0}")]
80    IoError(#[from] std::io::Error),
81    
82    #[error("Serialization error: {0}")]
83    SerializationError(String),
84    
85    #[error("Memory error: {0}")]
86    MemoryError(String),
87    
88    #[error("Training failed: {0}")]
89    TrainingFailed(String),
90}
91
92pub type TrainingResult<T> = Result<T, TrainingError>;
93
94/// Core training data structure for time series
95#[derive(Debug, Clone)]
96pub struct TrainingData<T: Float + Send + Sync> {
97    /// Input sequences [batch_size, sequence_length, input_features]
98    pub inputs: Vec<Vec<Vec<T>>>,
99    /// Target outputs [batch_size, horizon, output_features]
100    pub targets: Vec<Vec<Vec<T>>>,
101    /// Optional exogenous features [batch_size, total_length, exog_features]
102    pub exogenous: Option<Vec<Vec<Vec<T>>>>,
103    /// Static features per time series [batch_size, static_features]
104    pub static_features: Option<Vec<Vec<T>>>,
105    /// Metadata for each time series
106    pub metadata: Vec<TimeSeriesMetadata>,
107}
108
109/// Metadata for individual time series
110#[derive(Debug, Clone)]
111pub struct TimeSeriesMetadata {
112    pub id: String,
113    pub frequency: String,
114    pub seasonal_periods: Vec<usize>,
115    pub scale: Option<f64>,
116}
117
118/// Training configuration
119#[derive(Debug, Clone)]
120pub struct TrainingConfig<T: Float + Send + Sync> {
121    /// Maximum number of training epochs
122    pub max_epochs: usize,
123    /// Batch size for training
124    pub batch_size: usize,
125    /// Validation frequency (every N epochs)
126    pub validation_frequency: usize,
127    /// Early stopping patience
128    pub patience: Option<usize>,
129    /// Gradient clipping threshold
130    pub gradient_clip: Option<T>,
131    /// Enable mixed precision training
132    pub mixed_precision: bool,
133    /// Random seed for reproducibility
134    pub seed: Option<u64>,
135    /// Device configuration
136    pub device: DeviceConfig,
137    /// Checkpoint configuration
138    pub checkpoint: CheckpointConfig,
139}
140
141/// Device configuration for training
142#[derive(Debug, Clone)]
143pub enum DeviceConfig {
144    Cpu { num_threads: Option<usize> },
145    Gpu { device_id: usize },
146    Multi { devices: Vec<usize> },
147}
148
149/// Checkpoint configuration
150#[derive(Debug, Clone)]
151pub struct CheckpointConfig {
152    pub enabled: bool,
153    pub save_frequency: usize,
154    pub keep_best_only: bool,
155    pub monitor_metric: String,
156    pub mode: CheckpointMode,
157}
158
159#[derive(Debug, Clone)]
160pub enum CheckpointMode {
161    Min,
162    Max,
163}
164
165/// Training results and metrics
166#[derive(Debug, Clone)]
167pub struct TrainingResults<T: Float + Send + Sync> {
168    pub final_loss: T,
169    pub best_loss: T,
170    pub epochs_trained: usize,
171    pub training_history: Vec<EpochMetrics<T>>,
172    pub validation_history: Vec<EpochMetrics<T>>,
173    pub early_stopped: bool,
174    pub training_time: std::time::Duration,
175}
176
177/// Metrics for a single epoch
178#[derive(Debug, Clone)]
179pub struct EpochMetrics<T: Float + Send + Sync> {
180    pub epoch: usize,
181    pub loss: T,
182    pub learning_rate: T,
183    pub gradient_norm: Option<T>,
184    pub additional_metrics: HashMap<String, T>,
185}
186
187/// Bridge for integrating with ruv-FANN training algorithms
188pub struct TrainingBridge<T: Float + Send + Sync> {
189    pub(crate) ruv_fann_trainer: Option<Box<dyn ruv_fann::training::TrainingAlgorithm<T>>>,
190    pub(crate) loss_adapter: Option<LossAdapter<T>>,
191    pub(crate) config: Option<TrainingConfig<T>>,
192}
193
194impl<T: Float + Send + Sync> TrainingBridge<T> {
195    /// Create a new training bridge
196    pub fn new() -> Self {
197        Self {
198            ruv_fann_trainer: None,
199            loss_adapter: None,
200            config: None,
201        }
202    }
203    
204    /// Set the ruv-FANN training algorithm
205    pub fn with_ruv_fann_trainer(
206        mut self, 
207        trainer: Box<dyn ruv_fann::training::TrainingAlgorithm<T>>
208    ) -> Self {
209        self.ruv_fann_trainer = Some(trainer);
210        self
211    }
212    
213    /// Set the loss adapter
214    pub fn with_loss_adapter(mut self, adapter: LossAdapter<T>) -> Self {
215        self.loss_adapter = Some(adapter);
216        self
217    }
218    
219    /// Set the training configuration
220    pub fn with_config(mut self, config: TrainingConfig<T>) -> Self {
221        self.config = Some(config);
222        self
223    }
224}
225
226impl<T: Float + Send + Sync> Default for TrainingBridge<T> {
227    fn default() -> Self {
228        Self::new()
229    }
230}
231
232/// Adapter for integrating different loss functions with ruv-FANN
233pub struct LossAdapter<T: Float + Send + Sync> {
234    loss_function: Box<dyn LossFunction<T>>,
235}
236
237impl<T: Float + Send + Sync> LossAdapter<T> {
238    pub fn new(loss_function: Box<dyn LossFunction<T>>) -> Self {
239        Self { loss_function }
240    }
241    
242    pub fn calculate_loss(&self, predictions: &[T], targets: &[T]) -> TrainingResult<T> {
243        self.loss_function.forward(predictions, targets)
244    }
245    
246    pub fn calculate_gradient(&self, predictions: &[T], targets: &[T]) -> TrainingResult<Vec<T>> {
247        self.loss_function.backward(predictions, targets)
248    }
249}
250
251impl<T: Float + Send + Sync> ruv_fann::training::ErrorFunction<T> for LossAdapter<T> {
252    fn calculate(&self, actual: &[T], desired: &[T]) -> T {
253        self.calculate_loss(actual, desired).unwrap_or_else(|_| T::zero())
254    }
255    
256    fn derivative(&self, actual: T, desired: T) -> T {
257        let actual_slice = [actual];
258        let desired_slice = [desired];
259        self.calculate_gradient(&actual_slice, &desired_slice)
260            .unwrap_or_else(|_| vec![T::zero()])
261            .first()
262            .copied()
263            .unwrap_or_else(T::zero)
264    }
265}
266
267/// Utility functions
268pub mod utils {
269    use super::*;
270    
271    /// Initialize random seed for reproducibility
272    pub fn set_seed(seed: u64) {
273        use rand::{Rng, SeedableRng};
274        let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
275        // Set global seed for consistent behavior
276    }
277    
278    /// Calculate gradient norm for monitoring
279    pub fn gradient_norm<T: Float + Send + Sync>(gradients: &[Vec<T>]) -> T {
280        let sum_squares = gradients.iter()
281            .flat_map(|layer| layer.iter())
282            .map(|&g| g * g)
283            .fold(T::zero(), |acc, x| acc + x);
284        sum_squares.sqrt()
285    }
286    
287    /// Clip gradients by norm
288    pub fn clip_gradients_by_norm<T: Float + Send + Sync>(
289        gradients: &mut [Vec<T>], 
290        max_norm: T
291    ) -> T {
292        let norm = gradient_norm(gradients);
293        if norm > max_norm {
294            let scale = max_norm / norm;
295            for layer in gradients.iter_mut() {
296                for gradient in layer.iter_mut() {
297                    *gradient = *gradient * scale;
298                }
299            }
300        }
301        norm
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308    
309    #[test]
310    fn test_training_bridge_creation() {
311        let bridge = TrainingBridge::<f32>::new();
312        assert!(bridge.ruv_fann_trainer.is_none());
313        assert!(bridge.loss_adapter.is_none());
314        assert!(bridge.config.is_none());
315    }
316    
317    #[test]
318    fn test_gradient_norm_calculation() {
319        let gradients = vec![
320            vec![3.0, 4.0],
321            vec![0.0, 0.0],
322        ];
323        let norm = utils::gradient_norm(&gradients);
324        assert!((norm - 5.0).abs() < 1e-6);
325    }
326    
327    #[test]
328    fn test_gradient_clipping() {
329        let mut gradients = vec![
330            vec![3.0, 4.0],
331            vec![6.0, 8.0],
332        ];
333        let norm = utils::clip_gradients_by_norm(&mut gradients, 5.0);
334        assert!((norm - 12.806248).abs() < 1e-5); // Original norm
335        
336        // Check clipped values
337        let new_norm = utils::gradient_norm(&gradients);
338        assert!((new_norm - 5.0).abs() < 1e-6);
339    }
340}