1use scirs2_core::ndarray::{s, Array, Array1, Array2, ArrayBase, Data, Dimension};
8use scirs2_core::numeric::Float;
9use scirs2_core::random::{rngs::StdRng, seeded_rng, thread_rng, CoreRandom};
10use std::collections::{HashMap, VecDeque};
11use std::fmt::Debug;
12
13use super::LearnedOptimizerConfig;
14use crate::common::cast_scalar;
15use crate::error::{OptimError, Result};
16
17pub mod bptt;
18pub mod components;
19pub mod features;
20pub mod introspection;
21pub mod trainer;
22
23pub use bptt::{
24 BpttConfig, DiagonalQuadraticTask, FrozenRollout, MetaTrainingTask, NetworkGradients,
25};
26pub use features::build_lstm_features;
27pub use trainer::MetaTrainer;
28
29#[derive(Debug)]
31pub struct LSTMOptimizer<T: Float + Debug + Send + Sync + 'static> {
32 config: LearnedOptimizerConfig,
34
35 lstm_network: LSTMNetwork<T>,
37
38 history_buffer: HistoryBuffer<T>,
40
41 meta_learner: MetaLearner<T>,
43
44 lr_controller: AdaptiveLearningRateController<T>,
46
47 state_tracker: OptimizationStateTracker<T>,
49
50 metrics: LSTMOptimizerMetrics,
52
53 step_count: usize,
55}
56
57#[derive(Debug, Clone)]
59pub struct LSTMNetwork<T: Float + Debug + Send + Sync + 'static> {
60 layers: Vec<LSTMLayer<T>>,
62
63 output_projection: OutputProjection<T>,
65
66 attention: Option<AttentionMechanism<T>>,
68
69 layer_norms: Vec<LayerNormalization<T>>,
71
72 dropout_rate: f64,
74
75 training: bool,
85}
86
87#[derive(Debug, Clone)]
89pub struct LSTMLayer<T: Float + Debug + Send + Sync + 'static> {
90 weight_ih: Array2<T>,
92
93 weight_hh: Array2<T>,
95
96 bias_ih: Array1<T>,
98
99 bias_hh: Array1<T>,
101
102 hidden_state: Array1<T>,
104
105 cell_state: Array1<T>,
107
108 hiddensize: usize,
110}
111
112#[derive(Debug, Clone)]
114pub struct OutputProjection<T: Float + Debug + Send + Sync + 'static> {
115 weights: Array2<T>,
117
118 bias: Array1<T>,
120
121 output_transform: OutputTransform,
123}
124
125impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> OutputProjection<T> {
126 pub fn new(
133 input_size: usize,
134 output_size: usize,
135 output_transform: OutputTransform,
136 ) -> Result<Self> {
137 Self::new_with_rng(
138 input_size,
139 output_size,
140 output_transform,
141 &mut seeded_rng(thread_rng().random::<u64>()),
142 )
143 }
144
145 pub(crate) fn new_with_rng(
148 input_size: usize,
149 output_size: usize,
150 output_transform: OutputTransform,
151 rng: &mut CoreRandom<StdRng>,
152 ) -> Result<Self> {
153 let limit = LSTMLayer::<T>::xavier_limit(input_size, output_size);
159 let weights = LSTMLayer::<T>::xavier_init(rng, output_size, input_size, limit);
160 let bias = Array1::zeros(output_size);
161
162 Ok(Self {
163 weights,
164 bias,
165 output_transform,
166 })
167 }
168
169 pub fn forward(&self, input: &Array1<T>) -> Result<Array1<T>> {
171 if input.len() != self.weights.ncols() {
172 return Err(OptimError::InvalidConfig(format!(
173 "OutputProjection expected {} input features, got {}",
174 self.weights.ncols(),
175 input.len()
176 )));
177 }
178 Ok(self.weights.dot(input) + &self.bias)
179 }
180
181 pub fn reset(&mut self, input_size: usize, output_size: usize) {
189 let limit = LSTMLayer::<T>::xavier_limit(input_size, output_size);
190 let mut rng = seeded_rng(thread_rng().random::<u64>());
193 self.weights = LSTMLayer::<T>::xavier_init(&mut rng, output_size, input_size, limit);
194 self.bias = Array1::zeros(output_size);
195 }
196
197 pub fn output_size(&self) -> usize {
199 self.weights.nrows()
200 }
201}
202
203#[derive(Debug, Clone, Copy)]
205pub enum OutputTransform {
206 Identity,
208
209 Tanh,
211
212 ScaledTanh { scale: f64 },
214
215 AdaptiveScale,
217
218 LearnedNonlinear,
220}
221
222#[derive(Debug, Clone)]
224pub struct AttentionMechanism<T: Float + Debug + Send + Sync + 'static> {
225 query_proj: Array2<T>,
227
228 key_proj: Array2<T>,
230
231 value_proj: Array2<T>,
233
234 output_proj: Array2<T>,
236
237 num_heads: usize,
239
240 head_size: usize,
242
243 attentionweights: Option<Array2<T>>,
245
246 head_logits: Option<Array1<T>>,
253}
254
255impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> AttentionMechanism<T> {
256 pub fn new(config: &LearnedOptimizerConfig) -> Result<Self> {
258 Self::new_with_rng(config, &mut seeded_rng(thread_rng().random::<u64>()))
259 }
260
261 pub(crate) fn new_with_rng(
264 config: &LearnedOptimizerConfig,
265 rng: &mut CoreRandom<StdRng>,
266 ) -> Result<Self> {
267 let hiddensize = config.hidden_size;
268 let num_heads = config.attention_heads.max(1);
269 if !hiddensize.is_multiple_of(num_heads) {
270 return Err(OptimError::InvalidConfig(format!(
271 "attention_heads ({num_heads}) must evenly divide hidden_size ({hiddensize})"
272 )));
273 }
274 let head_size = hiddensize / num_heads;
275 let limit = LSTMLayer::<T>::xavier_limit(hiddensize, hiddensize);
277
278 Ok(Self {
279 query_proj: LSTMLayer::<T>::xavier_init(rng, hiddensize, hiddensize, limit),
280 key_proj: LSTMLayer::<T>::xavier_init(rng, hiddensize, hiddensize, limit),
281 value_proj: LSTMLayer::<T>::xavier_init(rng, hiddensize, hiddensize, limit),
282 output_proj: LSTMLayer::<T>::xavier_init(rng, hiddensize, hiddensize, limit),
283 num_heads,
284 head_size,
285 attentionweights: None,
286 head_logits: None,
287 })
288 }
289
290 pub fn forward(&mut self, input: &Array1<T>) -> Result<Array1<T>> {
306 let dim = self.query_proj.nrows();
307 if input.len() != dim {
308 return Err(OptimError::InvalidConfig(format!(
309 "AttentionMechanism expected {dim} features, got {}",
310 input.len()
311 )));
312 }
313
314 let query = self.query_proj.dot(input);
315 let key = self.key_proj.dot(input);
316 let value = self.value_proj.dot(input);
317
318 let head_scale = T::one()
319 / scirs2_core::numeric::NumCast::from(self.head_size)
320 .unwrap_or_else(T::one)
321 .sqrt();
322 let mut weights = Array2::zeros((self.num_heads, 1));
323 let mut logits = Array1::zeros(self.num_heads);
324 for h in 0..self.num_heads {
325 let start = h * self.head_size;
326 let end = start + self.head_size;
327 let score = query
328 .slice(s![start..end])
329 .iter()
330 .zip(key.slice(s![start..end]).iter())
331 .fold(T::zero(), |acc, (&q, &k)| acc + q * k)
332 * head_scale;
333 logits[h] = score;
334 weights[[h, 0]] = T::one();
339 }
340 self.attentionweights = Some(weights);
341 self.head_logits = Some(logits);
342
343 Ok(self.output_proj.dot(&value))
344 }
345}
346
347#[derive(Debug, Clone)]
349pub struct LayerNormalization<T: Float + Debug + Send + Sync + 'static> {
350 gamma: Array1<T>,
352
353 beta: Array1<T>,
355
356 epsilon: T,
358}
359
360impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> LayerNormalization<T> {
361 pub fn new(features: usize) -> Result<Self> {
363 Ok(Self {
364 gamma: Array1::ones(features),
365 beta: Array1::zeros(features),
366 epsilon: scirs2_core::numeric::NumCast::from(1e-5).unwrap_or_else(|| T::zero()),
367 })
368 }
369
370 pub fn forward(&self, input: &Array1<T>) -> Result<Array1<T>> {
374 if input.len() != self.gamma.len() {
375 return Err(OptimError::InvalidConfig(format!(
376 "LayerNormalization expected {} features, got {}",
377 self.gamma.len(),
378 input.len()
379 )));
380 }
381 if input.is_empty() {
382 return Ok(input.clone());
383 }
384 let n = scirs2_core::numeric::NumCast::from(input.len()).unwrap_or_else(T::one);
385 let mean = input.iter().copied().fold(T::zero(), |a, b| a + b) / n;
386 let variance = input
387 .iter()
388 .map(|&x| (x - mean) * (x - mean))
389 .fold(T::zero(), |a, b| a + b)
390 / n;
391 let inv_std = T::one() / (variance + self.epsilon).sqrt();
392
393 let mut output = Array1::zeros(input.len());
394 for i in 0..input.len() {
395 output[i] = (input[i] - mean) * inv_std * self.gamma[i] + self.beta[i];
396 }
397 Ok(output)
398 }
399}
400
401#[derive(Debug, Clone)]
403pub struct HistoryBuffer<T: Float + Debug + Send + Sync + 'static> {
404 gradients: VecDeque<Array1<T>>,
406
407 parameters: VecDeque<Array1<T>>,
409
410 losses: VecDeque<T>,
412
413 _maxlength: usize,
415
416 feature_cache: Option<Array2<T>>,
418}
419
420#[derive(Debug, Clone)]
429pub struct MetaLearner<T: Float + Debug + Send + Sync + 'static> {
430 meta_parameters: HashMap<String, Array1<T>>,
432
433 meta_gradients: HashMap<String, Array1<T>>,
435
436 task_history: VecDeque<MetaTask<T>>,
438
439 meta_state: MetaLearningState<T>,
441
442 transfer_learner: TransferLearner<T>,
444}
445
446#[derive(Debug, Clone)]
448pub struct MetaTask<T: Float + Debug + Send + Sync + 'static> {
449 pub id: String,
451
452 pub task_type: TaskType,
454
455 pub training_trajectory: Vec<TrajectoryPoint<T>>,
457
458 pub final_performance: T,
460
461 pub characteristics: TaskCharacteristics<T>,
463
464 pub weight: T,
466}
467
468#[derive(Debug, Clone, Copy)]
470pub enum TaskType {
471 SupervisedLearning,
473
474 ReinforcementLearning,
476
477 UnsupervisedLearning,
479
480 FewShotLearning,
482
483 OnlineLearning,
485
486 AdversarialTraining,
488}
489
490#[derive(Debug, Clone)]
492pub struct TrajectoryPoint<T: Float + Debug + Send + Sync + 'static> {
493 pub step: usize,
495
496 pub gradient: Array1<T>,
498
499 pub parameters: Array1<T>,
501
502 pub loss: T,
504
505 pub learning_rate: T,
507
508 pub update: Array1<T>,
510}
511
512#[derive(Debug, Clone)]
514pub struct TaskCharacteristics<T: Float + Debug + Send + Sync + 'static> {
515 pub dimensionality: usize,
517
518 pub curvature: T,
520
521 pub noise_level: T,
523
524 pub conditioning: T,
526
527 pub difficulty: T,
529
530 pub domain_features: Array1<T>,
532}
533
534#[derive(Debug, Clone)]
536pub struct MetaLearningState<T: Float + Debug + Send + Sync + 'static> {
537 pub meta_step: usize,
539
540 pub meta_lr: T,
542
543 pub adaptation_rate: T,
545
546 pub meta_validation_performance: T,
548
549 pub adaptation_history: VecDeque<AdaptationEvent<T>>,
551
552 pub inner_loop_state: InnerLoopState<T>,
554}
555
556#[derive(Debug, Clone)]
558pub struct AdaptationEvent<T: Float + Debug + Send + Sync + 'static> {
559 pub source_task: String,
561
562 pub target_task: String,
564
565 pub adaptation_steps: usize,
567
568 pub transfer_efficiency: T,
570
571 pub performance_improvement: T,
573}
574
575#[derive(Debug, Clone)]
577pub struct InnerLoopState<T: Float + Debug + Send + Sync + 'static> {
578 pub inner_step: usize,
580
581 pub inner_parameters: Array1<T>,
583
584 pub inner_optimizer_state: HashMap<String, Array1<T>>,
586
587 pub inner_performance: T,
589}
590
591#[derive(Debug, Clone)]
593pub struct TransferLearner<T: Float + Debug + Send + Sync + 'static> {
594 pub source_knowledge: HashMap<String, Array1<T>>,
596
597 pub adaptation_parameters: Array1<T>,
599
600 pub transfer_metrics: TransferMetrics<T>,
602
603 pub similarity_estimator: DomainSimilarityEstimator<T>,
605}
606
607#[derive(Debug, Clone)]
609pub struct TransferMetrics<T: Float + Debug + Send + Sync + 'static> {
610 pub efficiency: T,
612
613 pub adaptation_speed: T,
615
616 pub knowledge_retention: T,
618
619 pub negative_transfer_score: T,
621}
622
623#[derive(Debug, Clone)]
625pub struct DomainSimilarityEstimator<T: Float + Debug + Send + Sync + 'static> {
626 pub domain_embeddings: HashMap<String, Array1<T>>,
628
629 pub similarity_params: Array1<T>,
631
632 pub similarity_function: SimilarityFunction,
634}
635
636#[derive(Debug, Clone, Copy)]
638pub enum SimilarityFunction {
639 Cosine,
641
642 Euclidean,
644
645 LearnedMetric,
647
648 TaskSpecific,
650}
651
652#[derive(Debug, Clone)]
654pub struct AdaptiveLearningRateController<T: Float + Debug + Send + Sync + 'static> {
655 base_lr: T,
657
658 current_lr: T,
660
661 adaptation_params: LRAdaptationParams<T>,
663
664 lr_history: VecDeque<T>,
666
667 performance_tracker: PerformanceTracker<T>,
669}
670
671#[derive(Debug, Clone)]
673pub struct LRAdaptationParams<T: Float + Debug + Send + Sync + 'static> {
674 pub momentum: T,
676
677 pub gradient_sensitivity: T,
679
680 pub loss_sensitivity: T,
682
683 pub min_lr: T,
685
686 pub max_lr: T,
688
689 pub adaptation_rate: T,
691}
692
693#[derive(Debug, Clone)]
695pub struct PerformanceTracker<T: Float + Debug + Send + Sync + 'static> {
696 recent_losses: VecDeque<T>,
698
699 trend: PerformanceTrend,
701
702 stagnation_counter: usize,
704
705 best_performance: T,
707
708 improvement_rate: T,
710}
711
712#[derive(Debug, Clone, Copy)]
714pub enum PerformanceTrend {
715 Improving,
717
718 Stagnating,
720
721 Degrading,
723
724 Oscillating,
726
727 Unknown,
729}
730
731#[derive(Debug, Clone)]
733pub struct OptimizationStateTracker<T: Float + Debug + Send + Sync + 'static> {
734 phase: OptimizationPhase,
736
737 convergence_indicators: ConvergenceIndicators<T>,
739
740 gradient_analyzer: GradientAnalyzer<T>,
742
743 landscape_analyzer: LossLandscapeAnalyzer<T>,
745
746 stability_metrics: StabilityMetrics<T>,
748
749 previous_gradient: Option<Array1<T>>,
753
754 previous_loss: Option<T>,
756
757 step_count: usize,
759}
760
761#[derive(Debug, Clone, Copy)]
763pub enum OptimizationPhase {
764 InitialDescent,
766
767 SteadyProgress,
769
770 FineTuning,
772
773 Converged,
775
776 Plateau,
778
779 Diverging,
781}
782
783#[derive(Debug, Clone)]
785pub struct ConvergenceIndicators<T: Float + Debug + Send + Sync + 'static> {
786 pub gradient_norm_trend: Vec<T>,
788
789 pub loss_change_trend: Vec<T>,
791
792 pub parameter_change_magnitude: T,
794
795 pub convergence_probability: T,
797
798 pub estimated_steps_to_convergence: Option<usize>,
800}
801
802#[derive(Debug, Clone)]
804pub struct GradientAnalyzer<T: Float + Debug + Send + Sync + 'static> {
805 pub gradient_stats: GradientStatistics<T>,
807
808 pub correlation_tracker: GradientCorrelationTracker<T>,
810
811 pub noise_estimator: GradientNoiseEstimator<T>,
813
814 pub flow_analyzer: GradientFlowAnalyzer<T>,
816}
817
818#[derive(Debug, Clone)]
820pub struct GradientStatistics<T: Float + Debug + Send + Sync + 'static> {
821 pub mean_norm: T,
823
824 pub norm_variance: T,
826
827 pub direction_consistency: T,
829
830 pub magnitude_distribution: Vec<T>,
832
833 pub component_stats: Array1<T>,
835}
836
837#[derive(Debug, Clone)]
839pub struct GradientCorrelationTracker<T: Float + Debug + Send + Sync + 'static> {
840 pub correlation_matrix: Array2<T>,
842
843 pub temporal_correlations: VecDeque<T>,
845
846 pub cross_correlations: HashMap<String, T>,
848}
849
850#[derive(Debug, Clone)]
852pub struct GradientNoiseEstimator<T: Float + Debug + Send + Sync + 'static> {
853 pub noise_level: T,
855
856 pub signal_to_noise_ratio: T,
858
859 pub noise_characteristics: NoiseCharacteristics<T>,
861}
862
863#[derive(Debug, Clone)]
865pub struct NoiseCharacteristics<T: Float + Debug + Send + Sync + 'static> {
866 pub noise_type: NoiseType,
868
869 pub scale: T,
871
872 pub temporal_correlation: T,
874
875 pub spatial_correlation: T,
877}
878
879#[derive(Debug, Clone, Copy)]
881pub enum NoiseType {
882 White,
884
885 Colored,
887
888 Structured,
890
891 Adaptive,
893}
894
895#[derive(Debug, Clone)]
897pub struct GradientFlowAnalyzer<T: Float + Debug + Send + Sync + 'static> {
898 pub flow_field: Array2<T>,
900
901 pub critical_points: Vec<Array1<T>>,
903
904 pub stability: FlowStability,
906
907 pub attractors: Vec<Array1<T>>,
909 pub repellers: Vec<Array1<T>>,
910}
911
912#[derive(Debug, Clone, Copy)]
914pub enum FlowStability {
915 Stable,
917
918 Unstable,
920
921 Chaotic,
923
924 Unknown,
926}
927
928#[derive(Debug, Clone)]
930pub struct LossLandscapeAnalyzer<T: Float + Debug + Send + Sync + 'static> {
931 pub local_curvature: T,
933
934 pub hessian_eigenvalues: Option<Array1<T>>,
936
937 pub roughness: T,
939
940 pub basin_size: T,
942
943 pub barrier_heights: Vec<T>,
945}
946
947#[derive(Debug, Clone)]
949pub struct StabilityMetrics<T: Float + Debug + Send + Sync + 'static> {
950 pub lyapunov_exponents: Array1<T>,
952
953 pub stability_margin: T,
955
956 pub perturbation_sensitivity: T,
958
959 pub robustness_score: T,
961}
962
963#[derive(Debug, Clone)]
965pub struct LSTMOptimizerMetrics {
966 pub meta_learning_loss: f64,
968
969 pub avg_convergence_speed: f64,
971
972 pub generalization_performance: f64,
974
975 pub adaptation_efficiency: f64,
977
978 pub transfer_success_rate: f64,
980
981 pub memory_usage_mb: f64,
983
984 pub computational_overhead: f64,
986
987 pub lstm_stats: LSTMNetworkStats,
989
990 pub attention_stats: Option<AttentionStats>,
992}
993
994#[derive(Debug, Clone)]
996pub struct LSTMNetworkStats {
997 pub gate_activations: GateActivationStats,
999
1000 pub hidden_state_stats: StateStatistics,
1002
1003 pub cell_state_stats: StateStatistics,
1005
1006 pub gradient_flow_stats: GradientFlowStats,
1008}
1009
1010#[derive(Debug, Clone)]
1012pub struct GateActivationStats {
1013 pub input_gate: StateStatistics,
1015
1016 pub forget_gate: StateStatistics,
1018
1019 pub output_gate: StateStatistics,
1021
1022 pub cell_gate: StateStatistics,
1024}
1025
1026#[derive(Debug, Clone)]
1028pub struct StateStatistics {
1029 pub mean: f64,
1031
1032 pub std: f64,
1034
1035 pub min: f64,
1037
1038 pub max: f64,
1040
1041 pub saturation_percent: f64,
1043}
1044
1045#[derive(Debug, Clone)]
1047pub struct GradientFlowStats {
1048 pub layer_gradient_norms: Vec<f64>,
1050
1051 pub layer_correlations: Vec<f64>,
1053
1054 pub vanishing_gradient_score: f64,
1056
1057 pub exploding_gradient_score: f64,
1059}
1060
1061#[derive(Debug, Clone)]
1063pub struct AttentionStats {
1064 pub attention_entropy: f64,
1066
1067 pub attention_concentration: f64,
1069
1070 pub head_diversity: f64,
1072
1073 pub temporal_patterns: Vec<f64>,
1075}
1076
1077impl<
1078 T: Float
1079 + Default
1080 + Clone
1081 + Send
1082 + Sync
1083 + std::iter::Sum
1084 + for<'a> std::iter::Sum<&'a T>
1085 + scirs2_core::ndarray::ScalarOperand
1086 + std::fmt::Debug,
1087 > LSTMOptimizer<T>
1088{
1089 pub fn new(config: LearnedOptimizerConfig) -> Result<Self> {
1091 Self::validate_config(&config)?;
1093
1094 let lstm_network = LSTMNetwork::new(&config)?;
1096
1097 let history_buffer = HistoryBuffer::new(config.gradient_history_size);
1099
1100 let meta_learner = MetaLearner::new(&config)?;
1102
1103 let lr_controller = AdaptiveLearningRateController::new(&config)?;
1105
1106 let state_tracker = OptimizationStateTracker::new();
1108
1109 let metrics = LSTMOptimizerMetrics::new();
1111
1112 Ok(Self {
1113 config,
1114 lstm_network,
1115 history_buffer,
1116 meta_learner,
1117 lr_controller,
1118 state_tracker,
1119 metrics,
1120 step_count: 0,
1121 })
1122 }
1123
1124 pub fn lstm_step<S, D>(
1126 &mut self,
1127 parameters: &ArrayBase<S, D>,
1128 gradients: &ArrayBase<S, D>,
1129 loss: Option<T>,
1130 ) -> Result<Array<T, D>>
1131 where
1132 S: Data<Elem = T>,
1133 D: Dimension + Clone,
1134 {
1135 let flat_params = self.flatten_to_1d(parameters)?;
1137 let flat_gradients = self.flatten_to_1d(gradients)?;
1138
1139 if self.lstm_network.output_projection.output_size() != flat_params.len() {
1146 self.lstm_network
1147 .output_projection
1148 .reset(self.config.hidden_size, flat_params.len());
1149 }
1150
1151 self.history_buffer
1153 .update(&flat_params, &flat_gradients, loss);
1154
1155 let lstm_input = self.prepare_lstm_input(&flat_gradients)?;
1157
1158 let lstm_output = self.lstm_network.forward(&lstm_input)?;
1160
1161 let learning_rate =
1163 self.lr_controller
1164 .compute_lr(&flat_gradients, loss, &self.history_buffer)?;
1165
1166 let updates = self.generate_updates(&lstm_output, &flat_gradients, learning_rate)?;
1168
1169 let updated_flat = &flat_params - &updates;
1171
1172 self.state_tracker.update(&flat_gradients, &updates, loss);
1174
1175 self.update_metrics(&flat_gradients, &updates, learning_rate);
1177
1178 let updated_params = self.reshape_from_1d(&updated_flat, parameters.raw_dim())?;
1180
1181 self.step_count += 1;
1182
1183 Ok(updated_params)
1184 }
1185
1186 pub fn meta_learning_step(&mut self, tasks: &[MetaTask<T>]) -> Result<T> {
1188 let meta_loss = self.meta_learner.step(tasks, &mut self.lstm_network)?;
1190
1191 self.metrics.meta_learning_loss = meta_loss.to_f64().unwrap_or(0.0);
1193
1194 Ok(meta_loss)
1195 }
1196
1197 pub fn transfer_to_domain(
1199 &mut self,
1200 target_tasks: &[MetaTask<T>],
1201 ) -> Result<TransferResults<T>> {
1202 self.meta_learner
1203 .transfer_learner
1204 .transfer_to_domain(target_tasks, &mut self.lstm_network)
1205 }
1206
1207 pub fn get_metrics(&self) -> &LSTMOptimizerMetrics {
1209 &self.metrics
1210 }
1211
1212 pub fn network(&self) -> &LSTMNetwork<T> {
1214 &self.lstm_network
1215 }
1216
1217 pub fn meta_learner(&self) -> &MetaLearner<T> {
1220 &self.meta_learner
1221 }
1222
1223 pub fn network_mut(&mut self) -> &mut LSTMNetwork<T> {
1229 &mut self.lstm_network
1230 }
1231
1232 pub fn current_learning_rate(&self) -> T {
1237 self.lr_controller.current_lr()
1238 }
1239
1240 pub fn stagnation_counter(&self) -> usize {
1243 self.lr_controller.stagnation_counter()
1244 }
1245
1246 pub fn get_state_analysis(&self) -> OptimizationStateAnalysis<T> {
1248 OptimizationStateAnalysis {
1249 current_phase: self.state_tracker.phase,
1250 convergence_indicators: self.state_tracker.convergence_indicators.clone(),
1251 gradient_analysis: self.state_tracker.gradient_analyzer.clone(),
1252 landscape_analysis: self.state_tracker.landscape_analyzer.clone(),
1253 stability_metrics: self.state_tracker.stability_metrics.clone(),
1254 }
1255 }
1256
1257 fn prepare_lstm_input(&self, gradients: &Array1<T>) -> Result<Array1<T>> {
1266 let recent = self
1267 .history_buffer
1268 .get_recent_gradients(5)
1269 .unwrap_or_default();
1270 let loss_features = self.history_buffer.get_loss_features();
1271 features::build_lstm_features(
1272 gradients,
1273 &recent,
1274 loss_features.as_deref(),
1275 self.config.input_features,
1276 )
1277 }
1278
1279 fn generate_updates(
1281 &self,
1282 lstm_output: &Array1<T>,
1283 gradients: &Array1<T>,
1284 learning_rate: T,
1285 ) -> Result<Array1<T>> {
1286 let transformed_output = match self.lstm_network.output_projection.output_transform {
1288 OutputTransform::Identity => lstm_output.clone(),
1289 OutputTransform::Tanh => lstm_output.mapv(|x| x.tanh()),
1290 OutputTransform::ScaledTanh { scale } => {
1291 let scale_t =
1292 scirs2_core::numeric::NumCast::from(scale).unwrap_or_else(|| T::zero());
1293 lstm_output.mapv(|x| x.tanh() * scale_t)
1294 }
1295 OutputTransform::AdaptiveScale => {
1296 let grad_norm = gradients.iter().map(|&g| g * g).sum::<T>().sqrt();
1297 let adaptive_scale = T::one() / (T::one() + grad_norm);
1298 lstm_output.mapv(|x| x * adaptive_scale)
1299 }
1300 OutputTransform::LearnedNonlinear => {
1301 lstm_output.mapv(|x| {
1303 let exp_x = x.exp();
1304 (exp_x - (-x).exp()) / (exp_x + (-x).exp()) })
1306 }
1307 };
1308
1309 let updates = &transformed_output * learning_rate;
1311
1312 Ok(updates)
1313 }
1314
1315 fn update_metrics(&mut self, gradients: &Array1<T>, updates: &Array1<T>, lr: T) {
1325 let grad_norm = gradients.iter().map(|&g| g * g).sum::<T>().sqrt();
1327 let update_norm = updates.iter().map(|&u| u * u).sum::<T>().sqrt();
1328
1329 self.update_lstm_stats();
1331
1332 let sgd_step_norm = lr.abs() * grad_norm;
1336 self.metrics.adaptation_efficiency = if sgd_step_norm > T::zero() {
1337 (update_norm / sgd_step_norm).to_f64().unwrap_or(1.0)
1338 } else {
1339 1.0
1340 };
1341
1342 self.metrics.computational_overhead = self.estimate_computational_overhead();
1344
1345 self.metrics.memory_usage_mb = self.estimate_memory_usage();
1347 }
1348
1349 fn update_lstm_stats(&mut self) {
1351 for layer in self.lstm_network.layers.iter() {
1353 let hidden_stats = self.compute_state_stats(&layer.hidden_state);
1354 let cell_stats = self.compute_state_stats(&layer.cell_state);
1355
1356 self.metrics.lstm_stats.hidden_state_stats = hidden_stats;
1358 self.metrics.lstm_stats.cell_state_stats = cell_stats;
1359 }
1360
1361 if let Some(ref attention) = self.lstm_network.attention {
1363 if let Some(ref attentionweights) = attention.attentionweights {
1364 self.metrics.attention_stats = Some(
1365 self.compute_attention_stats(attentionweights, attention.head_logits.as_ref()),
1366 );
1367 }
1368 }
1369 }
1370
1371 fn compute_state_stats(&self, state: &Array1<T>) -> StateStatistics {
1373 let values: Vec<f64> = state.iter().map(|&x| x.to_f64().unwrap_or(0.0)).collect();
1374
1375 let mean = values.iter().sum::<f64>() / values.len() as f64;
1376 let variance =
1377 values.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
1378 let std = variance.sqrt();
1379 let min = values.iter().cloned().fold(f64::INFINITY, f64::min);
1380 let max = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
1381
1382 let saturation_count = values.iter().filter(|&&x| x.abs() > 0.95).count();
1383 let saturation_percent = saturation_count as f64 / values.len() as f64 * 100.0;
1384
1385 StateStatistics {
1386 mean,
1387 std,
1388 min,
1389 max,
1390 saturation_percent,
1391 }
1392 }
1393
1394 fn compute_attention_stats(
1407 &self,
1408 attentionweights: &Array2<T>,
1409 head_logits: Option<&Array1<T>>,
1410 ) -> AttentionStats {
1411 let weights: Vec<f64> = attentionweights
1412 .iter()
1413 .map(|&w| w.to_f64().unwrap_or(0.0))
1414 .collect();
1415
1416 let entropy = weights
1418 .iter()
1419 .filter(|&&w| w > 0.0)
1420 .map(|&w| -w * w.ln())
1421 .sum::<f64>();
1422
1423 let concentration = 1.0 / (1.0 + entropy);
1425
1426 let logits: Vec<f64> = head_logits
1427 .map(|l| l.iter().map(|&x| x.to_f64().unwrap_or(0.0)).collect())
1428 .unwrap_or_default();
1429 let head_diversity = if logits.is_empty() {
1430 0.0
1431 } else {
1432 let mean = logits.iter().sum::<f64>() / logits.len() as f64;
1433 (logits.iter().map(|&x| (x - mean) * (x - mean)).sum::<f64>() / logits.len() as f64)
1434 .sqrt()
1435 };
1436
1437 AttentionStats {
1438 attention_entropy: entropy,
1439 attention_concentration: concentration,
1440 head_diversity,
1441 temporal_patterns: logits,
1442 }
1443 }
1444
1445 fn estimate_computational_overhead(&self) -> f64 {
1447 let lstm_overhead = self.config.num_layers as f64 * 0.1;
1449 let attention_overhead = if self.config.use_attention { 0.2 } else { 0.0 };
1450 let meta_learning_overhead = 0.1;
1451
1452 1.0 + lstm_overhead + attention_overhead + meta_learning_overhead
1453 }
1454
1455 fn estimate_memory_usage(&self) -> f64 {
1457 let parameter_memory =
1459 self.config.hidden_size as f64 * self.config.num_layers as f64 * 8.0 / 1024.0 / 1024.0;
1460 let history_memory =
1461 self.config.gradient_history_size as f64 * self.config.input_features as f64 * 8.0
1462 / 1024.0
1463 / 1024.0;
1464 let lstm_state_memory =
1465 self.config.hidden_size as f64 * self.config.num_layers as f64 * 2.0 * 8.0
1466 / 1024.0
1467 / 1024.0;
1468
1469 parameter_memory + history_memory + lstm_state_memory
1470 }
1471
1472 fn validate_config(config: &LearnedOptimizerConfig) -> Result<()> {
1474 if config.hidden_size == 0 {
1475 return Err(OptimError::InvalidConfig(
1476 "Hidden size must be positive".to_string(),
1477 ));
1478 }
1479
1480 if config.num_layers == 0 {
1481 return Err(OptimError::InvalidConfig(
1482 "Number of layers must be positive".to_string(),
1483 ));
1484 }
1485
1486 if config.input_features == 0 {
1487 return Err(OptimError::InvalidConfig(
1488 "Input features must be positive".to_string(),
1489 ));
1490 }
1491
1492 if config.meta_learning_rate <= 0.0 {
1493 return Err(OptimError::InvalidConfig(
1494 "Meta learning rate must be positive".to_string(),
1495 ));
1496 }
1497
1498 Ok(())
1499 }
1500
1501 fn flatten_to_1d<S, D>(&self, array: &ArrayBase<S, D>) -> Result<Array1<T>>
1503 where
1504 S: Data<Elem = T>,
1505 D: Dimension,
1506 {
1507 Ok(Array1::from_iter(array.iter().cloned()))
1508 }
1509
1510 fn reshape_from_1d<D>(&self, flat: &Array1<T>, shape: D) -> Result<Array<T, D>>
1511 where
1512 D: Dimension + Clone,
1513 {
1514 Array::from_shape_vec(shape, flat.to_vec())
1515 .map_err(|e| OptimError::InvalidConfig(format!("Reshape error: {}", e)))
1516 }
1517}
1518
1519impl<T: Float + Debug + Default + Clone + 'static + Send + Sync> LSTMNetwork<T> {
1522 pub fn new(config: &LearnedOptimizerConfig) -> Result<Self> {
1530 Self::new_seeded(config, thread_rng().random::<u64>())
1531 }
1532
1533 pub fn new_seeded(config: &LearnedOptimizerConfig, seed: u64) -> Result<Self> {
1540 let mut rng = seeded_rng(seed);
1541 let mut layers = Vec::new();
1542
1543 for i in 0..config.num_layers {
1545 let input_size = if i == 0 {
1546 config.input_features
1547 } else {
1548 config.hidden_size
1549 };
1550 let layer = LSTMLayer::new(input_size, config.hidden_size, &mut rng)?;
1551 layers.push(layer);
1552 }
1553
1554 let output_projection = OutputProjection::new_with_rng(
1556 config.hidden_size,
1557 config.output_features,
1558 OutputTransform::ScaledTanh { scale: 0.1 },
1559 &mut rng,
1560 )?;
1561
1562 let attention = if config.use_attention {
1564 Some(AttentionMechanism::new_with_rng(config, &mut rng)?)
1565 } else {
1566 None
1567 };
1568
1569 let layer_norms = (0..config.num_layers)
1571 .map(|_| LayerNormalization::new(config.hidden_size))
1572 .collect::<Result<Vec<_>>>()?;
1573
1574 Ok(Self {
1575 layers,
1576 output_projection,
1577 attention,
1578 layer_norms,
1579 dropout_rate: config.dropout_rate,
1580 training: false,
1581 })
1582 }
1583
1584 pub fn set_training(&mut self, training: bool) {
1590 self.training = training;
1591 }
1592
1593 pub fn is_training(&self) -> bool {
1595 self.training
1596 }
1597
1598 pub fn reset_state(&mut self) {
1604 for layer in self.layers.iter_mut() {
1605 layer.hidden_state.fill(T::zero());
1606 layer.cell_state.fill(T::zero());
1607 }
1608 }
1609
1610 pub fn forward(&mut self, input: &Array1<T>) -> Result<Array1<T>> {
1620 let mut current_input = input.clone();
1621
1622 for i in 0..self.layers.len() {
1624 current_input = self.layers[i].forward(¤t_input)?;
1625
1626 current_input = self.layer_norms[i].forward(¤t_input)?;
1628
1629 if self.training && self.dropout_rate > 0.0 {
1631 current_input = self.apply_dropout(¤t_input)?;
1632 }
1633 }
1634
1635 if let Some(ref mut attention) = self.attention {
1637 current_input = attention.forward(¤t_input)?;
1638 }
1639
1640 let output = self.output_projection.forward(¤t_input)?;
1642
1643 Ok(output)
1644 }
1645
1646 fn apply_dropout(&self, input: &Array1<T>) -> Result<Array1<T>> {
1657 if !(0.0..1.0).contains(&self.dropout_rate) {
1658 return Err(OptimError::InvalidConfig(format!(
1659 "dropout_rate must be in [0, 1), got {}",
1660 self.dropout_rate
1661 )));
1662 }
1663 if self.dropout_rate == 0.0 {
1664 return Ok(input.clone());
1665 }
1666 let threshold: T = cast_scalar(self.dropout_rate)?;
1667 let keep_scale: T = cast_scalar(1.0 - self.dropout_rate)?;
1668 let mut rng = scirs2_core::random::thread_rng();
1669 let mut out = input.clone();
1670 for value in out.iter_mut() {
1671 let sample: T = cast_scalar(rng.gen_range(0.0..1.0))?;
1672 *value = if sample < threshold {
1673 T::zero()
1674 } else {
1675 *value / keep_scale
1676 };
1677 }
1678 Ok(out)
1679 }
1680}
1681
1682impl<T: Float + Debug + Default + Clone + 'static + Send + Sync> LSTMLayer<T> {
1683 fn new(_input_size: usize, hiddensize: usize, rng: &mut CoreRandom<StdRng>) -> Result<Self> {
1685 let limit_ih = Self::xavier_limit(_input_size, hiddensize);
1692 let limit_hh = Self::xavier_limit(hiddensize, hiddensize);
1693
1694 Ok(Self {
1695 weight_ih: Self::xavier_init(rng, 4 * hiddensize, _input_size, limit_ih),
1696 weight_hh: Self::xavier_init(rng, 4 * hiddensize, hiddensize, limit_hh),
1697 bias_ih: Array1::zeros(4 * hiddensize),
1698 bias_hh: Array1::zeros(4 * hiddensize),
1699 hidden_state: Array1::zeros(hiddensize),
1700 cell_state: Array1::zeros(hiddensize),
1701 hiddensize,
1702 })
1703 }
1704
1705 fn forward(&mut self, input: &Array1<T>) -> Result<Array1<T>> {
1707 let ih_linear = self.weight_ih.dot(input) + &self.bias_ih;
1709 let hh_linear = self.weight_hh.dot(&self.hidden_state) + &self.bias_hh;
1710 let gates = ih_linear + hh_linear;
1711
1712 let input_gate = Self::sigmoid(&gates.slice(s![0..self.hiddensize]).to_owned());
1714 let forget_gate = Self::sigmoid(
1715 &gates
1716 .slice(s![self.hiddensize..2 * self.hiddensize])
1717 .to_owned(),
1718 );
1719 let cell_gate = Self::tanh(
1720 &gates
1721 .slice(s![2 * self.hiddensize..3 * self.hiddensize])
1722 .to_owned(),
1723 );
1724 let output_gate = Self::sigmoid(
1725 &gates
1726 .slice(s![3 * self.hiddensize..4 * self.hiddensize])
1727 .to_owned(),
1728 );
1729
1730 self.cell_state = &forget_gate * &self.cell_state + &input_gate * &cell_gate;
1732
1733 self.hidden_state = &output_gate * &Self::tanh(&self.cell_state);
1735
1736 Ok(self.hidden_state.clone())
1737 }
1738
1739 pub(crate) fn xavier_limit(fan_in: usize, fan_out: usize) -> f64 {
1749 (6.0 / (fan_in + fan_out).max(1) as f64).sqrt()
1750 }
1751
1752 fn xavier_init(
1759 rng: &mut CoreRandom<StdRng>,
1760 rows: usize,
1761 cols: usize,
1762 limit: f64,
1763 ) -> Array2<T> {
1764 Array2::from_shape_fn((rows, cols), |_| {
1765 let val = (rng.gen_range(0.0..1.0) - 0.5) * 2.0 * limit;
1766 scirs2_core::numeric::NumCast::from(val).unwrap_or_else(|| T::zero())
1767 })
1768 }
1769
1770 fn sigmoid(x: &Array1<T>) -> Array1<T> {
1772 x.mapv(|xi| T::one() / (T::one() + (-xi).exp()))
1773 }
1774
1775 fn tanh(x: &Array1<T>) -> Array1<T> {
1777 x.mapv(|xi| xi.tanh())
1778 }
1779}
1780
1781impl<T: Float + Debug + Send + Sync + 'static + Default + Clone> HistoryBuffer<T> {
1782 fn new(_maxlength: usize) -> Self {
1784 Self {
1785 gradients: VecDeque::with_capacity(_maxlength),
1786 parameters: VecDeque::with_capacity(_maxlength),
1787 losses: VecDeque::with_capacity(_maxlength),
1788 _maxlength,
1789 feature_cache: None,
1790 }
1791 }
1792
1793 fn update(&mut self, params: &Array1<T>, grads: &Array1<T>, loss: Option<T>) {
1795 self.parameters.push_back(params.clone());
1797 self.gradients.push_back(grads.clone());
1798
1799 if let Some(l) = loss {
1800 self.losses.push_back(l);
1801 }
1802
1803 while self.parameters.len() > self._maxlength {
1805 self.parameters.pop_front();
1806 }
1807 while self.gradients.len() > self._maxlength {
1808 self.gradients.pop_front();
1809 }
1810 while self.losses.len() > self._maxlength {
1811 self.losses.pop_front();
1812 }
1813
1814 self.feature_cache = None;
1816 }
1817
1818 fn get_recent_gradients(&self, count: usize) -> Option<Vec<&Array1<T>>> {
1820 if self.gradients.len() < count {
1821 return None;
1822 }
1823
1824 Some(self.gradients.iter().rev().take(count).collect())
1825 }
1826
1827 fn get_loss_features(&self) -> Option<Vec<T>> {
1829 if self.losses.len() < 2 {
1830 return None;
1831 }
1832
1833 let current_loss = *self.losses.back()?;
1837 let prev_loss = *self.losses.get(self.losses.len() - 2)?;
1838
1839 let loss_change = current_loss - prev_loss;
1840 let loss_ratio = if prev_loss.abs()
1841 > scirs2_core::numeric::NumCast::from(1e-8).unwrap_or_else(|| T::zero())
1842 {
1843 current_loss / prev_loss
1844 } else {
1845 T::one()
1846 };
1847
1848 Some(vec![loss_change, loss_ratio])
1849 }
1850}
1851
1852#[derive(Debug, Clone)]
1855pub struct OptimizationStateAnalysis<T: Float + Debug + Send + Sync + 'static> {
1856 pub current_phase: OptimizationPhase,
1857 pub convergence_indicators: ConvergenceIndicators<T>,
1858 pub gradient_analysis: GradientAnalyzer<T>,
1859 pub landscape_analysis: LossLandscapeAnalyzer<T>,
1860 pub stability_metrics: StabilityMetrics<T>,
1861}
1862
1863#[derive(Debug, Clone)]
1865pub struct TransferResults<T: Float + Debug + Send + Sync + 'static> {
1866 pub initial_performance: T,
1867 pub final_performance: T,
1868 pub adaptation_steps: usize,
1869 pub transfer_efficiency: T,
1870}
1871
1872impl Default for LSTMOptimizerMetrics {
1875 fn default() -> Self {
1876 Self::new()
1877 }
1878}
1879
1880impl LSTMOptimizerMetrics {
1881 fn new() -> Self {
1882 Self {
1883 meta_learning_loss: 0.0,
1884 avg_convergence_speed: 0.0,
1885 generalization_performance: 0.0,
1886 adaptation_efficiency: 0.0,
1887 transfer_success_rate: 0.0,
1888 memory_usage_mb: 0.0,
1889 computational_overhead: 1.0,
1890 lstm_stats: LSTMNetworkStats {
1891 gate_activations: GateActivationStats {
1892 input_gate: StateStatistics::default(),
1893 forget_gate: StateStatistics::default(),
1894 output_gate: StateStatistics::default(),
1895 cell_gate: StateStatistics::default(),
1896 },
1897 hidden_state_stats: StateStatistics::default(),
1898 cell_state_stats: StateStatistics::default(),
1899 gradient_flow_stats: GradientFlowStats {
1900 layer_gradient_norms: Vec::new(),
1901 layer_correlations: Vec::new(),
1902 vanishing_gradient_score: 0.0,
1903 exploding_gradient_score: 0.0,
1904 },
1905 },
1906 attention_stats: None,
1907 }
1908 }
1909}
1910
1911impl Default for StateStatistics {
1912 fn default() -> Self {
1913 Self {
1914 mean: 0.0,
1915 std: 0.0,
1916 min: 0.0,
1917 max: 0.0,
1918 saturation_percent: 0.0,
1919 }
1920 }
1921}
1922
1923#[cfg(test)]
1924mod tests {
1925 use super::*;
1926
1927 #[test]
1928 fn test_lstm_optimizer_creation() {
1929 let config = LearnedOptimizerConfig::default();
1930 let optimizer = LSTMOptimizer::<f64>::new(config);
1931 assert!(optimizer.is_ok());
1932 }
1933
1934 #[test]
1935 fn test_lstm_layer_creation() {
1936 let layer = LSTMLayer::<f64>::new(10, 20, &mut seeded_rng(7));
1937 assert!(layer.is_ok());
1938
1939 let layer = layer.expect("LSTMLayer::new should succeed");
1940 assert_eq!(layer.hiddensize, 20);
1941 assert_eq!(layer.weight_ih.shape(), &[80, 10]); assert_eq!(layer.weight_hh.shape(), &[80, 20]); }
1944
1945 #[test]
1946 fn test_history_buffer() {
1947 let mut buffer = HistoryBuffer::<f64>::new(5);
1948
1949 let params = Array1::from_vec(vec![1.0, 2.0, 3.0]);
1950 let grads = Array1::from_vec(vec![0.1, 0.2, 0.3]);
1951
1952 buffer.update(¶ms, &grads, Some(0.5));
1953
1954 assert_eq!(buffer.gradients.len(), 1);
1955 assert_eq!(buffer.parameters.len(), 1);
1956 assert_eq!(buffer.losses.len(), 1);
1957 }
1958
1959 #[test]
1960 fn test_config_validation() {
1961 let mut config = LearnedOptimizerConfig::default();
1962 assert!(LSTMOptimizer::<f64>::validate_config(&config).is_ok());
1963
1964 config.hidden_size = 0;
1965 assert!(LSTMOptimizer::<f64>::validate_config(&config).is_err());
1966 }
1967
1968 #[test]
1969 fn test_lstm_network_creation() {
1970 let config = LearnedOptimizerConfig::default();
1971 let network = LSTMNetwork::<f64>::new(&config);
1972 assert!(network.is_ok());
1973
1974 let network = network.expect("LSTMNetwork::new should succeed");
1975 assert_eq!(network.layers.len(), config.num_layers);
1976 assert!(network.attention.is_some()); }
1978
1979 #[test]
1980 fn test_metrics_initialization() {
1981 let metrics = LSTMOptimizerMetrics::new();
1982 assert_eq!(metrics.meta_learning_loss, 0.0);
1983 assert_eq!(metrics.computational_overhead, 1.0);
1984 assert!(metrics.attention_stats.is_none());
1985 }
1986}