optirs_learned/adaptive/
performance_predictor.rs1use scirs2_core::numeric::Float;
12use std::fmt::Debug;
13
14use crate::error::Result;
15
16use super::predictor::{
17 PredictionCache, PredictionFeatures, PredictorFitReport, PredictorNetwork, PredictorSample,
18 PREDICTION_FEATURE_COUNT,
19};
20use super::types::{
21 ArchitectureAdaptation, ArchitectureChange, LandscapeAnalysis, OptimizationStrategy,
22 PerformancePrediction, UncertaintyMethod,
23};
24use super::AdaptiveConfig;
25
26#[derive(Debug)]
37pub struct TransformerPerformancePredictor<
38 T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static,
39> {
40 predictor_network: PredictorNetwork<T>,
42 prediction_cache: PredictionCache,
44 uncertainty_method: UncertaintyMethod,
46 prediction_horizon: usize,
49}
50impl<T: Float + Debug + scirs2_core::ndarray::ScalarOperand + Send + Sync + 'static>
51 TransformerPerformancePredictor<T>
52{
53 pub fn new(config: &AdaptiveConfig<T>) -> Result<Self> {
60 let horizon = config.prediction_horizon.max(1);
61 let hidden = (2 * PREDICTION_FEATURE_COUNT).max(horizon).min(128);
65 let feature_width = PREDICTION_FEATURE_COUNT.max(horizon.min(64));
66 let ridge = {
67 let lr = config.adaptation_lr.to_f64().unwrap_or(0.0).abs();
68 let lambda = if lr > 0.0 { lr * 1e-2 } else { 1e-4 };
69 scirs2_core::numeric::NumCast::from(lambda.clamp(1e-8, 1.0)).unwrap_or_else(|| T::one())
70 };
71 Ok(Self {
72 predictor_network: PredictorNetwork::new(
73 vec![PREDICTION_FEATURE_COUNT, hidden, feature_width],
74 ridge,
75 )?,
76 prediction_cache: PredictionCache::new(8 * horizon),
77 uncertainty_method: UncertaintyMethod::Ensemble,
78 prediction_horizon: horizon,
79 })
80 }
81
82 pub fn train(&mut self, samples: &[PredictorSample]) -> Result<PredictorFitReport> {
85 self.prediction_cache.clear();
86 self.predictor_network.fit(samples)
87 }
88
89 pub fn is_trained(&self) -> bool {
91 self.predictor_network.is_trained()
92 }
93
94 pub fn cache_hit_rate(&self) -> f64 {
96 self.prediction_cache.hit_rate()
97 }
98
99 pub fn uncertainty_method(&self) -> UncertaintyMethod {
101 self.uncertainty_method
102 }
103
104 pub fn prediction_horizon(&self) -> usize {
106 self.prediction_horizon
107 }
108
109 pub fn extract_features(
112 landscape: &LandscapeAnalysis<T>,
113 adaptation: &ArchitectureAdaptation<T>,
114 ) -> PredictionFeatures {
115 let complexity = landscape.complexity.to_f64().unwrap_or(0.0);
116 let difficulty = landscape.difficulty.to_f64().unwrap_or(0.0);
117
118 let mut conservative = 0.0;
120 let mut aggressive = 0.0;
121 let mut exploratory = 0.0;
122 for strategy in &landscape.recommended_strategies {
123 match strategy {
124 OptimizationStrategy::Conservative | OptimizationStrategy::Exploitative => {
125 conservative += 1.0
126 }
127 OptimizationStrategy::Aggressive => aggressive += 1.0,
128 OptimizationStrategy::Exploratory => exploratory += 1.0,
129 OptimizationStrategy::Adaptive => {
130 conservative += 0.5;
132 aggressive += 0.5;
133 }
134 }
135 }
136 let strategy_total = (conservative + aggressive + exploratory).max(1.0);
137
138 let mut layers = adaptation.adapted_config.num_transformer_layers as f64;
142 let mut hidden = adaptation.adapted_config.model_dimension as f64;
143 let mut heads = adaptation.adapted_config.num_attention_heads as f64;
144 let mut dropout = adaptation.adapted_config.dropout_rate;
145 for change in &adaptation.changes {
146 match change {
147 ArchitectureChange::LayerCountChange(n) => layers = *n as f64,
148 ArchitectureChange::HiddenSizeChange(n) => hidden = *n as f64,
149 ArchitectureChange::AttentionHeadChange(n) => heads = *n as f64,
150 ArchitectureChange::DropoutChange(d) => dropout = *d,
151 ArchitectureChange::ActivationChange(_) => {}
152 }
153 }
154
155 PredictionFeatures {
156 complexity,
157 difficulty,
158 landscape_confidence: landscape.confidence.to_f64().unwrap_or(0.0),
159 expected_improvement: adaptation.expected_improvement.to_f64().unwrap_or(0.0),
160 adaptation_confidence: adaptation.confidence.to_f64().unwrap_or(0.0),
161 change_count: (adaptation.changes.len() as f64 / 8.0).min(1.0),
162 layer_count: (layers / 24.0).min(1.0),
163 hidden_size: (hidden / 2048.0).min(1.0),
164 head_count: (heads / 32.0).min(1.0),
165 dropout: dropout.clamp(0.0, 1.0),
166 interaction: complexity * difficulty,
167 complexity_sq: complexity * complexity,
168 difficulty_sq: difficulty * difficulty,
169 strategy_conservative: conservative / strategy_total,
170 strategy_aggressive: aggressive / strategy_total,
171 strategy_exploratory: exploratory / strategy_total,
172 }
173 }
174
175 pub fn predict_improvement(
176 &mut self,
177 landscape: &LandscapeAnalysis<T>,
178 adaptation: &ArchitectureAdaptation<T>,
179 ) -> Result<PerformancePrediction<T>> {
180 let features = Self::extract_features(landscape, adaptation);
181 let key = features.cache_key();
182 let output = match self.prediction_cache.get(&key) {
183 Some(cached) => cached,
184 None => {
185 let fresh = self.predictor_network.predict(&features)?;
186 self.prediction_cache.insert(key, fresh.clone());
187 fresh
188 }
189 };
190
191 let cast =
192 |v: f64| -> T { scirs2_core::numeric::NumCast::from(v).unwrap_or_else(|| T::zero()) };
193 Ok(PerformancePrediction {
194 convergence_improvement: cast(output.convergence_improvement),
195 final_performance: cast(output.final_performance),
196 confidence: cast(output.confidence),
197 uncertainty: cast(output.uncertainty),
198 })
199 }
200}