Skip to main content

scirs2_optimize/reinforcement_learning/
policy_gradient.rs

1//! Advanced Policy Gradient Optimization with Meta-Gradient Learning
2//!
3//! Implementation of cutting-edge policy gradient methods with meta-learning capabilities:
4//! - Meta-gradient learning for automatic learning rate adaptation
5//! - Higher-order optimization dynamics
6//! - Meta-policy networks for learning optimization strategies
7//! - Adaptive curriculum learning across problem classes
8//! - Hierarchical optimization policies
9
10use super::{
11    utils, Experience, ImprovementReward, OptimizationAction, OptimizationState,
12    RLOptimizationConfig, RLOptimizer, RewardFunction,
13};
14use crate::error::{OptimizeError, OptimizeResult};
15use crate::result::OptimizeResults;
16use scirs2_core::ndarray::{Array1, Array2, Array3, ArrayView1};
17// use scirs2_core::error::CoreResult; // Unused import
18// use scirs2_core::simd_ops::SimdUnifiedOps; // Unused import
19use scirs2_core::random::{rng, Rng, RngExt};
20use std::collections::{HashMap, VecDeque};
21
22/// Advanced Neural Network with Meta-Learning Capabilities
23#[derive(Debug, Clone)]
24pub struct MetaPolicyNetwork {
25    /// Primary policy weights
26    pub policy_weights: Array3<f64>, // [layer, output, input]
27    /// Meta-policy weights for learning rate adaptation
28    pub meta_weights: Array3<f64>,
29    /// Bias terms
30    pub policy_bias: Array2<f64>, // [layer, neuron]
31    pub meta_bias: Array2<f64>,
32    /// Network architecture
33    pub layer_sizes: Vec<usize>,
34    /// Adaptive learning rates per parameter
35    pub adaptive_learning_rates: Array2<f64>,
36    /// Meta-gradient accumulator
37    pub meta_gradient_accumulator: Array3<f64>,
38    /// Higher-order derivative tracking
39    pub second_order_info: Array3<f64>,
40    /// Curriculum learning difficulty
41    pub curriculum_difficulty: f64,
42    /// Problem class embeddings
43    pub problem_embeddings: HashMap<String, Array1<f64>>,
44}
45
46impl MetaPolicyNetwork {
47    /// Create new meta-policy network with hierarchical structure
48    pub fn new(_input_size: usize, output_size: usize, hidden_sizes: Vec<usize>) -> Self {
49        let mut layer_sizes = vec![_input_size];
50        layer_sizes.extend(hidden_sizes);
51        layer_sizes.push(output_size);
52
53        let num_layers = layer_sizes.len() - 1;
54        let max_layer_size = *layer_sizes.iter().max().expect("Operation failed");
55
56        // Initialize weights with Xavier initialization
57        let mut policy_weights = Array3::zeros((num_layers, max_layer_size, max_layer_size));
58        let mut meta_weights = Array3::zeros((num_layers, max_layer_size, max_layer_size));
59
60        for layer in 0..num_layers {
61            let fan_in = layer_sizes[layer];
62            let fan_out = layer_sizes[layer + 1];
63            let xavier_std = (2.0 / (fan_in + fan_out) as f64).sqrt();
64
65            for i in 0..fan_out {
66                for j in 0..fan_in {
67                    policy_weights[[layer, i, j]] =
68                        scirs2_core::random::rng().random_range(-0.5..0.5) * 2.0 * xavier_std;
69                    meta_weights[[layer, i, j]] =
70                        scirs2_core::random::rng().random_range(-0.5..0.5) * 2.0 * xavier_std * 0.1;
71                }
72            }
73        }
74
75        Self {
76            policy_weights,
77            meta_weights,
78            policy_bias: Array2::zeros((num_layers, max_layer_size)),
79            meta_bias: Array2::zeros((num_layers, max_layer_size)),
80            layer_sizes,
81            adaptive_learning_rates: Array2::from_elem((num_layers, max_layer_size), 0.01),
82            meta_gradient_accumulator: Array3::zeros((num_layers, max_layer_size, max_layer_size)),
83            second_order_info: Array3::zeros((num_layers, max_layer_size, max_layer_size)),
84            curriculum_difficulty: 0.1,
85            problem_embeddings: HashMap::new(),
86        }
87    }
88
89    /// Forward pass with meta-learning augmentation
90    pub fn meta_forward(
91        &mut self,
92        state_features: &ArrayView1<f64>,
93        problem_class: &str,
94        meta_context: &Array1<f64>,
95    ) -> (Array1<f64>, Array1<f64>) {
96        // Get or create problem embedding
97        let problem_embedding =
98            self.get_or_create_problem_embedding(problem_class, state_features.len());
99
100        // Combine input with problem embedding and meta-_context
101        let mut augmented_input = state_features.to_owned();
102
103        // Add problem-specific _context
104        for (i, &emb) in problem_embedding.iter().enumerate() {
105            if i < augmented_input.len() {
106                augmented_input[i] += emb * 0.1;
107            }
108        }
109
110        // Forward pass through policy network
111        let policy_output = self.forward_policy(&augmented_input.view());
112
113        // Forward pass through meta-network for learning rate adaptation
114        let meta_output = self.forward_meta(&augmented_input.view(), meta_context);
115
116        (policy_output, meta_output)
117    }
118
119    fn forward_policy(&self, input: &ArrayView1<f64>) -> Array1<f64> {
120        let mut current_input = input.to_owned();
121
122        for layer in 0..(self.layer_sizes.len() - 1) {
123            let layer_input_size = self.layer_sizes[layer];
124            let layer_output_size = self.layer_sizes[layer + 1];
125
126            let mut layer_output = Array1::<f64>::zeros(layer_output_size);
127
128            for i in 0..layer_output_size {
129                for j in 0..layer_input_size.min(current_input.len()) {
130                    layer_output[i] += self.policy_weights[[layer, i, j]] * current_input[j];
131                }
132                layer_output[i] += self.policy_bias[[layer, i]];
133
134                // Apply activation function (ELU for smooth gradients)
135                layer_output[i] = if layer_output[i] > 0.0 {
136                    layer_output[i]
137                } else {
138                    layer_output[i].exp() - 1.0
139                };
140            }
141
142            current_input = layer_output;
143        }
144
145        current_input
146    }
147
148    fn forward_meta(&self, input: &ArrayView1<f64>, metacontext: &Array1<f64>) -> Array1<f64> {
149        // Combine input with meta-_context
150        let mut meta_input = input.to_owned();
151        for (i, &ctx) in metacontext.iter().enumerate() {
152            if i < meta_input.len() {
153                meta_input[i] += ctx * 0.05;
154            }
155        }
156
157        let mut current_input = meta_input;
158
159        for layer in 0..(self.layer_sizes.len() - 1) {
160            let layer_input_size = self.layer_sizes[layer];
161            let layer_output_size = self.layer_sizes[layer + 1];
162
163            let mut layer_output = Array1::<f64>::zeros(layer_output_size);
164
165            for i in 0..layer_output_size {
166                for j in 0..layer_input_size.min(current_input.len()) {
167                    layer_output[i] += self.meta_weights[[layer, i, j]] * current_input[j];
168                }
169                layer_output[i] += self.meta_bias[[layer, i]];
170
171                // Sigmoid activation for learning rate scaling
172                layer_output[i] = 1.0 / (1.0 + (-layer_output[i]).exp());
173            }
174
175            current_input = layer_output;
176        }
177
178        current_input
179    }
180
181    fn get_or_create_problem_embedding(
182        &mut self,
183        problem_class: &str,
184        input_size: usize,
185    ) -> Array1<f64> {
186        if let Some(embedding) = self.problem_embeddings.get(problem_class) {
187            embedding.clone()
188        } else {
189            let embedding = Array1::from_shape_fn(input_size, |_| {
190                scirs2_core::random::rng().random_range(-0.05..0.05)
191            });
192            self.problem_embeddings
193                .insert(problem_class.to_string(), embedding.clone());
194            embedding
195        }
196    }
197
198    /// Update network using meta-gradients
199    pub fn meta_update(
200        &mut self,
201        meta_gradients: &MetaGradients,
202        base_learning_rate: f64,
203        meta_learning_rate: f64,
204    ) {
205        // Update adaptive learning rates using meta-_gradients
206        for layer in 0..(self.layer_sizes.len() - 1) {
207            for i in 0..self.layer_sizes[layer + 1] {
208                for j in 0..self.layer_sizes[layer] {
209                    // Meta-gradient update for learning rates
210                    let meta_grad = meta_gradients.meta_lr_gradients[[layer, i, j]];
211                    self.adaptive_learning_rates[[layer, i]] *=
212                        (1.0 + meta_learning_rate * meta_grad).max(0.1).min(10.0);
213
214                    // Policy weight update with adaptive learning _rate
215                    let adaptive_lr = self.adaptive_learning_rates[[layer, i]] * base_learning_rate;
216                    self.policy_weights[[layer, i, j]] +=
217                        adaptive_lr * meta_gradients.policy_gradients[[layer, i, j]];
218
219                    // Meta-weight update
220                    self.meta_weights[[layer, i, j]] +=
221                        meta_learning_rate * meta_gradients.meta_weight_gradients[[layer, i, j]];
222                }
223
224                // Bias updates
225                let adaptive_lr = self.adaptive_learning_rates[[layer, i]] * base_learning_rate;
226                self.policy_bias[[layer, i]] +=
227                    adaptive_lr * meta_gradients.policy_bias_gradients[[layer, i]];
228                self.meta_bias[[layer, i]] +=
229                    meta_learning_rate * meta_gradients.meta_bias_gradients[[layer, i]];
230            }
231        }
232
233        // Update curriculum difficulty based on meta-learning progress
234        self.update_curriculum_difficulty(meta_gradients);
235    }
236
237    fn update_curriculum_difficulty(&mut self, metagradients: &MetaGradients) {
238        let gradient_norm = metagradients
239            .policy_gradients
240            .iter()
241            .map(|&g| g * g)
242            .sum::<f64>()
243            .sqrt();
244
245        if gradient_norm < 0.1 {
246            self.curriculum_difficulty = (self.curriculum_difficulty * 1.05).min(1.0);
247        } else if gradient_norm > 1.0 {
248            self.curriculum_difficulty = (self.curriculum_difficulty * 0.95).max(0.01);
249        }
250    }
251}
252
253/// Meta-gradients for higher-order optimization
254#[derive(Debug, Clone)]
255pub struct MetaGradients {
256    /// Gradients for policy parameters
257    pub policy_gradients: Array3<f64>,
258    /// Gradients for meta-parameters
259    pub meta_weight_gradients: Array3<f64>,
260    /// Gradients for learning rates (meta-gradients)
261    pub meta_lr_gradients: Array3<f64>,
262    /// Bias gradients
263    pub policy_bias_gradients: Array2<f64>,
264    pub meta_bias_gradients: Array2<f64>,
265    /// Higher-order terms
266    pub second_order_terms: Array3<f64>,
267}
268
269/// Advanced Policy Gradient Optimizer with Meta-Learning
270#[derive(Debug, Clone)]
271pub struct AdvancedAdvancedPolicyGradientOptimizer {
272    /// Configuration
273    config: RLOptimizationConfig,
274    /// Meta-policy network
275    meta_policy: MetaPolicyNetwork,
276    /// Reward function
277    reward_function: ImprovementReward,
278    /// Episode trajectories for meta-learning
279    meta_trajectories: VecDeque<MetaTrajectory>,
280    /// Problem class history
281    problem_class_history: VecDeque<String>,
282    /// Best solution tracking
283    best_params: Array1<f64>,
284    best_objective: f64,
285    /// Meta-learning statistics
286    meta_stats: MetaLearningStats,
287    /// Curriculum learning controller
288    curriculum_controller: CurriculumController,
289    /// Experience replay buffer for meta-learning
290    meta_experience_buffer: MetaExperienceBuffer,
291}
292
293/// Enhanced trajectory with meta-learning information
294#[derive(Debug, Clone)]
295pub struct MetaTrajectory {
296    /// Regular experiences
297    pub experiences: Vec<Experience>,
298    /// Problem class identifier
299    pub problem_class: String,
300    /// Meta-context at start of trajectory
301    pub initial_meta_context: Array1<f64>,
302    /// Learning progress measures
303    pub learning_metrics: LearningMetrics,
304    /// Adaptation speed
305    pub adaptation_speed: f64,
306}
307
308/// Learning metrics for meta-learning
309#[derive(Debug, Clone)]
310pub struct LearningMetrics {
311    /// Rate of improvement
312    pub improvement_rate: f64,
313    /// Convergence speed
314    pub convergence_speed: f64,
315    /// Exploration efficiency
316    pub exploration_efficiency: f64,
317    /// Generalization measure
318    pub generalization_score: f64,
319}
320
321/// Meta-learning statistics
322#[derive(Debug, Clone)]
323pub struct MetaLearningStats {
324    /// Average learning rates across parameters
325    pub avg_learning_rates: Array1<f64>,
326    /// Meta-gradient norms
327    pub meta_gradient_norms: VecDeque<f64>,
328    /// Problem class performance
329    pub problem_class_performance: HashMap<String, f64>,
330    /// Curriculum progress
331    pub curriculum_progress: f64,
332    /// Adaptation efficiency
333    pub adaptation_efficiency: f64,
334}
335
336/// Curriculum learning controller
337#[derive(Debug, Clone)]
338pub struct CurriculumController {
339    /// Current difficulty level
340    pub difficulty_level: f64,
341    /// Performance thresholds for advancement
342    pub advancement_thresholds: Vec<f64>,
343    /// Problem generators for different difficulties
344    pub difficulty_generators: HashMap<String, f64>,
345    /// Learning progress tracker
346    pub progress_tracker: VecDeque<f64>,
347}
348
349impl Default for CurriculumController {
350    fn default() -> Self {
351        Self::new()
352    }
353}
354
355impl CurriculumController {
356    pub fn new() -> Self {
357        Self {
358            difficulty_level: 0.1,
359            advancement_thresholds: vec![0.8, 0.85, 0.9, 0.95],
360            difficulty_generators: HashMap::new(),
361            progress_tracker: VecDeque::with_capacity(100),
362        }
363    }
364
365    pub fn should_advance(&self) -> bool {
366        if self.progress_tracker.len() < 20 {
367            return false;
368        }
369
370        let recent_performance: f64 =
371            self.progress_tracker.iter().rev().take(20).sum::<f64>() / 20.0;
372
373        let threshold_idx = ((self.difficulty_level * 4.0) as usize).min(3);
374        recent_performance > self.advancement_thresholds[threshold_idx]
375    }
376
377    pub fn advance_difficulty(&mut self) {
378        self.difficulty_level = (self.difficulty_level * 1.2).min(1.0);
379    }
380
381    pub fn update_progress(&mut self, performance: f64) {
382        self.progress_tracker.push_back(performance);
383        if self.progress_tracker.len() > 100 {
384            self.progress_tracker.pop_front();
385        }
386
387        if self.should_advance() {
388            self.advance_difficulty();
389        }
390    }
391}
392
393/// Meta-experience buffer for higher-order learning
394#[derive(Debug, Clone)]
395pub struct MetaExperienceBuffer {
396    /// Buffer of meta-trajectories
397    pub trajectories: VecDeque<MetaTrajectory>,
398    /// Maximum buffer size
399    pub max_size: usize,
400    /// Sampling weights for different problem classes
401    pub class_weights: HashMap<String, f64>,
402}
403
404impl MetaExperienceBuffer {
405    pub fn new(_maxsize: usize) -> Self {
406        Self {
407            trajectories: VecDeque::with_capacity(_maxsize),
408            max_size: _maxsize,
409            class_weights: HashMap::new(),
410        }
411    }
412
413    pub fn add_trajectory(&mut self, trajectory: MetaTrajectory) {
414        // Update class weights based on performance
415        let avg_reward = trajectory.experiences.iter().map(|e| e.reward).sum::<f64>()
416            / trajectory.experiences.len().max(1) as f64;
417
418        *self
419            .class_weights
420            .entry(trajectory.problem_class.clone())
421            .or_insert(1.0) *= if avg_reward > 0.0 { 1.05 } else { 0.95 };
422
423        self.trajectories.push_back(trajectory);
424        if self.trajectories.len() > self.max_size {
425            self.trajectories.pop_front();
426        }
427    }
428
429    pub fn sample_meta_batch(&self, batchsize: usize) -> Vec<MetaTrajectory> {
430        let mut batch = Vec::new();
431
432        for _ in 0..batchsize.min(self.trajectories.len()) {
433            // Weighted sampling based on problem class performance
434            let idx = scirs2_core::random::rng().random_range(0..self.trajectories.len());
435            if let Some(trajectory) = self.trajectories.get(idx) {
436                batch.push(trajectory.clone());
437            }
438        }
439
440        batch
441    }
442}
443
444impl AdvancedAdvancedPolicyGradientOptimizer {
445    /// Create new advanced policy gradient optimizer
446    pub fn new(config: RLOptimizationConfig, state_size: usize, actionsize: usize) -> Self {
447        let hidden_sizes = vec![state_size * 2, state_size * 3, state_size * 2];
448        let meta_policy = MetaPolicyNetwork::new(state_size, actionsize, hidden_sizes);
449
450        Self {
451            config,
452            meta_policy,
453            reward_function: ImprovementReward::default(),
454            meta_trajectories: VecDeque::with_capacity(1000),
455            problem_class_history: VecDeque::with_capacity(100),
456            best_params: Array1::zeros(state_size),
457            best_objective: f64::INFINITY,
458            meta_stats: MetaLearningStats {
459                avg_learning_rates: Array1::zeros(state_size),
460                meta_gradient_norms: VecDeque::with_capacity(1000),
461                problem_class_performance: HashMap::new(),
462                curriculum_progress: 0.0,
463                adaptation_efficiency: 1.0,
464            },
465            curriculum_controller: CurriculumController::new(),
466            meta_experience_buffer: MetaExperienceBuffer::new(500),
467        }
468    }
469
470    /// Extract advanced state features with meta-learning context
471    fn extract_meta_state_features(
472        &self,
473        state: &OptimizationState,
474        problem_class: &str,
475    ) -> (Array1<f64>, Array1<f64>) {
476        let mut base_features = Vec::new();
477
478        // Basic parameter features
479        for &param in state.parameters.iter() {
480            base_features.push(param.tanh());
481        }
482
483        // Objective and convergence features
484        base_features.push((state.objective_value / (state.objective_value.abs() + 1.0)).tanh());
485        base_features.push(
486            state
487                .convergence_metrics
488                .relative_objective_change
489                .ln()
490                .max(-10.0)
491                .tanh(),
492        );
493        base_features.push(state.convergence_metrics.parameter_change_norm.tanh());
494
495        // Step and temporal features
496        base_features.push((state.step as f64 / 100.0).tanh());
497
498        // Problem-specific features
499        let problem_difficulty = self.meta_policy.curriculum_difficulty;
500        base_features.push(problem_difficulty);
501
502        // Meta-context features
503        let mut meta_context = Vec::new();
504
505        // Historical performance for this problem _class
506        let class_performance = self
507            .meta_stats
508            .problem_class_performance
509            .get(problem_class)
510            .copied()
511            .unwrap_or(0.0);
512        meta_context.push(class_performance);
513
514        // Recent meta-gradient norms
515        let recent_meta_grad_norm = self
516            .meta_stats
517            .meta_gradient_norms
518            .iter()
519            .rev()
520            .take(10)
521            .sum::<f64>()
522            / 10.0;
523        meta_context.push(recent_meta_grad_norm.tanh());
524
525        // Curriculum progress
526        meta_context.push(self.meta_stats.curriculum_progress);
527
528        // Adaptation efficiency
529        meta_context.push(self.meta_stats.adaptation_efficiency);
530
531        // Recent problem _class diversity
532        let recent_classes: std::collections::HashSet<String> = self
533            .problem_class_history
534            .iter()
535            .rev()
536            .take(10)
537            .cloned()
538            .collect();
539        meta_context.push((recent_classes.len() as f64 / 10.0).min(1.0));
540
541        (Array1::from(base_features), Array1::from(meta_context))
542    }
543
544    /// Decode sophisticated actions from meta-policy output
545    fn decode_meta_action(
546        &self,
547        policy_output: &ArrayView1<f64>,
548        meta_output: &ArrayView1<f64>,
549    ) -> OptimizationAction {
550        if policy_output.is_empty() {
551            return OptimizationAction::GradientStep {
552                learning_rate: 0.01,
553            };
554        }
555
556        // Use meta-_output to modulate action selection
557        let meta_modulation = meta_output.get(0).copied().unwrap_or(1.0);
558        let action_strength = meta_output.get(1).copied().unwrap_or(1.0);
559
560        // Enhanced action decoding with meta-learning insights
561        let action_logits = policy_output.mapv(|x| x * meta_modulation);
562        let action_type = action_logits
563            .iter()
564            .enumerate()
565            .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
566            .map(|(idx, _)| idx)
567            .unwrap_or(0);
568
569        match action_type {
570            0 => OptimizationAction::GradientStep {
571                learning_rate: 0.01 * action_strength * (1.0 + policy_output[0] * 0.5),
572            },
573            1 => OptimizationAction::RandomPerturbation {
574                magnitude: 0.1 * action_strength * (1.0 + policy_output[1] * 0.5),
575            },
576            2 => OptimizationAction::MomentumUpdate {
577                momentum: (0.9 * action_strength * (1.0 + policy_output[2] * 0.1)).min(0.99),
578            },
579            3 => OptimizationAction::AdaptiveLearningRate {
580                factor: (0.5 + 0.5 * policy_output[3] * action_strength)
581                    .max(0.1)
582                    .min(2.0),
583            },
584            4 => OptimizationAction::ResetToBest,
585            _ => OptimizationAction::Terminate,
586        }
587    }
588
589    /// Compute meta-gradients for higher-order learning
590    fn compute_meta_gradients(&self, metabatch: &[MetaTrajectory]) -> MetaGradients {
591        let num_layers = self.meta_policy.layer_sizes.len() - 1;
592        let max_size = *self
593            .meta_policy
594            .layer_sizes
595            .iter()
596            .max()
597            .expect("Operation failed");
598
599        let mut meta_gradients = MetaGradients {
600            policy_gradients: Array3::zeros((num_layers, max_size, max_size)),
601            meta_weight_gradients: Array3::zeros((num_layers, max_size, max_size)),
602            meta_lr_gradients: Array3::zeros((num_layers, max_size, max_size)),
603            policy_bias_gradients: Array2::zeros((num_layers, max_size)),
604            meta_bias_gradients: Array2::zeros((num_layers, max_size)),
605            second_order_terms: Array3::zeros((num_layers, max_size, max_size)),
606        };
607
608        for trajectory in metabatch {
609            // Compute trajectory-specific gradients
610            let trajectory_return: f64 = trajectory.experiences.iter().map(|e| e.reward).sum();
611
612            let learning_speed_bonus = trajectory.learning_metrics.convergence_speed * 0.1;
613            let exploration_bonus = trajectory.learning_metrics.exploration_efficiency * 0.05;
614            let adjusted_return = trajectory_return + learning_speed_bonus + exploration_bonus;
615
616            // For each experience in trajectory, compute gradients
617            for (step, experience) in trajectory.experiences.iter().enumerate() {
618                let (state_features, meta_context) =
619                    self.extract_meta_state_features(&experience.state, &trajectory.problem_class);
620
621                // Compute discounted return from this step
622                let gamma = self.config.discount_factor;
623                let step_return: f64 = trajectory.experiences[step..]
624                    .iter()
625                    .enumerate()
626                    .map(|(i, e)| gamma.powi(i as i32) * e.reward)
627                    .sum();
628
629                // Policy gradient with meta-learning augmentation
630                let advantage = step_return - adjusted_return / trajectory.experiences.len() as f64;
631
632                // Accumulate the meta-policy gradients for this step. This is a
633                // first-order REINFORCE-style estimate that uses the input
634                // (state/meta) features directly as a proxy for the policy
635                // score function rather than back-propagating through the
636                // network; it yields a real, advantage-weighted gradient that
637                // drives meta-adaptation even though it is not the exact
638                // analytic gradient.
639                for layer in 0..num_layers {
640                    for i in 0..self.meta_policy.layer_sizes[layer + 1] {
641                        for j in 0..self.meta_policy.layer_sizes[layer] {
642                            if j < state_features.len() {
643                                // Standard policy gradient
644                                meta_gradients.policy_gradients[[layer, i, j]] +=
645                                    advantage * state_features[j] * 0.01;
646
647                                // Meta-gradient for learning rate adaptation
648                                let meta_lr_grad = advantage
649                                    * state_features[j]
650                                    * trajectory.learning_metrics.convergence_speed;
651                                meta_gradients.meta_lr_gradients[[layer, i, j]] +=
652                                    meta_lr_grad * 0.001;
653
654                                // Meta-weight gradients
655                                if j < meta_context.len() {
656                                    meta_gradients.meta_weight_gradients[[layer, i, j]] +=
657                                        advantage * meta_context[j] * 0.001;
658                                }
659                            }
660                        }
661
662                        // Bias gradients
663                        meta_gradients.policy_bias_gradients[[layer, i]] += advantage * 0.01;
664                        meta_gradients.meta_bias_gradients[[layer, i]] +=
665                            advantage * trajectory.learning_metrics.generalization_score * 0.001;
666                    }
667                }
668            }
669        }
670
671        // Normalize by _batch size
672        if !metabatch.is_empty() {
673            let batch_size = metabatch.len() as f64;
674            meta_gradients.policy_gradients /= batch_size;
675            meta_gradients.meta_weight_gradients /= batch_size;
676            meta_gradients.meta_lr_gradients /= batch_size;
677            meta_gradients.policy_bias_gradients /= batch_size;
678            meta_gradients.meta_bias_gradients /= batch_size;
679        }
680
681        meta_gradients
682    }
683
684    /// Update meta-learning statistics
685    fn update_meta_stats(
686        &mut self,
687        meta_gradients: &MetaGradients,
688        problem_class: &str,
689        performance: f64,
690    ) {
691        // Update gradient norms
692        let grad_norm = meta_gradients
693            .policy_gradients
694            .iter()
695            .map(|&g| g * g)
696            .sum::<f64>()
697            .sqrt();
698        self.meta_stats.meta_gradient_norms.push_back(grad_norm);
699        if self.meta_stats.meta_gradient_norms.len() > 1000 {
700            self.meta_stats.meta_gradient_norms.pop_front();
701        }
702
703        // Update problem _class performance
704        let current_perf = self
705            .meta_stats
706            .problem_class_performance
707            .entry(problem_class.to_string())
708            .or_insert(0.0);
709        *current_perf = 0.9 * *current_perf + 0.1 * performance;
710
711        // Update curriculum progress
712        self.meta_stats.curriculum_progress = self.curriculum_controller.difficulty_level;
713
714        // Update adaptation efficiency based on meta-gradient stability
715        let grad_stability = if self.meta_stats.meta_gradient_norms.len() > 10 {
716            let recent_grads: Vec<f64> = self
717                .meta_stats
718                .meta_gradient_norms
719                .iter()
720                .rev()
721                .take(10)
722                .cloned()
723                .collect();
724            let mean = recent_grads.iter().sum::<f64>() / recent_grads.len() as f64;
725            let variance = recent_grads
726                .iter()
727                .map(|&x| (x - mean).powi(2))
728                .sum::<f64>()
729                / recent_grads.len() as f64;
730            1.0 / (1.0 + variance)
731        } else {
732            1.0
733        };
734
735        self.meta_stats.adaptation_efficiency =
736            0.95 * self.meta_stats.adaptation_efficiency + 0.05 * grad_stability;
737    }
738
739    /// Extract problem class from objective function characteristics
740    fn classify_problem<F>(&self, objective: &F, params: &ArrayView1<f64>) -> String
741    where
742        F: Fn(&ArrayView1<f64>) -> f64,
743    {
744        // Simple problem classification based on function behavior
745        let base_value = objective(params);
746
747        // Test convexity by checking second derivatives (simplified)
748        let eps = 1e-6;
749        let mut curvature_sum = 0.0;
750
751        for i in 0..params.len().min(3) {
752            // Limit checks for efficiency
753            let mut params_plus = params.to_owned();
754            let mut params_minus = params.to_owned();
755            params_plus[i] += eps;
756            params_minus[i] -= eps;
757
758            let f_plus = objective(&params_plus.view());
759            let f_minus = objective(&params_minus.view());
760            let curvature = (f_plus + f_minus - 2.0 * base_value) / (eps * eps);
761            curvature_sum += curvature;
762        }
763
764        let avg_curvature = curvature_sum / params.len().min(3) as f64;
765
766        if avg_curvature > 1.0 {
767            "convex".to_string()
768        } else if avg_curvature < -1.0 {
769            "concave".to_string()
770        } else if base_value.abs() < 1.0 {
771            "low_scale".to_string()
772        } else if base_value.abs() > 100.0 {
773            "high_scale".to_string()
774        } else {
775            "general".to_string()
776        }
777    }
778}
779
780impl RLOptimizer for AdvancedAdvancedPolicyGradientOptimizer {
781    fn config(&self) -> &RLOptimizationConfig {
782        &self.config
783    }
784
785    fn select_action(&mut self, state: &OptimizationState) -> OptimizationAction {
786        let problem_class = "general"; // Simplified for this implementation
787        let (state_features, meta_context) = self.extract_meta_state_features(state, problem_class);
788        let (policy_output, meta_output) =
789            self.meta_policy
790                .meta_forward(&state_features.view(), problem_class, &meta_context);
791        self.decode_meta_action(&policy_output.view(), &meta_output.view())
792    }
793
794    fn update(&mut self, experience: &Experience) -> Result<(), OptimizeError> {
795        // Meta-learning updates are done in batch after collecting trajectories
796        Ok(())
797    }
798
799    fn run_episode<F>(
800        &mut self,
801        objective: &F,
802        initial_params: &ArrayView1<f64>,
803    ) -> OptimizeResult<OptimizeResults<f64>>
804    where
805        F: Fn(&ArrayView1<f64>) -> f64,
806    {
807        let problem_class = self.classify_problem(objective, initial_params);
808        self.problem_class_history.push_back(problem_class.clone());
809        if self.problem_class_history.len() > 100 {
810            self.problem_class_history.pop_front();
811        }
812
813        let initial_meta_context = Array1::from(vec![
814            self.meta_stats.curriculum_progress,
815            self.meta_stats.adaptation_efficiency,
816            self.curriculum_controller.difficulty_level,
817        ]);
818
819        let mut current_params = initial_params.to_owned();
820        let mut current_state = utils::create_state(current_params.clone(), objective, 0, None);
821        let mut experiences = Vec::new();
822        let mut momentum = Array1::zeros(initial_params.len());
823
824        let start_objective = current_state.objective_value;
825        let mut max_improvement = 0.0;
826        let mut exploration_steps = 0;
827
828        for step in 0..self.config.max_steps_per_episode {
829            // Select action using meta-policy
830            let action = self.select_action(&current_state);
831
832            // Apply action
833            let new_params =
834                utils::apply_action(&current_state, &action, &self.best_params, &mut momentum);
835            let new_state =
836                utils::create_state(new_params, objective, step + 1, Some(&current_state));
837
838            // Compute reward with meta-learning augmentation
839            let base_reward =
840                self.reward_function
841                    .compute_reward(&current_state, &action, &new_state);
842            let exploration_bonus =
843                if matches!(action, OptimizationAction::RandomPerturbation { .. }) {
844                    exploration_steps += 1;
845                    0.01
846                } else {
847                    0.0
848                };
849            let reward = base_reward + exploration_bonus;
850
851            // Track improvement for learning metrics
852            let improvement = current_state.objective_value - new_state.objective_value;
853            if improvement > max_improvement {
854                max_improvement = improvement;
855            }
856
857            // Store experience
858            let experience = Experience {
859                state: current_state.clone(),
860                action: action.clone(),
861                reward,
862                next_state: new_state.clone(),
863                done: utils::should_terminate(&new_state, self.config.max_steps_per_episode),
864            };
865            experiences.push(experience);
866
867            // Update best solution
868            if new_state.objective_value < self.best_objective {
869                self.best_objective = new_state.objective_value;
870                self.best_params = new_state.parameters.clone();
871            }
872
873            current_state = new_state;
874            current_params = current_state.parameters.clone();
875
876            // Check termination
877            if utils::should_terminate(&current_state, self.config.max_steps_per_episode)
878                || matches!(action, OptimizationAction::Terminate)
879            {
880                break;
881            }
882        }
883
884        // Compute learning metrics
885        let final_objective = current_state.objective_value;
886        let total_improvement = start_objective - final_objective;
887        let learning_metrics = LearningMetrics {
888            improvement_rate: total_improvement / (current_state.step as f64 + 1.0),
889            convergence_speed: if total_improvement > 0.0 {
890                max_improvement / total_improvement
891            } else {
892                0.0
893            },
894            exploration_efficiency: (exploration_steps as f64) / (current_state.step as f64 + 1.0),
895            generalization_score: if total_improvement > 0.0 {
896                (total_improvement / start_objective.abs()).min(1.0)
897            } else {
898                0.0
899            },
900        };
901
902        // Create meta-trajectory
903        let meta_trajectory = MetaTrajectory {
904            experiences,
905            problem_class: problem_class.clone(),
906            initial_meta_context,
907            learning_metrics: learning_metrics.clone(),
908            adaptation_speed: learning_metrics.improvement_rate.abs(),
909        };
910
911        // Add to meta-experience buffer
912        self.meta_experience_buffer.add_trajectory(meta_trajectory);
913
914        // Update curriculum controller
915        let episode_performance = learning_metrics.generalization_score;
916        self.curriculum_controller
917            .update_progress(episode_performance);
918
919        Ok(OptimizeResults::<f64> {
920            x: current_params,
921            fun: current_state.objective_value,
922            success: current_state.convergence_metrics.relative_objective_change < 1e-6,
923            nit: current_state.step,
924            nfev: current_state.step, // Approximate function evaluations
925            njev: 0,
926            nhev: 0,
927            maxcv: 0,
928            status: 0,
929            message: format!(
930                "Meta-policy gradient episode completed for problem class: {}",
931                problem_class
932            ),
933            jac: None,
934            hess: None,
935            constr: None,
936        })
937    }
938
939    fn train<F>(
940        &mut self,
941        objective: &F,
942        initial_params: &ArrayView1<f64>,
943    ) -> OptimizeResult<OptimizeResults<f64>>
944    where
945        F: Fn(&ArrayView1<f64>) -> f64,
946    {
947        let mut best_result = OptimizeResults::<f64> {
948            x: initial_params.to_owned(),
949            fun: f64::INFINITY,
950            success: false,
951            nit: 0,
952            nfev: 0,
953            njev: 0,
954            nhev: 0,
955            maxcv: 0,
956            status: 0,
957            message: "Meta-learning training not completed".to_string(),
958            jac: None,
959            hess: None,
960            constr: None,
961        };
962
963        // Meta-learning training loop
964        for episode in 0..self.config.num_episodes {
965            let result = self.run_episode(objective, initial_params)?;
966
967            if result.fun < best_result.fun {
968                best_result = result;
969            }
970
971            // Meta-learning update every few episodes
972            if (episode + 1) % 5 == 0 && self.meta_experience_buffer.trajectories.len() >= 10 {
973                let meta_batch = self.meta_experience_buffer.sample_meta_batch(10);
974                let meta_gradients = self.compute_meta_gradients(&meta_batch);
975
976                // Update meta-policy with adaptive learning rates
977                self.meta_policy.meta_update(
978                    &meta_gradients,
979                    self.config.learning_rate,
980                    self.config.learning_rate * 0.1,
981                );
982
983                // Update meta-statistics
984                let avg_performance = meta_batch
985                    .iter()
986                    .map(|t| t.learning_metrics.generalization_score)
987                    .sum::<f64>()
988                    / meta_batch.len() as f64;
989
990                if let Some(trajectory) = meta_batch.first() {
991                    self.update_meta_stats(
992                        &meta_gradients,
993                        &trajectory.problem_class,
994                        avg_performance,
995                    );
996                }
997            }
998        }
999
1000        best_result.x = self.best_params.clone();
1001        best_result.fun = self.best_objective;
1002        best_result.message = format!(
1003            "Meta-learning training completed. Curriculum level: {:.3}, Adaptation efficiency: {:.3}",
1004            self.meta_stats.curriculum_progress,
1005            self.meta_stats.adaptation_efficiency
1006        );
1007
1008        Ok(best_result)
1009    }
1010
1011    fn reset(&mut self) {
1012        self.meta_trajectories.clear();
1013        self.problem_class_history.clear();
1014        self.best_objective = f64::INFINITY;
1015        self.best_params.fill(0.0);
1016        self.meta_stats.meta_gradient_norms.clear();
1017        self.meta_stats.problem_class_performance.clear();
1018        self.curriculum_controller = CurriculumController::new();
1019        self.meta_experience_buffer = MetaExperienceBuffer::new(500);
1020    }
1021}
1022
1023/// Convenience function for advanced meta-learning policy gradient optimization
1024#[allow(dead_code)]
1025pub fn advanced_advanced_policy_gradient_optimize<F>(
1026    objective: F,
1027    initial_params: &ArrayView1<f64>,
1028    config: Option<RLOptimizationConfig>,
1029) -> OptimizeResult<OptimizeResults<f64>>
1030where
1031    F: Fn(&ArrayView1<f64>) -> f64,
1032{
1033    let config = config.unwrap_or_else(|| RLOptimizationConfig {
1034        num_episodes: 100,
1035        max_steps_per_episode: 50,
1036        learning_rate: 0.001,
1037        ..Default::default()
1038    });
1039
1040    let mut optimizer = AdvancedAdvancedPolicyGradientOptimizer::new(
1041        config,
1042        initial_params.len() + 5, // Extra features for meta-context
1043        6,                        // Number of action types
1044    );
1045    optimizer.train(&objective, initial_params)
1046}
1047
1048/// Legacy convenience function for backward compatibility
1049#[allow(dead_code)]
1050pub fn policy_gradient_optimize<F>(
1051    objective: F,
1052    initial_params: &ArrayView1<f64>,
1053    config: Option<RLOptimizationConfig>,
1054) -> OptimizeResult<OptimizeResults<f64>>
1055where
1056    F: Fn(&ArrayView1<f64>) -> f64,
1057{
1058    advanced_advanced_policy_gradient_optimize(objective, initial_params, config)
1059}
1060
1061#[cfg(test)]
1062mod tests {
1063    use super::*;
1064
1065    #[test]
1066    fn test_meta_policy_network_creation() {
1067        let network = MetaPolicyNetwork::new(4, 2, vec![8, 6]);
1068        assert_eq!(network.layer_sizes, vec![4, 8, 6, 2]);
1069    }
1070
1071    #[test]
1072    fn test_meta_forward_pass() {
1073        let mut network = MetaPolicyNetwork::new(3, 2, vec![4]);
1074        let input = Array1::from(vec![0.5, -0.3, 0.8]);
1075        let meta_context = Array1::from(vec![0.1, 0.2]);
1076
1077        let (policy_out, meta_out) = network.meta_forward(&input.view(), "test", &meta_context);
1078
1079        assert_eq!(policy_out.len(), 2);
1080        assert_eq!(meta_out.len(), 2);
1081    }
1082
1083    #[test]
1084    fn test_curriculum_controller() {
1085        let mut controller = CurriculumController::new();
1086        assert_eq!(controller.difficulty_level, 0.1);
1087
1088        // Add good performance
1089        for _ in 0..25 {
1090            controller.update_progress(0.9);
1091        }
1092
1093        assert!(controller.difficulty_level > 0.1);
1094    }
1095
1096    #[test]
1097    fn test_meta_experience_buffer() {
1098        let mut buffer = MetaExperienceBuffer::new(10);
1099
1100        let trajectory = MetaTrajectory {
1101            experiences: vec![],
1102            problem_class: "test".to_string(),
1103            initial_meta_context: Array1::zeros(3),
1104            learning_metrics: LearningMetrics {
1105                improvement_rate: 0.1,
1106                convergence_speed: 0.2,
1107                exploration_efficiency: 0.3,
1108                generalization_score: 0.4,
1109            },
1110            adaptation_speed: 0.1,
1111        };
1112
1113        buffer.add_trajectory(trajectory);
1114        assert_eq!(buffer.trajectories.len(), 1);
1115
1116        let batch = buffer.sample_meta_batch(1);
1117        assert_eq!(batch.len(), 1);
1118    }
1119
1120    #[test]
1121    fn test_advanced_advanced_optimizer_creation() {
1122        let config = RLOptimizationConfig::default();
1123        let optimizer = AdvancedAdvancedPolicyGradientOptimizer::new(config, 4, 3);
1124
1125        assert_eq!(optimizer.meta_policy.layer_sizes[0], 4);
1126        assert_eq!(optimizer.meta_policy.layer_sizes.last(), Some(&3));
1127    }
1128
1129    #[test]
1130    fn test_problem_classification() {
1131        let config = RLOptimizationConfig::default();
1132        let optimizer = AdvancedAdvancedPolicyGradientOptimizer::new(config, 2, 3);
1133
1134        let quadratic = |x: &ArrayView1<f64>| x[0].powi(2) + x[1].powi(2);
1135        let params = Array1::from(vec![1.0, 1.0]);
1136
1137        let class = optimizer.classify_problem(&quadratic, &params.view());
1138        assert!(!class.is_empty());
1139    }
1140
1141    #[test]
1142    fn test_meta_optimization() {
1143        let config = RLOptimizationConfig {
1144            num_episodes: 50,
1145            max_steps_per_episode: 50,
1146            learning_rate: 0.05,
1147            ..Default::default()
1148        };
1149
1150        let objective = |x: &ArrayView1<f64>| (x[0] - 1.0).powi(2) + (x[1] + 0.5).powi(2);
1151        let initial = Array1::from(vec![0.0, 0.0]);
1152
1153        let result =
1154            advanced_advanced_policy_gradient_optimize(objective, &initial.view(), Some(config))
1155                .expect("Operation failed");
1156
1157        assert!(result.nit > 0);
1158        assert!(result.fun <= objective(&initial.view()) * 1.01);
1159    }
1160}