1use super::config::TransformerBasedOptimizerConfig;
4use super::meta_learning::MetaState;
5use crate::common::cast_scalar;
6use crate::error::{OptimError, Result};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::numeric::Float;
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, VecDeque};
11use std::fmt::Debug;
12use std::time::{Duration, Instant, SystemTime};
13
14pub struct TransformerOptimizerState<
16 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
17> {
18 pub current_parameters: Array1<T>,
20
21 parameter_history: ParameterHistory<T>,
23
24 optimization_state: OptimizationState<T>,
26
27 learning_state: LearningState<T>,
29
30 memory_state: MemoryState<T>,
32
33 checkpoint_manager: CheckpointManager<T>,
35
36 config: StateConfig,
38
39 statistics: StateStatistics<T>,
41
42 version: usize,
44
45 created_at: std::time::Instant,
47
48 last_updated: std::time::Instant,
50}
51
52impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
53 TransformerOptimizerState<T>
54{
55 pub fn new(config: &TransformerBasedOptimizerConfig<T>) -> Result<Self> {
57 let parameter_count = config.model_dimension * config.num_transformer_layers;
58 let current_parameters = Array1::zeros(parameter_count);
59
60 let parameter_history = ParameterHistory::new(1000, parameter_count)?;
61 let optimization_state = OptimizationState::new(config)?;
62 let learning_state = LearningState::new(config)?;
63 let memory_state = MemoryState::new()?;
64 let checkpoint_manager = CheckpointManager::new(config)?;
65 let state_config = StateConfig::from_optimizer_config(config);
66 let statistics = StateStatistics::new();
67
68 let now = std::time::Instant::now();
69
70 Ok(Self {
71 current_parameters,
72 parameter_history,
73 optimization_state,
74 learning_state,
75 memory_state,
76 checkpoint_manager,
77 config: state_config,
78 statistics,
79 version: 0,
80 created_at: now,
81 last_updated: now,
82 })
83 }
84
85 pub fn update_with_step(&mut self, update: &Array1<T>, loss: Option<T>) -> Result<()> {
94 let projected = Self::project_update(update, self.current_parameters.len())?;
95 let update = &projected;
96
97 self.current_parameters = &self.current_parameters + update;
99
100 self.parameter_history
102 .record_parameters(&self.current_parameters)?;
103
104 self.optimization_state.update_with_step(update, loss)?;
106
107 if let Some(loss_val) = loss {
109 self.learning_state.update_with_loss(loss_val)?;
110 }
111
112 self.statistics.record_update(update, loss);
114
115 self.version += 1;
117 self.last_updated = std::time::Instant::now();
118
119 Ok(())
120 }
121
122 fn project_update(update: &Array1<T>, target_len: usize) -> Result<Array1<T>> {
133 let update_len = update.len();
134 if update_len == target_len {
135 return Ok(update.clone());
136 }
137 if update_len == 0 || !target_len.is_multiple_of(update_len) {
138 return Err(OptimError::InvalidConfig(format!(
139 "optimizer update has {update_len} elements, which cannot be \
140 tiled to fill the {target_len}-element parameter vector \
141 (expected {update_len} to evenly divide {target_len})"
142 )));
143 }
144 let repeats = target_len / update_len;
145 let mut tiled = Vec::with_capacity(target_len);
146 for _ in 0..repeats {
147 tiled.extend(update.iter().copied());
148 }
149 Ok(Array1::from_vec(tiled))
150 }
151
152 pub fn create_snapshot(&self) -> Result<OptimizerStateSnapshot<T>> {
154 Ok(OptimizerStateSnapshot {
155 parameters: self.current_parameters.clone(),
156 optimization_state: self.optimization_state.clone(),
157 learning_state: self.learning_state.clone(),
158 memory_state: self.memory_state.clone(),
159 version: self.version,
160 timestamp: self.last_updated,
161 metadata: SnapshotMetadata {
162 parameter_count: self.current_parameters.len(),
163 total_updates: self.statistics.total_updates,
164 session_duration: self.last_updated.duration_since(self.created_at),
165 },
166 })
167 }
168
169 pub fn restore_from_snapshot(&mut self, snapshot: &OptimizerStateSnapshot<T>) -> Result<()> {
171 self.current_parameters = snapshot.parameters.clone();
172 self.optimization_state = snapshot.optimization_state.clone();
173 self.learning_state = snapshot.learning_state.clone();
174 self.memory_state = snapshot.memory_state.clone();
175 self.version = snapshot.version;
176 self.last_updated = snapshot.timestamp;
177
178 Ok(())
179 }
180
181 pub fn save_checkpoint(&mut self, name: String) -> Result<String> {
183 let snapshot = self.create_snapshot()?;
184 let checkpoint_id = self.checkpoint_manager.save_checkpoint(name, snapshot)?;
185 Ok(checkpoint_id)
186 }
187
188 pub fn load_checkpoint(&mut self, checkpoint_id: &str) -> Result<()> {
190 let snapshot = self.checkpoint_manager.load_checkpoint(checkpoint_id)?;
191 self.restore_from_snapshot(&snapshot)?;
192 Ok(())
193 }
194
195 pub fn get_parameter_stats(&self) -> ParameterStatistics<T> {
197 self.parameter_history.get_statistics()
198 }
199
200 pub fn get_optimization_progress(&self) -> OptimizationProgress<T> {
202 self.optimization_state.get_progress()
203 }
204
205 pub fn get_learning_stats(&self) -> LearningStatistics<T> {
207 self.learning_state.get_statistics()
208 }
209
210 pub fn reset(&mut self) -> Result<()> {
212 self.current_parameters.fill(T::zero());
213 self.parameter_history.clear();
214 self.optimization_state.reset()?;
215 self.learning_state.reset()?;
216 self.memory_state.reset()?;
217 self.statistics.reset();
218 self.version = 0;
219 self.last_updated = std::time::Instant::now();
220 Ok(())
221 }
222
223 pub fn validate_state(&self) -> Result<StateValidationReport> {
225 let mut issues = Vec::new();
226
227 if self.current_parameters.iter().any(|&x| !x.is_finite()) {
229 issues.push("Invalid parameters detected (NaN or infinity)".to_string());
230 }
231
232 if self.version == 0 && self.statistics.total_updates > 0 {
234 issues.push("Version mismatch with update count".to_string());
235 }
236
237 if self.last_updated < self.created_at {
239 issues.push("Invalid timestamp ordering".to_string());
240 }
241
242 let opt_validation = self.optimization_state.validate()?;
244 issues.extend(opt_validation.issues);
245
246 let learning_validation = self.learning_state.validate()?;
248 issues.extend(learning_validation.issues);
249
250 Ok(StateValidationReport {
251 is_valid: issues.is_empty(),
252 issues,
253 validation_timestamp: std::time::Instant::now(),
254 })
255 }
256
257 pub fn get_state_summary(&self) -> StateSummary<T> {
259 StateSummary {
260 version: self.version,
261 parameter_count: self.current_parameters.len(),
262 parameter_norm: self.compute_parameter_norm(),
263 total_updates: self.statistics.total_updates,
264 session_duration: self.last_updated.duration_since(self.created_at),
265 last_update_magnitude: self.statistics.last_update_magnitude,
266 average_loss: self.learning_state.get_average_loss(),
267 convergence_rate: self.learning_state.get_convergence_rate(),
268 memory_usage: self.memory_state.get_total_usage(),
269 checkpoint_count: self.checkpoint_manager.get_checkpoint_count(),
270 }
271 }
272
273 fn compute_parameter_norm(&self) -> T {
275 self.current_parameters
276 .iter()
277 .map(|&x| x * x)
278 .fold(T::zero(), |acc, x| acc + x)
279 .sqrt()
280 }
281
282 pub fn get_metadata(&self) -> StateMetadata {
284 StateMetadata {
285 version: self.version,
286 created_at: SystemTime::now(), last_updated: SystemTime::now(), total_updates: self.statistics.total_updates,
289 configuration: self.config.clone(),
290 }
291 }
292
293 pub fn export_state(&self) -> Result<SerializableState<T>> {
295 Ok(SerializableState {
296 parameters: self.current_parameters.to_vec(),
297 parameter_shape: self.current_parameters.shape().to_vec(),
298 optimization_state: self.optimization_state.to_serializable()?,
299 learning_state: self.learning_state.to_serializable()?,
300 metadata: self.get_metadata(),
301 statistics: self.statistics.clone(),
302 })
303 }
304
305 pub fn import_state(&mut self, state: SerializableState<T>) -> Result<()> {
307 if state.parameter_shape.len() != 1 {
309 return Err(crate::error::OptimError::Other(
310 "Invalid parameter shape for 1D array".to_string(),
311 ));
312 }
313
314 self.current_parameters = Array1::from_vec(state.parameters);
315
316 self.optimization_state
318 .from_serializable(state.optimization_state)?;
319 self.learning_state
320 .from_serializable(state.learning_state)?;
321 self.statistics = state.statistics;
322 self.version = state.metadata.version;
323 self.last_updated = Instant::now(); Ok(())
326 }
327}
328
329pub struct ParameterHistory<
331 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
332> {
333 snapshots: VecDeque<ParameterSnapshot<T>>,
335
336 max_size: usize,
338
339 parameter_dimension: usize,
341
342 statistics: ParameterStatistics<T>,
344}
345
346impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
347 ParameterHistory<T>
348{
349 pub fn new(max_size: usize, parameter_dimension: usize) -> Result<Self> {
350 Ok(Self {
351 snapshots: VecDeque::new(),
352 max_size,
353 parameter_dimension,
354 statistics: ParameterStatistics::new(),
355 })
356 }
357
358 pub fn record_parameters(&mut self, parameters: &Array1<T>) -> Result<()> {
366 if parameters.len() != self.parameter_dimension {
367 return Err(crate::error::OptimError::InvalidConfig(format!(
368 "ParameterHistory holds {}-dimensional snapshots but was given {}",
369 self.parameter_dimension,
370 parameters.len()
371 )));
372 }
373 let snapshot = ParameterSnapshot {
374 parameters: parameters.clone(),
375 timestamp: std::time::Instant::now(),
376 norm: parameters
377 .iter()
378 .map(|&x| x * x)
379 .fold(T::zero(), |acc, x| acc + x)
380 .sqrt(),
381 };
382
383 self.snapshots.push_back(snapshot.clone());
384 if self.snapshots.len() > self.max_size {
385 self.snapshots.pop_front();
386 }
387
388 self.statistics.update_with_snapshot(&snapshot);
389 Ok(())
390 }
391
392 pub fn get_recent_parameters(&self, count: usize) -> Vec<Array1<T>> {
393 self.snapshots
394 .iter()
395 .rev()
396 .take(count)
397 .map(|snapshot| snapshot.parameters.clone())
398 .collect()
399 }
400
401 pub fn get_statistics(&self) -> ParameterStatistics<T> {
402 self.statistics.clone()
403 }
404
405 pub fn clear(&mut self) {
406 self.snapshots.clear();
407 self.statistics = ParameterStatistics::new();
408 }
409}
410
411#[derive(Debug, Clone)]
413pub struct OptimizationState<
414 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
415> {
416 pub learning_rate: T,
418
419 pub momentum: Option<Array1<T>>,
421
422 pub adaptive_state: Option<AdaptiveState<T>>,
424
425 pub gradient_accumulator: GradientAccumulator<T>,
427
428 pub step_count: usize,
430
431 pub last_update_magnitude: T,
433
434 pub convergence_tracker: ConvergenceTracker<T>,
436}
437
438impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
439 OptimizationState<T>
440{
441 pub fn new(config: &TransformerBasedOptimizerConfig<T>) -> Result<Self> {
442 let parameter_count = config.model_dimension * config.num_transformer_layers;
443
444 Ok(Self {
445 learning_rate: config.learning_rate,
446 momentum: None,
447 adaptive_state: Some(AdaptiveState::new(parameter_count)?),
448 gradient_accumulator: GradientAccumulator::new(parameter_count)?,
449 step_count: 0,
450 last_update_magnitude: T::zero(),
451 convergence_tracker: ConvergenceTracker::new(),
452 })
453 }
454
455 pub fn update_with_step(&mut self, update: &Array1<T>, loss: Option<T>) -> Result<()> {
456 self.step_count += 1;
457 self.last_update_magnitude = update
458 .iter()
459 .map(|&x| x * x)
460 .fold(T::zero(), |acc, x| acc + x)
461 .sqrt();
462
463 if let Some(loss_val) = loss {
464 self.convergence_tracker.record_loss(loss_val);
465 }
466
467 if let Some(ref mut adaptive) = self.adaptive_state {
469 adaptive.update_with_step(update)?;
470 }
471
472 Ok(())
473 }
474
475 pub fn get_progress(&self) -> OptimizationProgress<T> {
476 OptimizationProgress {
477 step_count: self.step_count,
478 current_learning_rate: self.learning_rate,
479 last_update_magnitude: self.last_update_magnitude,
480 convergence_rate: self.convergence_tracker.get_convergence_rate(),
481 stability_score: self.convergence_tracker.get_stability_score(),
482 }
483 }
484
485 pub fn reset(&mut self) -> Result<()> {
486 self.step_count = 0;
487 self.last_update_magnitude = T::zero();
488 self.convergence_tracker.reset();
489
490 if let Some(ref mut adaptive) = self.adaptive_state {
491 adaptive.reset()?;
492 }
493
494 self.gradient_accumulator.reset()?;
495 Ok(())
496 }
497
498 pub fn validate(&self) -> Result<ValidationResult> {
499 let mut issues = Vec::new();
500
501 if self.learning_rate <= T::zero() {
502 issues.push("Invalid learning rate".to_string());
503 }
504
505 if !self.last_update_magnitude.is_finite() {
506 issues.push("Invalid update magnitude".to_string());
507 }
508
509 Ok(ValidationResult { issues })
510 }
511
512 pub fn to_serializable(&self) -> Result<SerializableOptimizationState<T>> {
513 Ok(SerializableOptimizationState {
514 learning_rate: self.learning_rate,
515 step_count: self.step_count,
516 last_update_magnitude: self.last_update_magnitude,
517 momentum: self.momentum.as_ref().map(|m| m.to_vec()),
518 convergence_metrics: self.convergence_tracker.to_serializable(),
519 })
520 }
521
522 pub fn from_serializable(&mut self, state: SerializableOptimizationState<T>) -> Result<()> {
523 self.learning_rate = state.learning_rate;
524 self.step_count = state.step_count;
525 self.last_update_magnitude = state.last_update_magnitude;
526
527 if let Some(momentum_vec) = state.momentum {
528 self.momentum = Some(Array1::from_vec(momentum_vec));
529 }
530
531 self.convergence_tracker
532 .from_serializable(state.convergence_metrics)?;
533 Ok(())
534 }
535}
536
537#[derive(Debug, Clone)]
539pub struct LearningState<
540 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
541> {
542 loss_history: VecDeque<T>,
544
545 meta_state: Option<MetaState<T>>,
547
548 adaptation_history: VecDeque<TaskAdaptationRecord<T>>,
550
551 learning_schedule: LearningSchedule<T>,
553
554 performance_metrics: LearningPerformanceMetrics<T>,
556}
557
558impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
559 LearningState<T>
560{
561 pub fn new(config: &TransformerBasedOptimizerConfig<T>) -> Result<Self> {
562 let meta_state = Some(MetaState::new(config.model_dimension)?);
563 let learning_schedule = LearningSchedule::new(config.learning_rate, config.warmup_steps);
564
565 Ok(Self {
566 loss_history: VecDeque::new(),
567 meta_state,
568 adaptation_history: VecDeque::new(),
569 learning_schedule,
570 performance_metrics: LearningPerformanceMetrics::new(),
571 })
572 }
573
574 pub fn update_with_loss(&mut self, loss: T) -> Result<()> {
575 self.loss_history.push_back(loss);
576 if self.loss_history.len() > 1000 {
577 self.loss_history.pop_front();
578 }
579
580 self.performance_metrics.record_loss(loss);
581 self.learning_schedule.step();
582
583 if let Some(ref mut meta) = self.meta_state {
584 meta.update_loss_history(loss);
585 }
586
587 Ok(())
588 }
589
590 pub fn current_learning_rate(&self) -> T {
592 self.learning_schedule.current_rate
593 }
594
595 pub fn learning_schedule(&self) -> &LearningSchedule<T> {
597 &self.learning_schedule
598 }
599
600 pub fn get_statistics(&self) -> LearningStatistics<T> {
601 LearningStatistics {
602 total_episodes: self.loss_history.len(),
603 average_loss: self.get_average_loss(),
604 best_loss: self.get_best_loss(),
605 convergence_rate: self.get_convergence_rate(),
606 learning_stability: self.performance_metrics.get_stability_score(),
607 }
608 }
609
610 pub fn get_average_loss(&self) -> T {
611 let Some(count) = cast_scalar::<T, _>(self.loss_history.len())
612 .ok()
613 .filter(|c| *c > T::zero())
614 else {
615 return T::zero();
616 };
617 self.loss_history
618 .iter()
619 .fold(T::zero(), |acc, &loss| acc + loss)
620 / count
621 }
622
623 pub fn get_best_loss(&self) -> T {
624 self.loss_history
625 .iter()
626 .fold(T::infinity(), |min, &loss| min.min(loss))
627 }
628
629 pub fn get_convergence_rate(&self) -> T {
630 if self.loss_history.len() < 2 {
631 return T::zero();
632 }
633
634 let recent_losses: Vec<_> = self.loss_history.iter().rev().take(10).cloned().collect();
635 if recent_losses.len() < 2 {
636 return T::zero();
637 }
638
639 let (Some(&initial), Some(&final_loss)) = (recent_losses.last(), recent_losses.first())
642 else {
643 return T::zero();
644 };
645
646 if initial > T::zero() {
647 (initial - final_loss) / initial
648 } else {
649 T::zero()
650 }
651 }
652
653 pub fn reset(&mut self) -> Result<()> {
654 self.loss_history.clear();
655 self.adaptation_history.clear();
656 self.performance_metrics.reset();
657
658 if let Some(ref mut meta) = self.meta_state {
659 *meta = MetaState::new(meta.get_parameters().len())?;
660 }
661
662 Ok(())
663 }
664
665 pub fn validate(&self) -> Result<ValidationResult> {
666 let mut issues = Vec::new();
667
668 if self.loss_history.iter().any(|&loss| !loss.is_finite()) {
669 issues.push("Invalid loss values detected".to_string());
670 }
671
672 Ok(ValidationResult { issues })
673 }
674
675 pub fn to_serializable(&self) -> Result<SerializableLearningState<T>> {
676 Ok(SerializableLearningState {
677 loss_history: self.loss_history.iter().cloned().collect(),
678 average_loss: self.get_average_loss(),
679 best_loss: self.get_best_loss(),
680 convergence_rate: self.get_convergence_rate(),
681 })
682 }
683
684 pub fn from_serializable(&mut self, state: SerializableLearningState<T>) -> Result<()> {
685 self.loss_history = VecDeque::from(state.loss_history);
686 Ok(())
687 }
688}
689
690#[derive(Debug, Clone)]
692pub struct MemoryState<
693 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
694> {
695 attention_caches: HashMap<String, AttentionCache<T>>,
697
698 memory_usage: MemoryUsageTracker,
700
701 cache_statistics: CacheStatistics,
703}
704
705impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
706 MemoryState<T>
707{
708 pub fn new() -> Result<Self> {
709 Ok(Self {
710 attention_caches: HashMap::new(),
711 memory_usage: MemoryUsageTracker::new(),
712 cache_statistics: CacheStatistics::new(),
713 })
714 }
715
716 pub fn get_total_usage(&self) -> usize {
717 self.memory_usage.total_usage
718 }
719
720 pub fn reset(&mut self) -> Result<()> {
721 self.attention_caches.clear();
722 self.memory_usage.reset();
723 self.cache_statistics.reset();
724 Ok(())
725 }
726}
727
728const DEFAULT_MAX_CHECKPOINTS: usize = 10;
732
733const AUTO_SAVE_PREFIX: &str = "auto_";
737
738pub struct CheckpointManager<
740 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
741> {
742 checkpoints: HashMap<String, OptimizerStateSnapshot<T>>,
744
745 metadata: HashMap<String, CheckpointMetadata>,
747
748 max_checkpoints: usize,
750
751 auto_save_config: AutoSaveConfig,
753}
754
755impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
756 CheckpointManager<T>
757{
758 pub fn new(config: &TransformerBasedOptimizerConfig<T>) -> Result<Self> {
765 let state_config = StateConfig::from_optimizer_config(config);
766 Ok(Self {
767 checkpoints: HashMap::new(),
768 metadata: HashMap::new(),
769 max_checkpoints: DEFAULT_MAX_CHECKPOINTS,
770 auto_save_config: AutoSaveConfig {
771 enabled: state_config.auto_save_enabled,
772 frequency: state_config.checkpoint_frequency,
773 ..AutoSaveConfig::default()
774 },
775 })
776 }
777
778 pub fn auto_save_config(&self) -> &AutoSaveConfig {
780 &self.auto_save_config
781 }
782
783 pub fn maybe_auto_save(
794 &mut self,
795 step: usize,
796 snapshot: OptimizerStateSnapshot<T>,
797 ) -> Result<Option<String>> {
798 if !self.auto_save_config.enabled
799 || self.auto_save_config.frequency == 0
800 || !step.is_multiple_of(self.auto_save_config.frequency)
801 {
802 return Ok(None);
803 }
804 let max_auto_saves = self.auto_save_config.max_auto_saves;
805 let id = self.save_checkpoint(format!("{AUTO_SAVE_PREFIX}{step}"), snapshot)?;
806 self.evict_surplus_auto_saves(max_auto_saves);
807 Ok(Some(id))
808 }
809
810 pub fn auto_save_count(&self) -> usize {
812 self.metadata
813 .values()
814 .filter(|m| m.name.starts_with(AUTO_SAVE_PREFIX))
815 .count()
816 }
817
818 fn evict_surplus_auto_saves(&mut self, max_auto_saves: usize) {
821 loop {
822 let mut autos: Vec<(String, std::time::Instant)> = self
823 .metadata
824 .iter()
825 .filter(|(_, m)| m.name.starts_with(AUTO_SAVE_PREFIX))
826 .map(|(id, m)| (id.clone(), m.created_at))
827 .collect();
828 if autos.len() <= max_auto_saves {
829 return;
830 }
831 autos.sort_by_key(|(_, created)| *created);
832 match autos.first() {
833 Some((oldest, _)) => {
834 let oldest = oldest.clone();
835 self.checkpoints.remove(&oldest);
836 self.metadata.remove(&oldest);
837 }
838 None => return,
839 }
840 }
841 }
842
843 pub fn save_checkpoint(
844 &mut self,
845 name: String,
846 snapshot: OptimizerStateSnapshot<T>,
847 ) -> Result<String> {
848 let checkpoint_id = format!("{}_{}", name, snapshot.version);
849
850 let metadata = CheckpointMetadata {
852 id: checkpoint_id.clone(),
853 name: name.clone(),
854 created_at: std::time::Instant::now(),
855 size_estimate: snapshot.parameters.len() * std::mem::size_of::<T>(),
856 description: format!("Checkpoint at version {}", snapshot.version),
857 };
858
859 self.checkpoints.insert(checkpoint_id.clone(), snapshot);
860 self.metadata.insert(checkpoint_id.clone(), metadata);
861
862 if self.checkpoints.len() > self.max_checkpoints {
864 self.cleanup_old_checkpoints()?;
865 }
866
867 Ok(checkpoint_id)
868 }
869
870 pub fn load_checkpoint(&self, checkpoint_id: &str) -> Result<OptimizerStateSnapshot<T>> {
871 self.checkpoints.get(checkpoint_id).cloned().ok_or_else(|| {
872 crate::error::OptimError::Other(format!("Checkpoint {} not found", checkpoint_id))
873 })
874 }
875
876 pub fn list_checkpoints(&self) -> Vec<CheckpointMetadata> {
877 self.metadata.values().cloned().collect()
878 }
879
880 pub fn delete_checkpoint(&mut self, checkpoint_id: &str) -> Result<bool> {
881 let removed_checkpoint = self.checkpoints.remove(checkpoint_id).is_some();
882 let removed_metadata = self.metadata.remove(checkpoint_id).is_some();
883 Ok(removed_checkpoint && removed_metadata)
884 }
885
886 pub fn get_checkpoint_count(&self) -> usize {
887 self.checkpoints.len()
888 }
889
890 fn cleanup_old_checkpoints(&mut self) -> Result<()> {
891 while self.checkpoints.len() > self.max_checkpoints {
893 if let Some((oldest_id, _)) = self
894 .metadata
895 .iter()
896 .min_by_key(|(_, metadata)| metadata.created_at)
897 .map(|(id, metadata)| (id.clone(), metadata.clone()))
898 {
899 self.checkpoints.remove(&oldest_id);
900 self.metadata.remove(&oldest_id);
901 } else {
902 break;
903 }
904 }
905 Ok(())
906 }
907}
908
909#[derive(Debug, Clone)]
912pub struct ParameterSnapshot<
913 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
914> {
915 pub parameters: Array1<T>,
916 pub timestamp: std::time::Instant,
917 pub norm: T,
918}
919
920#[derive(Debug, Clone)]
921pub struct ParameterStatistics<
922 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
923> {
924 pub total_snapshots: usize,
925 pub average_norm: T,
926 pub max_norm: T,
927 pub min_norm: T,
928 pub norm_trend: T,
929}
930
931impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static> Default
932 for ParameterStatistics<T>
933{
934 fn default() -> Self {
935 Self::new()
936 }
937}
938
939impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
940 ParameterStatistics<T>
941{
942 pub fn new() -> Self {
943 Self {
944 total_snapshots: 0,
945 average_norm: T::zero(),
946 max_norm: T::zero(),
947 min_norm: T::infinity(),
948 norm_trend: T::zero(),
949 }
950 }
951
952 pub fn update_with_snapshot(&mut self, snapshot: &ParameterSnapshot<T>) {
953 self.total_snapshots += 1;
954 self.average_norm = (self.average_norm
955 * scirs2_core::numeric::NumCast::from(self.total_snapshots - 1)
956 .unwrap_or_else(|| T::zero())
957 + snapshot.norm)
958 / scirs2_core::numeric::NumCast::from(self.total_snapshots)
959 .unwrap_or_else(|| T::zero());
960 self.max_norm = self.max_norm.max(snapshot.norm);
961 self.min_norm = self.min_norm.min(snapshot.norm);
962 }
963}
964
965#[derive(Debug, Clone)]
966pub struct AdaptiveState<
967 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
968> {
969 pub m: Array1<T>,
971 pub v: Array1<T>,
973 pub step_count: usize,
975 pub beta1: T,
977 pub beta2: T,
978 pub epsilon: T,
980}
981
982impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
983 AdaptiveState<T>
984{
985 pub fn new(parameter_count: usize) -> Result<Self> {
986 Ok(Self {
987 m: Array1::zeros(parameter_count),
988 v: Array1::zeros(parameter_count),
989 step_count: 0,
990 beta1: scirs2_core::numeric::NumCast::from(0.9).unwrap_or_else(|| T::zero()),
991 beta2: scirs2_core::numeric::NumCast::from(0.999).unwrap_or_else(|| T::zero()),
992 epsilon: scirs2_core::numeric::NumCast::from(1e-8).unwrap_or_else(|| T::zero()),
993 })
994 }
995
996 pub fn update_with_step(&mut self, _update: &Array1<T>) -> Result<()> {
997 self.step_count += 1;
998 Ok(())
1000 }
1001
1002 pub fn reset(&mut self) -> Result<()> {
1003 self.m.fill(T::zero());
1004 self.v.fill(T::zero());
1005 self.step_count = 0;
1006 Ok(())
1007 }
1008}
1009
1010#[derive(Debug, Clone)]
1011pub struct GradientAccumulator<
1012 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1013> {
1014 pub accumulated_gradients: Array1<T>,
1016 pub accumulation_count: usize,
1018}
1019
1020impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
1021 GradientAccumulator<T>
1022{
1023 pub fn new(parameter_count: usize) -> Result<Self> {
1024 Ok(Self {
1025 accumulated_gradients: Array1::zeros(parameter_count),
1026 accumulation_count: 0,
1027 })
1028 }
1029
1030 pub fn reset(&mut self) -> Result<()> {
1031 self.accumulated_gradients.fill(T::zero());
1032 self.accumulation_count = 0;
1033 Ok(())
1034 }
1035}
1036
1037#[derive(Debug, Clone)]
1038pub struct ConvergenceTracker<
1039 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1040> {
1041 recent_losses: VecDeque<T>,
1043 convergence_threshold: T,
1045 stability_window: usize,
1047}
1048
1049impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static> Default
1050 for ConvergenceTracker<T>
1051{
1052 fn default() -> Self {
1053 Self::new()
1054 }
1055}
1056
1057impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
1058 ConvergenceTracker<T>
1059{
1060 pub fn new() -> Self {
1061 Self {
1062 recent_losses: VecDeque::new(),
1063 convergence_threshold: scirs2_core::numeric::NumCast::from(1e-6)
1064 .unwrap_or_else(|| T::zero()),
1065 stability_window: 10,
1066 }
1067 }
1068
1069 pub fn record_loss(&mut self, loss: T) {
1070 self.recent_losses.push_back(loss);
1071 if self.recent_losses.len() > self.stability_window {
1072 self.recent_losses.pop_front();
1073 }
1074 }
1075
1076 pub fn has_converged(&self) -> bool {
1084 if self.recent_losses.len() < 2 {
1085 return false;
1086 }
1087 let mut total = T::zero();
1088 for pair in self.recent_losses.iter().collect::<Vec<_>>().windows(2) {
1089 total = total + (*pair[1] - *pair[0]).abs();
1090 }
1091 let steps: T = scirs2_core::numeric::NumCast::from(self.recent_losses.len() - 1)
1092 .unwrap_or_else(|| T::one());
1093 (total / steps) <= self.convergence_threshold
1094 }
1095
1096 pub fn convergence_threshold(&self) -> T {
1098 self.convergence_threshold
1099 }
1100
1101 pub fn get_convergence_rate(&self) -> T {
1102 if self.recent_losses.len() < 2 {
1103 return T::zero();
1104 }
1105
1106 let (Some(&first), Some(&last)) = (self.recent_losses.front(), self.recent_losses.back())
1107 else {
1108 return T::zero();
1109 };
1110
1111 if first > T::zero() {
1112 (first - last) / first
1113 } else {
1114 T::zero()
1115 }
1116 }
1117
1118 pub fn get_stability_score(&self) -> T {
1119 if self.recent_losses.len() < 2 {
1120 return T::zero();
1121 }
1122
1123 let Some(count) = cast_scalar::<T, _>(self.recent_losses.len())
1124 .ok()
1125 .filter(|c| *c > T::zero())
1126 else {
1127 return T::zero();
1128 };
1129 let mean = self.recent_losses.iter().fold(T::zero(), |acc, &x| acc + x) / count;
1130 let variance = self
1131 .recent_losses
1132 .iter()
1133 .map(|&x| (x - mean) * (x - mean))
1134 .fold(T::zero(), |acc, x| acc + x)
1135 / count;
1136
1137 T::one() / (T::one() + variance.sqrt())
1138 }
1139
1140 pub fn reset(&mut self) {
1141 self.recent_losses.clear();
1142 }
1143
1144 pub fn to_serializable(&self) -> SerializableConvergenceState<T> {
1145 SerializableConvergenceState {
1146 recent_losses: self.recent_losses.iter().cloned().collect(),
1147 convergence_rate: self.get_convergence_rate(),
1148 stability_score: self.get_stability_score(),
1149 }
1150 }
1151
1152 pub fn from_serializable(&mut self, state: SerializableConvergenceState<T>) -> Result<()> {
1153 self.recent_losses = VecDeque::from(state.recent_losses);
1154 Ok(())
1155 }
1156}
1157
1158#[derive(Debug, Clone)]
1160pub struct OptimizerStateSnapshot<
1161 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1162> {
1163 pub parameters: Array1<T>,
1164 pub optimization_state: OptimizationState<T>,
1165 pub learning_state: LearningState<T>,
1166 pub memory_state: MemoryState<T>,
1167 pub version: usize,
1168 pub timestamp: std::time::Instant,
1169 pub metadata: SnapshotMetadata,
1170}
1171
1172#[derive(Debug, Clone)]
1173pub struct SnapshotMetadata {
1174 pub parameter_count: usize,
1175 pub total_updates: usize,
1176 pub session_duration: Duration,
1177}
1178
1179#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1181pub struct StateConfig {
1182 pub max_history_size: usize,
1183 pub checkpoint_frequency: usize,
1184 pub auto_save_enabled: bool,
1185 pub validation_enabled: bool,
1186}
1187
1188impl StateConfig {
1189 pub fn from_optimizer_config<
1200 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1201 >(
1202 config: &TransformerBasedOptimizerConfig<T>,
1203 ) -> Self {
1204 Self {
1205 max_history_size: config.performance_config.max_history_size,
1206 checkpoint_frequency: config.performance_config.metrics_interval.max(1),
1207 auto_save_enabled: true,
1208 validation_enabled: true,
1209 }
1210 }
1211}
1212
1213#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1214pub struct StateMetadata {
1215 pub version: usize,
1216 pub created_at: std::time::SystemTime,
1217 pub last_updated: std::time::SystemTime,
1218 pub total_updates: usize,
1219 pub configuration: StateConfig,
1220}
1221
1222#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1224pub struct StateStatistics<
1225 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1226> {
1227 pub total_updates: usize,
1228 pub last_update_magnitude: T,
1229 pub average_update_magnitude: T,
1230 pub parameter_change_rate: T,
1231 pub update_frequency: f64,
1232}
1233
1234impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static> Default
1235 for StateStatistics<T>
1236{
1237 fn default() -> Self {
1238 Self::new()
1239 }
1240}
1241
1242impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
1243 StateStatistics<T>
1244{
1245 pub fn new() -> Self {
1246 Self {
1247 total_updates: 0,
1248 last_update_magnitude: T::zero(),
1249 average_update_magnitude: T::zero(),
1250 parameter_change_rate: T::zero(),
1251 update_frequency: 0.0,
1252 }
1253 }
1254
1255 pub fn record_update(&mut self, update: &Array1<T>, _loss: Option<T>) {
1256 self.total_updates += 1;
1257 let magnitude = update
1258 .iter()
1259 .map(|&x| x * x)
1260 .fold(T::zero(), |acc, x| acc + x)
1261 .sqrt();
1262 self.last_update_magnitude = magnitude;
1263 self.average_update_magnitude = (self.average_update_magnitude
1264 * scirs2_core::numeric::NumCast::from(self.total_updates - 1)
1265 .unwrap_or_else(|| T::zero())
1266 + magnitude)
1267 / scirs2_core::numeric::NumCast::from(self.total_updates).unwrap_or_else(|| T::zero());
1268 }
1269
1270 pub fn reset(&mut self) {
1271 self.total_updates = 0;
1272 self.last_update_magnitude = T::zero();
1273 self.average_update_magnitude = T::zero();
1274 self.parameter_change_rate = T::zero();
1275 self.update_frequency = 0.0;
1276 }
1277}
1278
1279#[derive(Debug, Clone)]
1281pub struct StateValidationReport {
1282 pub is_valid: bool,
1283 pub issues: Vec<String>,
1284 pub validation_timestamp: Instant,
1285}
1286
1287#[derive(Debug, Clone)]
1288pub struct ValidationResult {
1289 pub issues: Vec<String>,
1290}
1291
1292#[derive(Debug, Clone)]
1293pub struct StateSummary<
1294 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1295> {
1296 pub version: usize,
1297 pub parameter_count: usize,
1298 pub parameter_norm: T,
1299 pub total_updates: usize,
1300 pub session_duration: Duration,
1301 pub last_update_magnitude: T,
1302 pub average_loss: T,
1303 pub convergence_rate: T,
1304 pub memory_usage: usize,
1305 pub checkpoint_count: usize,
1306}
1307
1308#[derive(Debug, Clone, Serialize, Deserialize)]
1310pub struct SerializableState<
1311 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1312> {
1313 pub parameters: Vec<T>,
1314 pub parameter_shape: Vec<usize>,
1315 pub optimization_state: SerializableOptimizationState<T>,
1316 pub learning_state: SerializableLearningState<T>,
1317 pub metadata: StateMetadata,
1318 pub statistics: StateStatistics<T>,
1319}
1320
1321#[derive(Debug, Clone, Serialize, Deserialize)]
1322pub struct SerializableOptimizationState<
1323 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1324> {
1325 pub learning_rate: T,
1326 pub step_count: usize,
1327 pub last_update_magnitude: T,
1328 pub momentum: Option<Vec<T>>,
1329 pub convergence_metrics: SerializableConvergenceState<T>,
1330}
1331
1332#[derive(Debug, Clone, Serialize, Deserialize)]
1333pub struct SerializableLearningState<
1334 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1335> {
1336 pub loss_history: Vec<T>,
1337 pub average_loss: T,
1338 pub best_loss: T,
1339 pub convergence_rate: T,
1340}
1341
1342#[derive(Debug, Clone, Serialize, Deserialize)]
1343pub struct SerializableConvergenceState<
1344 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1345> {
1346 pub recent_losses: Vec<T>,
1347 pub convergence_rate: T,
1348 pub stability_score: T,
1349}
1350
1351#[derive(Debug, Clone)]
1353pub struct TaskAdaptationRecord<
1354 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1355> {
1356 pub task_id: String,
1357 pub adaptation_steps: usize,
1358 pub final_loss: T,
1359 pub adaptation_time: Duration,
1360}
1361
1362#[derive(Debug, Clone)]
1363pub struct LearningSchedule<
1369 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1370> {
1371 pub initial_rate: T,
1372 pub current_rate: T,
1373 pub warmup_steps: usize,
1374 pub decay_factor: T,
1375 steps_taken: usize,
1377}
1378
1379impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
1380 LearningSchedule<T>
1381{
1382 pub fn new(initial_rate: T, warmup_steps: usize) -> Self {
1383 Self {
1384 initial_rate,
1385 current_rate: initial_rate,
1386 warmup_steps,
1387 decay_factor: scirs2_core::numeric::NumCast::from(0.95).unwrap_or_else(|| T::zero()),
1388 steps_taken: 0,
1389 }
1390 }
1391
1392 pub fn rate_at(&self, step: usize) -> T {
1397 if step < self.warmup_steps {
1398 let progress: T =
1399 scirs2_core::numeric::NumCast::from((step + 1) as f64 / self.warmup_steps as f64)
1400 .unwrap_or_else(T::one);
1401 return self.initial_rate * progress;
1402 }
1403 let decayed: T = scirs2_core::numeric::NumCast::from((step - self.warmup_steps) as f64)
1404 .unwrap_or_else(T::zero);
1405 self.initial_rate * self.decay_factor.powf(decayed)
1406 }
1407
1408 pub fn step(&mut self) -> T {
1410 self.current_rate = self.rate_at(self.steps_taken);
1411 self.steps_taken += 1;
1412 self.current_rate
1413 }
1414
1415 pub fn steps_taken(&self) -> usize {
1417 self.steps_taken
1418 }
1419}
1420
1421#[derive(Debug, Clone)]
1422pub struct LearningPerformanceMetrics<
1423 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1424> {
1425 pub loss_trend: T,
1426 pub convergence_stability: T,
1427 pub adaptation_efficiency: T,
1428}
1429
1430impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static> Default
1431 for LearningPerformanceMetrics<T>
1432{
1433 fn default() -> Self {
1434 Self::new()
1435 }
1436}
1437
1438impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
1439 LearningPerformanceMetrics<T>
1440{
1441 pub fn new() -> Self {
1442 Self {
1443 loss_trend: T::zero(),
1444 convergence_stability: T::zero(),
1445 adaptation_efficiency: T::zero(),
1446 }
1447 }
1448
1449 pub fn record_loss(&mut self, _loss: T) {
1450 }
1452
1453 pub fn get_stability_score(&self) -> T {
1454 self.convergence_stability
1455 }
1456
1457 pub fn reset(&mut self) {
1458 self.loss_trend = T::zero();
1459 self.convergence_stability = T::zero();
1460 self.adaptation_efficiency = T::zero();
1461 }
1462}
1463
1464#[derive(Debug, Clone)]
1465pub struct OptimizationProgress<
1466 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1467> {
1468 pub step_count: usize,
1469 pub current_learning_rate: T,
1470 pub last_update_magnitude: T,
1471 pub convergence_rate: T,
1472 pub stability_score: T,
1473}
1474
1475#[derive(Debug, Clone)]
1476pub struct LearningStatistics<
1477 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1478> {
1479 pub total_episodes: usize,
1480 pub average_loss: T,
1481 pub best_loss: T,
1482 pub convergence_rate: T,
1483 pub learning_stability: T,
1484}
1485
1486#[derive(Debug, Clone)]
1487pub struct AttentionCache<
1488 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
1489> {
1490 pub cached_keys: Array2<T>,
1491 pub cached_values: Array2<T>,
1492 pub cache_size: usize,
1493}
1494
1495#[derive(Debug, Clone)]
1496pub struct MemoryUsageTracker {
1497 pub total_usage: usize,
1498 pub peak_usage: usize,
1499 pub allocation_count: usize,
1500}
1501
1502impl Default for MemoryUsageTracker {
1503 fn default() -> Self {
1504 Self::new()
1505 }
1506}
1507
1508impl MemoryUsageTracker {
1509 pub fn new() -> Self {
1510 Self {
1511 total_usage: 0,
1512 peak_usage: 0,
1513 allocation_count: 0,
1514 }
1515 }
1516
1517 pub fn reset(&mut self) {
1518 self.total_usage = 0;
1519 self.peak_usage = 0;
1520 self.allocation_count = 0;
1521 }
1522}
1523
1524#[derive(Debug, Clone)]
1525pub struct CacheStatistics {
1526 pub hit_count: usize,
1527 pub miss_count: usize,
1528 pub eviction_count: usize,
1529}
1530
1531impl Default for CacheStatistics {
1532 fn default() -> Self {
1533 Self::new()
1534 }
1535}
1536
1537impl CacheStatistics {
1538 pub fn new() -> Self {
1539 Self {
1540 hit_count: 0,
1541 miss_count: 0,
1542 eviction_count: 0,
1543 }
1544 }
1545
1546 pub fn reset(&mut self) {
1547 self.hit_count = 0;
1548 self.miss_count = 0;
1549 self.eviction_count = 0;
1550 }
1551}
1552
1553#[derive(Debug, Clone)]
1554pub struct CheckpointMetadata {
1555 pub id: String,
1556 pub name: String,
1557 pub created_at: Instant,
1558 pub size_estimate: usize,
1559 pub description: String,
1560}
1561
1562#[derive(Debug, Clone)]
1563pub struct AutoSaveConfig {
1564 pub enabled: bool,
1565 pub frequency: usize,
1566 pub max_auto_saves: usize,
1567}
1568
1569impl Default for AutoSaveConfig {
1570 fn default() -> Self {
1571 Self {
1572 enabled: true,
1573 frequency: 100,
1574 max_auto_saves: 5,
1575 }
1576 }
1577}
1578
1579#[cfg(test)]
1580mod tests {
1581 use super::*;
1582
1583 #[test]
1584 fn test_optimizer_state_creation() {
1585 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1586 let state = TransformerOptimizerState::new(&config);
1587 assert!(state.is_ok());
1588
1589 let s = state.expect("TransformerOptimizerState::new should succeed");
1590 assert_eq!(s.version, 0);
1591 assert!(!s.current_parameters.is_empty());
1592 }
1593
1594 #[test]
1595 fn test_state_update() {
1596 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1597 let mut state = TransformerOptimizerState::new(&config)
1598 .expect("TransformerOptimizerState::new should succeed");
1599
1600 let update = Array1::<f32>::ones(state.current_parameters.len());
1601 let result = state.update_with_step(&update, Some(1.5));
1602 assert!(result.is_ok());
1603 assert_eq!(state.version, 1);
1604 }
1605
1606 #[test]
1613 fn test_update_with_step_tiles_per_layer_update_on_default_config() {
1614 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1615 assert_ne!(
1616 config.num_transformer_layers, 1,
1617 "test is only meaningful when layers > 1"
1618 );
1619 let mut state = TransformerOptimizerState::new(&config)
1620 .expect("TransformerOptimizerState::new should succeed");
1621 let total_len = state.current_parameters.len();
1622 assert_eq!(
1623 total_len,
1624 config.model_dimension * config.num_transformer_layers
1625 );
1626
1627 let per_layer_update = Array1::<f32>::from_elem(config.model_dimension, 0.5);
1630 let result = state.update_with_step(&per_layer_update, Some(1.0));
1631 assert!(
1632 result.is_ok(),
1633 "a per-layer update must not panic or error on the default config: {result:?}"
1634 );
1635
1636 for layer in 0..config.num_transformer_layers {
1638 let start = layer * config.model_dimension;
1639 let segment = &state.current_parameters.as_slice().expect("contiguous")
1640 [start..start + config.model_dimension];
1641 for &v in segment {
1642 approx::assert_abs_diff_eq!(v, 0.5, epsilon = 1e-6);
1643 }
1644 }
1645 }
1646
1647 #[test]
1650 fn test_update_with_step_rejects_untileable_length() {
1651 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1652 let mut state = TransformerOptimizerState::new(&config)
1653 .expect("TransformerOptimizerState::new should succeed");
1654 let bad_update = Array1::<f32>::ones(state.current_parameters.len() + 3);
1655 let result = state.update_with_step(&bad_update, None);
1656 assert!(result.is_err(), "an untileable update length must error");
1657 }
1658
1659 #[test]
1660 fn test_snapshot_creation() {
1661 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1662 let state = TransformerOptimizerState::new(&config)
1663 .expect("TransformerOptimizerState::new should succeed");
1664
1665 let snapshot = state.create_snapshot();
1666 assert!(snapshot.is_ok());
1667
1668 let snap = snapshot.expect("create_snapshot should succeed");
1669 assert_eq!(snap.version, 0);
1670 assert_eq!(snap.parameters.len(), state.current_parameters.len());
1671 }
1672
1673 #[test]
1674 fn test_checkpoint_management() {
1675 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1676 let mut state = TransformerOptimizerState::new(&config)
1677 .expect("TransformerOptimizerState::new should succeed");
1678
1679 let checkpoint_id = state.save_checkpoint("test_checkpoint".to_string());
1680 assert!(checkpoint_id.is_ok());
1681
1682 let id = checkpoint_id.expect("save_checkpoint should succeed");
1683 let load_result = state.load_checkpoint(&id);
1684 assert!(load_result.is_ok());
1685 }
1686
1687 #[test]
1688 fn test_parameter_history() {
1689 let history = ParameterHistory::<f32>::new(10, 5);
1690 assert!(history.is_ok());
1691
1692 let mut h = history.expect("ParameterHistory::new should succeed");
1693 let params = Array1::<f32>::ones(5);
1694 assert!(h.record_parameters(¶ms).is_ok());
1695
1696 let recent = h.get_recent_parameters(1);
1697 assert_eq!(recent.len(), 1);
1698 }
1699
1700 #[test]
1701 fn test_convergence_tracker() {
1702 let mut tracker = ConvergenceTracker::<f32>::new();
1703
1704 tracker.record_loss(2.0);
1705 tracker.record_loss(1.5);
1706 tracker.record_loss(1.0);
1707
1708 let convergence = tracker.get_convergence_rate();
1709 assert!(convergence > 0.0);
1710
1711 let stability = tracker.get_stability_score();
1712 assert!(stability > 0.0 && stability <= 1.0);
1713 }
1714
1715 #[test]
1716 fn test_state_validation() {
1717 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1718 let state = TransformerOptimizerState::new(&config)
1719 .expect("TransformerOptimizerState::new should succeed");
1720
1721 let validation = state.validate_state();
1722 assert!(validation.is_ok());
1723
1724 let report = validation.expect("validate_state should succeed");
1725 assert!(report.is_valid);
1726 }
1727
1728 #[test]
1732 fn state_config_follows_the_optimizer_performance_config() {
1733 let mut config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1734 config.performance_config.max_history_size = 42;
1735 config.performance_config.metrics_interval = 7;
1736
1737 let derived = StateConfig::from_optimizer_config(&config);
1738 assert_eq!(derived.max_history_size, 42);
1739 assert_eq!(derived.checkpoint_frequency, 7);
1740
1741 config.performance_config.metrics_interval = 0;
1744 assert_eq!(
1745 StateConfig::from_optimizer_config(&config).checkpoint_frequency,
1746 1
1747 );
1748 }
1749
1750 #[test]
1754 fn checkpoint_manager_auto_saves_on_the_configured_cadence() {
1755 let mut config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1756 config.performance_config.metrics_interval = 3;
1757 let state = TransformerOptimizerState::new(&config).expect("state");
1758
1759 let mut manager = CheckpointManager::<f32>::new(&config).expect("manager");
1760 assert_eq!(manager.auto_save_config().frequency, 3);
1761
1762 for step in [1_usize, 2, 4, 5] {
1764 assert_eq!(
1765 manager
1766 .maybe_auto_save(step, state.create_snapshot().expect("snapshot"))
1767 .expect("auto save"),
1768 None,
1769 "step {step} is not a multiple of 3"
1770 );
1771 }
1772 assert_eq!(manager.auto_save_count(), 0);
1773
1774 let cap = manager.auto_save_config().max_auto_saves;
1776 assert!(cap > 0, "default policy should retain some auto-saves");
1777 for step in (0..).step_by(3).take(cap + 3) {
1778 let id = manager
1779 .maybe_auto_save(step, state.create_snapshot().expect("snapshot"))
1780 .expect("auto save")
1781 .expect("on-cadence step must produce a checkpoint");
1782 assert!(id.starts_with("auto_"), "unexpected id {id}");
1783 }
1784 assert_eq!(
1785 manager.auto_save_count(),
1786 cap,
1787 "surplus auto-saves must be evicted oldest-first"
1788 );
1789
1790 manager
1793 .save_checkpoint("manual".to_string(), state.create_snapshot().expect("snap"))
1794 .expect("manual save");
1795 manager
1796 .maybe_auto_save(3_000, state.create_snapshot().expect("snap"))
1797 .expect("auto save");
1798 assert_eq!(manager.auto_save_count(), cap);
1799 assert!(manager
1800 .list_checkpoints()
1801 .iter()
1802 .any(|m| m.name == "manual"));
1803 }
1804
1805 #[test]
1807 fn checkpoint_manager_auto_save_respects_a_disabled_policy() {
1808 let config = super::super::config::TransformerBasedOptimizerConfig::<f32>::default();
1809 let state = TransformerOptimizerState::new(&config).expect("state");
1810 let mut manager = CheckpointManager::<f32>::new(&config).expect("manager");
1811 manager.auto_save_config.enabled = false;
1812
1813 assert_eq!(
1814 manager
1815 .maybe_auto_save(0, state.create_snapshot().expect("snapshot"))
1816 .expect("auto save"),
1817 None
1818 );
1819 assert_eq!(manager.get_checkpoint_count(), 0);
1820 }
1821}