neuro_divergent_training/
lib.rs1use num_traits::Float;
34use std::collections::HashMap;
35use thiserror::Error;
36
37pub use ruv_fann::{Network, NetworkError};
39
40pub mod loss;
42pub mod optimizer;
43pub mod scheduler;
44pub mod metrics;
45
46pub use loss::*;
48pub use optimizer::*;
49pub use scheduler::*;
50pub use metrics::*;
51
52#[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#[derive(Debug, Clone)]
96pub struct TrainingData<T: Float + Send + Sync> {
97 pub inputs: Vec<Vec<Vec<T>>>,
99 pub targets: Vec<Vec<Vec<T>>>,
101 pub exogenous: Option<Vec<Vec<Vec<T>>>>,
103 pub static_features: Option<Vec<Vec<T>>>,
105 pub metadata: Vec<TimeSeriesMetadata>,
107}
108
109#[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#[derive(Debug, Clone)]
120pub struct TrainingConfig<T: Float + Send + Sync> {
121 pub max_epochs: usize,
123 pub batch_size: usize,
125 pub validation_frequency: usize,
127 pub patience: Option<usize>,
129 pub gradient_clip: Option<T>,
131 pub mixed_precision: bool,
133 pub seed: Option<u64>,
135 pub device: DeviceConfig,
137 pub checkpoint: CheckpointConfig,
139}
140
141#[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#[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#[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#[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
187pub 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 pub fn new() -> Self {
197 Self {
198 ruv_fann_trainer: None,
199 loss_adapter: None,
200 config: None,
201 }
202 }
203
204 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 pub fn with_loss_adapter(mut self, adapter: LossAdapter<T>) -> Self {
215 self.loss_adapter = Some(adapter);
216 self
217 }
218
219 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
232pub 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
267pub mod utils {
269 use super::*;
270
271 pub fn set_seed(seed: u64) {
273 use rand::{Rng, SeedableRng};
274 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
275 }
277
278 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 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); let new_norm = utils::gradient_norm(&gradients);
338 assert!((new_norm - 5.0).abs() < 1e-6);
339 }
340}